织梦CMS - 轻松建站从此开始!

罗索

ffmpeg 最简单的转码封装mp4文件

落鹤生 发布于 2014-11-02 12:45 点击:次 
本例简单实现了解码后的video重新编码264之后在mux成MP4文件的过程,主要是用来记录muxing的方法。保证你送给编码器的每一frame的pts都是顺序的, 否则编码器会报 “non-strictly-monotonic pts at frame”
TAG: 文件容器  MP4  

本例简单实现了解码后的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. if( cli_log_level >= X264_LOG_DEBUG || pts_warning_cnt < MAX_PTS_WARNING ) 
  3. x264_cli_log( "x264", X264_LOG_WARNING
  4. "non-strictly-monotonic 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
  8. "too many nonmonotonic pts warnings, suppressing further ones\n" ); 
  9.     pts_warning_cnt++; 
  10.     pic.i_pts = largest_pts + ticks_per_frame; 

在将数据送到编码器后,进行编码输出得到的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. delete pkt; 
  4. return 0; 
  5. 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.     av_register_all(); 
  6.      
  7.  
  8.  
  9.     AVFormatContext* pFormat = NULL; 
  10.     if (avformat_open_input(&pFormat, SRC_FILE, NULL, NULL) < 0) 
  11.     { 
  12.         return 0; 
  13.     } 
  14.     AVCodecContext* video_dec_ctx = NULL; 
  15.     AVCodec* video_dec = NULL; 
  16.     if (avformat_find_stream_info(pFormat, NULL) < 0) 
  17.     { 
  18.         return 0; 
  19.     } 
  20.     av_dump_format(pFormat, 0, SRC_FILE, 0); 
  21.     video_dec_ctx = pFormat->streams[0]->codec; 
  22.     video_dec = avcodec_find_decoder(video_dec_ctx->codec_id); 
  23.     if (avcodec_open2(video_dec_ctx, video_dec, NULL) < 0) 
  24.     { 
  25.         return 0; 
  26.     } 
  27.  
  28.     AVFormatContext* pOFormat = NULL; 
  29.     AVOutputFormat* ofmt = NULL; 
  30.     if (avformat_alloc_output_context2(&pOFormat, NULL, NULL, OUT_FILE) < 0) 
  31.     { 
  32.         return 0; 
  33.     } 
  34.     ofmt = pOFormat->oformat; 
  35.     if (avio_open(&(pOFormat->pb), OUT_FILE, AVIO_FLAG_READ_WRITE) < 0) 
  36.     { 
  37.         return 0; 
  38.     } 
  39.     AVCodecContext *video_enc_ctx = NULL; 
  40.     AVCodec *video_enc = NULL; 
  41.     video_enc = avcodec_find_encoder(AV_CODEC_ID_H264); 
  42.     AVStream *video_st = avformat_new_stream(pOFormat, video_enc); 
  43.     if (!video_st) 
  44.         return 0; 
  45.     video_enc_ctx = video_st->codec; 
  46.     video_enc_ctx->width = video_dec_ctx->width; 
  47.     video_enc_ctx->height = video_dec_ctx->height; 
  48.     video_enc_ctx->pix_fmt = PIX_FMT_YUV420P; 
  49.     video_enc_ctx->time_base.num = 1; 
  50.     video_enc_ctx->time_base.den = 25; 
  51.     video_enc_ctx->bit_rate = video_dec_ctx->bit_rate; 
  52.     video_enc_ctx->gop_size = 250; 
  53.     video_enc_ctx->max_b_frames = 10; 
  54.     //H264 
  55.     //pCodecCtx->me_range = 16; 
  56.     //pCodecCtx->max_qdiff = 4; 
  57.     video_enc_ctx->qmin = 10; 
  58.     video_enc_ctx->qmax = 51; 
  59.     if (avcodec_open2(video_enc_ctx, video_enc, NULL) < 0) 
  60.     { 
  61.         printf("编码器打开失败!\n"); 
  62.         return 0; 
  63.     } 
  64.     printf("Output264video Information====================\n"); 
  65.     av_dump_format(pOFormat, 0, OUT_FILE, 1); 
  66.     printf("Output264video Information====================\n"); 
  67.  
  68.     //mp4 file 
  69.     AVFormatContext* pMp4Format = NULL; 
  70.     AVOutputFormat* pMp4OFormat = NULL; 
  71.     if (avformat_alloc_output_context2(&pMp4Format, NULL, NULL, OUT_FMT_FILE) < 0) 
  72.     { 
  73.         return 0; 
  74.     } 
  75.     pMp4OFormat = pMp4Format->oformat; 
  76.     if (avio_open(&(pMp4Format->pb), OUT_FMT_FILE, AVIO_FLAG_READ_WRITE) < 0) 
  77.     { 
  78.         return 0; 
  79.     } 
  80.  
  81.     for (int i = 0; i < pFormat->nb_streams; i++) { 
  82.         AVStream *in_stream = pFormat->streams[i]; 
  83.         AVStream *out_stream = avformat_new_stream(pMp4Format, in_stream->codec->codec); 
  84.         if (!out_stream) { 
  85.             return 0; 
  86.         } 
  87.         int ret = 0; 
  88.         ret = avcodec_copy_context(out_stream->codec, in_stream->codec); 
  89.         if (ret < 0) { 
  90. fprintf(stderr, "Failed to copy context from input to output stream codec context\n"); 
  91. return 0; 
  92.         } 
  93.         out_stream->codec->codec_tag = 0; 
  94.         if (pMp4Format->oformat->flags & AVFMT_GLOBALHEADER) 
  95.             out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; 
  96.     } 
  97.  
  98.  
  99.     av_dump_format(pMp4Format, 0, OUT_FMT_FILE, 1); 
  100.  
  101.     if (avformat_write_header(pMp4Format, NULL) < 0) 
  102.     { 
  103.         return 0; 
  104.     }
  105.  
  106.     ////
  107.     av_opt_set(video_enc_ctx->priv_data, "preset""superfast", 0); 
  108.     av_opt_set(video_enc_ctx->priv_data, "tune""zerolatency", 0); 
  109.     avformat_write_header(pOFormat, NULL); 
  110.     AVPacket *pkt = new AVPacket(); 
  111.     av_init_packet(pkt); 
  112.     AVFrame *pFrame = avcodec_alloc_frame(); 
  113.     int ts = 0; 
  114.     while (1) 
  115.     { 
  116.         if (av_read_frame(pFormat, pkt) < 0) 
  117.         { 
  118.             avio_close(pOFormat->pb); 
  119.             av_write_trailer(pMp4Format); 
  120.             avio_close(pMp4Format->pb); 
  121.             delete pkt; 
  122.             return 0; 
  123.         } 
  124.         if (pkt->stream_index == 0) 
  125.         { 
  126.              
  127.             int got_picture = 0, ret = 0; 
  128.             ret = avcodec_decode_video2(video_dec_ctx, pFrame, &got_picture, pkt); 
  129.             if (ret < 0) 
  130.             { 
  131.                 delete pkt; 
  132.                 return 0; 
  133.             } 
  134.             pFrame->pts = pFrame->pkt_pts;//ts++; 
  135.             if (got_picture) 
  136.             { 
  137.                 AVPacket *tmppkt = new AVPacket; 
  138.                 av_init_packet(tmppkt); 
  139.                 int size = video_enc_ctx->width*video_enc_ctx->height * 3 / 2; 
  140.                 char* buf = new char[size]; 
  141.                 memset(buf, 0, size); 
  142.                 tmppkt->data = (uint8_t*)buf; 
  143.                 tmppkt->size = size; 
  144.                 ret = avcodec_encode_video2(video_enc_ctx, tmppkt, pFrame, &got_picture); 
  145.                 if (ret < 0) 
  146.                 { 
  147.                     avio_close(pOFormat->pb); 
  148.                     delete buf; 
  149.                     return 0; 
  150.                 } 
  151.                 if (got_picture) 
  152.                 { 
  153.                     //ret = av_interleaved_write_frame(pOFormat, tmppkt); 
  154.                     AVStream *in_stream = pFormat->streams[pkt->stream_index]; 
  155.                     AVStream *out_stream = pMp4Format->streams[pkt->stream_index]; 
  156.      
  157. tmppkt->pts = av_rescale_q_rnd(tmppkt->pts, in_stream->time_base
  158. , out_stream->time_base, AV_ROUND_NEAR_INF); 
  159. tmppkt->dts = av_rescale_q_rnd(tmppkt->dts, in_stream->time_base
  160. , out_stream->time_base, AV_ROUND_NEAR_INF); 
  161. tmppkt->duration = av_rescale_q(tmppkt->duration, in_stream->time_base
  162. , out_stream->time_base); 
  163. tmppkt->pos = -1; 
  164. ret = av_interleaved_write_frame(pMp4Format, tmppkt); 
  165.                     if (ret < 0) 
  166.                         return 0; 
  167.                     delete tmppkt; 
  168.                     delete buf; 
  169.                 } 
  170.             } 
  171.             //avcodec_free_frame(&pFrame); 
  172.         } 
  173.         else if (pkt->stream_index == 1) 
  174.         { 
  175.             AVStream *in_stream = pFormat->streams[pkt->stream_index]; 
  176.             AVStream *out_stream = pMp4Format->streams[pkt->stream_index]; 
  177.  
  178. pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base
  179. , out_stream->time_base, AV_ROUND_NEAR_INF); 
  180. pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base
  181. , out_stream->time_base, AV_ROUND_NEAR_INF); 
  182. pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base
  183. , out_stream->time_base); 
  184.             pkt->pos = -1; 
  185.             if (av_interleaved_write_frame(pMp4Format, pkt) < 0) 
  186.                 return 0; 
  187.         } 
  188.     } 
  189.     avcodec_free_frame(&pFrame); 
  190.     return 0; 

 

(dboyaoao)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201411/17115.html]
本文出处:ChinaUnix 作者:dboyaoao 原文
顶一下
(7)
87.5%
踩一下
(1)
12.5%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容