ffmpeg 最简单的转码封装mp4文件
分类:C/C++
本例简单实现了解码后的video重新编码264之后在mux成MP4文件的过程,主要是用来记录muxing的方法。
下面详细说一下细节:
大家都知道一般解码出来的数据都是播放顺序,解码器是将编码顺序的数据重新按照解码后的播放顺序输出的。而编码器是把数据根据解码需要的顺序重新排序保存的。
当然,以上情况只在有帧的情况下才有用,否则只有IP帧的话解码和编码的顺序是一样的
比如:解码后的数据是IBBP,那要将这个数据编码的话,编码后的数据保存的格式就是IPBB
这只是内部的处理,对于用ffmpeg的库的我们不用太过关心,但是,要注意,我们将数据塞给编码器的时候,要给顺序的播放加上顺序的时间标记,其实很简单只要保证你送给编码器的每一frame的pts都是顺序的就可以了,否则编码器会报“non-strictly-monotonic pts at frame” ,究其原因,是因为编码器需要送进来的frame时间上是递增的,为什么需要这个就得去本研究编码器了
点击(此处)折叠或打开
1.if( pic.i_pts <= largest_pts )
2.{
3.if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_P
TS_WARNING )
4. x264_cli_log("x264", X264_LOG_WARNING,"non-strictly-mono
tonic pts at frame %d (%"PRId64" <= %"PRId64")\n",
5. i_frame, pic.i_pts, largest_pts );
6.else if( pts_warning_cnt == MAX_PTS_WARNING )
7. x264_cli_log("x264", X264_LOG_WARNING,"too many nonmonot
onic pts warnings, suppressing further ones\n");
8. pts_warning_cnt++;
9. pic.i_pts = largest_pts + ticks_per_frame;
10.}
在将数据送到编码器后,进行编码输出得到的pkt有自己的pts和dts等数据,但是这个数据记得吗?是用我们自己送进去的pts来表示的,所以在和原来的audio mux的时候,会出现严重的音视频不同步,现在想想这个问题,就很容易理解了,两边的pts差距很大,当然解码后做同步的时候会差很多。
其实ffmpeg在解码的时候将解码出来的顺序时间戳给了frame的pkt_pts这个成员,所以我们可以直接用这个值赋值给frame的pts,在送进编码器,这样编码出来的pkt中的时间戳就和原来的audio对上了。
点击(此处)折叠或打开
1.ret = avcodec_decode_video2(video_dec_ctx, pFrame,&got_picture, pkt);
2.if(ret < 0)
3.{
4. delete pkt;
5. return 0;
6.}
7. pFrame->pts = pFrame->pkt_pts; //赋值解码后的pts
最后在进行mux成mp4文件就ok了
在mux的过程中,有个接口av_rescale_q_rnd,这个是用来换算pts的,因为在设定mp4输出格式的时候time_base这个值是和原来的文件不一样的,所以要用这个来重新算分装数据的在新的mp4中的pts和dts等数据,具体原因后续会继续往里研究
直接上代码:
点击(此处)折叠或打开
1.const char* SRC_FILE ="1.mkv";
2.const char* OUT_FILE ="outfile.h264";
3.const char* OUT_FMT_FILE ="outfmtfile.mp4";
4.int main()
5.{
6. av_register_all();
7.
8.
9.
10. AVFormatContext* pFormat =NULL;
11.if(avformat_open_input(&pFormat, SRC_FILE,NULL,NULL)< 0)
12.{
13. return 0;
14.}
15. AVCodecContext* video_dec_ctx =NULL;
16. AVCodec* video_dec =NULL;
17.if(avformat_find_stream_info(pFormat,NULL)< 0)
18.{
19. return 0;
20.}
21. av_dump_format(pFormat, 0, SRC_FILE, 0);
22. video_dec_ctx = pFormat->streams[0]->codec;
23. video_dec = avcodec_find_decoder(video_dec_ctx->codec_id);
24.if(avcodec_open2(video_dec_ctx, video_dec,NULL)< 0)
25.{
26. return 0;
27.}
28.
29. AVFormatContext* pOFormat =NULL;
30. AVOutputFormat* ofmt =NULL;
31.if(avformat_alloc_output_context2(&pOFormat,NULL,NULL, OUT_FILE)<
0)
32.{
33. return 0;
34.}
35. ofmt = pOFormat->oformat;
36.if(avio_open(&(pOFormat->pb), OUT_FILE, AVIO_FLAG_READ_WRITE)< 0)
37.{
38. return 0;
39.}
40. AVCodecContext *video_enc_ctx =NULL;
41. AVCodec *video_enc =NULL;
42. video_enc = avcodec_find_encoder(AV_CODEC_ID_H264);
43. AVStream *video_st = avformat_new_stream(pOFormat, video_enc);
44.if(!video_st)
45. return 0;
46. video_enc_ctx = video_st->codec;
47. video_enc_ctx->width = video_dec_ctx->width;
48. video_enc_ctx->height = video_dec_ctx->height;
49. video_enc_ctx->pix_fmt = PIX_FMT_YUV420P;
50. video_enc_ctx->time_base.num = 1;
51. video_enc_ctx->time_base.den = 25;
52. video_enc_ctx->bit_rate = video_dec_ctx->bit_rate;
53. video_enc_ctx->gop_size = 250;
54. video_enc_ctx->max_b_frames = 10;
55.//H264
56.//pCodecCtx->me_range = 16;
57.//pCodecCtx->max_qdiff = 4;
58. video_enc_ctx->qmin = 10;
59. video_enc_ctx->qmax = 51;
60.if(avcodec_open2(video_enc_ctx, video_enc,NULL)< 0)
61.{
62. printf("编码器打开失败!\n");
63. return 0;
64.}
65. printf("Output264video Information====================\n");
66. av_dump_format(pOFormat, 0, OUT_FILE, 1);
67. printf("Output264video Information====================\n");
68.
69.//mp4 file
70. AVFormatContext* pMp4Format =NULL;
71. AVOutputFormat* pMp4OFormat =NULL;
72.if(avformat_alloc_output_context2(&pMp4Format,NULL,NULL, OUT_FMT_FI
LE)< 0)
73.{
74. return 0;
75.}
76. pMp4OFormat = pMp4Format->oformat;
77.if(avio_open(&(pMp4Format->pb), OUT_FMT_FILE, AVIO_FLAG_READ_WRITE)<
0)
78.{
79. return 0;
80.}
81.
82.for(int i = 0; i < pFormat->nb_streams; i++){
83. AVStream *in_stream = pFormat->streams[i];
84. AVStream *out_stream = avformat_new_stream(pMp4Format, in_stream->
codec->codec);
85.if(!out_stream){
86. return 0;
87.}
88.int ret = 0;
89. ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
90.if(ret < 0){
91. fprintf(stderr,"Failed to copy context from input to output s
tream codec context\n");
92. return 0;
93.}
94. out_stream->codec->codec_tag = 0;
95.if(pMp4Format->oformat->flags & AVFMT_GLOBALHEADER)
96. out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
97.}
98.
99.
100. av_dump_format(pMp4Format, 0, OUT_FMT_FILE, 1);
101.
102.if(avformat_write_header(pMp4Format,NULL)< 0)
103.{
104. return 0;
105.}
106.
107.
108.////
109.
110.
111.
112. av_opt_set(video_enc_ctx->priv_data,"preset","superfast", 0);
113. av_opt_set(video_enc_ctx->priv_data,"tune","zerolatency", 0);
114. avformat_write_header(pOFormat,NULL);
115. AVPacket *pkt = new AVPacket();
116. av_init_packet(pkt);
117. AVFrame *pFrame = avcodec_alloc_frame();
118.int ts = 0;
119.while(1)
120.{
121.if(av_read_frame(pFormat, pkt)< 0)
122.{
123. avio_close(pOFormat->pb);
124. av_write_trailer(pMp4Format);
125. avio_close(pMp4Format->pb);
126. delete pkt;
127. return 0;
128.}
129.if(pkt->stream_index == 0)
130.{
131.
132.int got_picture = 0, ret = 0;
133. ret = avcodec_decode_video2(video_dec_ctx, pFrame,&got_pictur e, pkt);
134.if(ret < 0)
135.{
136. delete pkt;
137. return 0;
138.}
139. pFrame->pts = pFrame->pkt_pts;//ts++;
140.if(got_picture)
141.{
142. AVPacket *tmppkt = new AVPacket;
143. av_init_packet(tmppkt);
144.int size = video_enc_ctx->width*video_enc_ctx->height * 3 / 2;
145. char* buf = new char[size];
146. memset(buf, 0, size);
147. tmppkt->data =(uint8_t*)buf;
148. tmppkt->size = size;
149. ret = avcodec_encode_video2(video_enc_ctx, tmppkt, pFrame, &got_picture);
150.if(ret < 0)
151.{
152. avio_close(pOFormat->pb);
153. delete buf;
154. return 0;
155.}
156.if(got_picture)
157.{
158.//ret = av_interleaved_write_frame(pOFormat, tmppkt); 159. AVStream *in_stream = pFormat->streams[pkt->stream_ind ex];
160. AVStream *out_stream = pMp4Format->streams[pkt->stream _index];
161.
162. tmppkt->pts = av_rescale_q_rnd(tmppkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
163. tmppkt->dts = av_rescale_q_rnd(tmppkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
164. tmppkt->duration = av_rescale_q(tmppkt->duration, in_s tream->time_base, out_stream->time_base);
165. tmppkt->pos =-1;
166. ret = av_interleaved_write_frame(pMp4Format, tmppkt); 167.if(ret < 0)
168. return 0;
169. delete tmppkt;
170. delete buf;
171.}
172.}
173.//avcodec_free_frame(&pFrame);
174.}
175.else if(pkt->stream_index == 1)
176.{
177. AVStream *in_stream = pFormat->streams[pkt->stream_index]; 178. AVStream *out_stream = pMp4Format->streams[pkt->stream_index]; 179.
180. pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, ou t_stream->time_base, AV_ROUND_NEAR_INF);
181. pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, ou t_stream->time_base, AV_ROUND_NEAR_INF);
182. pkt->duration = av_rescale_q(pkt->duration, in_stream->time_ba se, out_stream->time_base);
183. pkt->pos =-1;
184.if(av_interleaved_write_frame(pMp4Format, pkt)< 0)
185. return 0;
186.}
187.}
188. avcodec_free_frame(&pFrame);
189. return 0;
190.}
阅读(4898) | 评论(0) | 转发(0) |
上一篇:ffmpeg 最简单的编码264
下一篇:设计模式-工厂模式与抽象工厂模式