基于FFmpeg将pcm+rgb编码并封装MP4

编程入门 行业动态 更新时间:2024-10-09 15:24:42

基于FFmpeg将<a href=https://www.elefans.com/category/jswz/34/1757429.html style=pcm+rgb编码并封装MP4"/>

基于FFmpeg将pcm+rgb编码并封装MP4

FFmpeg是非常强大好用的音视频处理库,功能强大,首先我们先说说如何配置使用FFmpeg。

首先,从官网上下载:FFmpeg

FFmpeg官提供了3个版本:Static,Shared,Dev。介绍如下:

前两个版本可以直接在命令行中使用,他们的区别在于:

Static里面只有3个应用程序:ffmpeg.exe,ffplay.exe,ffprobe.exe,每个exe的体积都很大,相关的Dll已经被编译到exe里面去了。

Shared里面除了3个应用程序:ffmpeg.exe,ffplay.exe,ffprobe.exe之外,还有一些Dll,比如说avcodec-54.dll之类的。Shared里面的exe体积很小,他们在运行的时候,到相应的Dll中调用功能。

Dev版本是用于开发的,里面包含了include(头文件xxx.h)和lib(库文件xxx.lib),这个版本不包含exe文件。

在vs使用和一般的库包一样只要把Dev版本下的include文件添加到包含目录,lib文件夹下的lib文件添加到附加依赖性项中。不要忘记在环境变量中添加bin目录。bin文件夹位于Shared版本里面。

简单实用FFmpeg把YUV的数据经过H264编码为视频码流,再封装成Mp4。

FFmpeg编码流程:

(1).首先使用av_register_all()函数注册所有的编码器和复用器(理解为格式封装器)。该步骤必须放在所有ffmpeg代码前第一个执行

 (2). avformat_alloc_output_context2():初始化包含有输出码流(AVStream)和解复用器(AVInputFormat)的AVFormatContext avio_open( )打开输出文件

(3).av_new_stream() 创建视频码流 该函数生成一个空AVstream 该结构存放编码后的视频码流 。视频码流被拆分为AVPacket新式保存在AVStream中。

(4).设置编码器信息,该步骤主要是为AVCodecContext(从AVStream->codec 获取指针)结构体设置一些参数,包括codec_id、codec_type、width、height、pix_fmt .....  根据编码器的不同,还要额外设置一些参数(如 h264 要设置qmax、qmin、qcompress参数才能正常使用h264编码)

(5).查找并打开编码器,根据前一步设置的编码器参数信息,来查找初始化一个编码其,并将其打开。用到函数为av_fine_encoder()和av_open2()。

(6).写头文件  avformat_write_header()。这一步主要是将封装格式的信息写入文件头部位置。

(7).编码帧。用到的函数 avcodec_encode_video2() 将AVFrame编码为AVPacket

(8).在写入文件之前 还需要做一件事情就是设置AVPacket一些信息。这些信息关乎最后封装格式能否被正确读取。后面回详细讲述该部分内容

(9).编码帧写入文件 av_write_frame()

(10).flush_encoder():输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的AVPacket。

(11).av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说MPEG2TS)

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

extern "C"  
{  
#include <libavcodec/avcodec.h>  
#include <libavformat/avformat.h>  
#include <libavutil/avutil.h>  
#include <libswscale/swscale.h>  
}  using namespace std;  int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index);  int main(int argc, char *argv[])  
{  AVFormatContext *pFormatCtx=nullptr;  AVOutputFormat *fmt=nullptr;  AVStream *video_st=nullptr;  AVCodecContext *pCodecCtx=nullptr;  AVCodec *pCodec=nullptr;  uint8_t *picture_buf=nullptr;  AVFrame *picture=nullptr;  int size;  //打开视频  FILE *in_file = fopen("F://src01_480x272.yuv", "rb");  if(!in_file)  {  cout<<"can not open file!"<<endl;  return -1;  }  int in_w=480,in_h=272;  int framenum=50;  const char* out_file="src01.mp4";  //[1] --注册所有ffmpeg组件  avcodec_register_all();  av_register_all();  //[1]  //[2] --初始化AVFormatContext结构体,根据文件名获取到合适的封装格式  avformat_alloc_output_context2(&pFormatCtx,NULL,NULL,out_file);  fmt = pFormatCtx->oformat;  //[2]  //[3] --打开文件  if(avio_open(&pFormatCtx->pb,out_file,AVIO_FLAG_READ_WRITE))  {  cout<<"output file open fail!";  goto end;  }  //[3]  //[4] --初始化视频码流  video_st = avformat_new_stream(pFormatCtx,0);  if(video_st==NULL)  { printf("failed allocating output stram\n");  goto end;  }  video_st->time_base.num = 1;  video_st->time_base.den =25;  //[4]  //[5] --编码器Context设置参数  pCodecCtx = video_st->codec;  pCodecCtx->codec_id = fmt->video_codec;  pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;  pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;  pCodecCtx->width=in_w;  pCodecCtx->height=in_h;  pCodecCtx->time_base.num = 1;  pCodecCtx->time_base.den = 25;  pCodecCtx->bit_rate = 400000;  pCodecCtx->gop_size = 12;  if(pCodecCtx->codec_id == AV_CODEC_ID_H264)  {  pCodecCtx->qmin = 10;  pCodecCtx->qmax = 51;  pCodecCtx->qcompress = 0.6;  }  if (pCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO)  pCodecCtx->max_b_frames = 2;  if (pCodecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO)  pCodecCtx->mb_decision = 2;  //[5]  //[6] --寻找编码器并打开编码器  pCodec = avcodec_find_encoder(pCodecCtx->codec_id);  if(!pCodec)  {  cout<<"no right encoder!"<<endl;  goto end;  }  if(avcodec_open2(pCodecCtx,pCodec,NULL)<0)  {  cout<<"open encoder fail!"<<endl;  goto end;  }  //[6]  //输出格式信息  av_dump_format(pFormatCtx,0,out_file,1);  //初始化帧  picture = av_frame_alloc();  picture->width=pCodecCtx->width;  picture->height=pCodecCtx->height;  picture->format=pCodecCtx->pix_fmt;  size = avpicture_get_size(pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height);  picture_buf = (uint8_t*)av_malloc(size);  avpicture_fill((AVPicture*)picture,picture_buf,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height);  //[7] --写头文件  avformat_write_header(pFormatCtx,NULL);  //[7]  AVPacket pkt; //创建已编码帧  int y_size = pCodecCtx->width*pCodecCtx->height;  av_new_packet(&pkt,size*3);  //[8] --循环编码每一帧  for(int i=0;i<framenum;i++)  {  //读入YUV  if(fread(picture_buf,1,y_size*3/2,in_file)<0)  {  cout<<"read file fail!"<<endl;  goto end;  }  else if(feof(in_file))  break;  picture->data[0] = picture_buf; //亮度Y  picture->data[1] = picture_buf+y_size; //U  picture->data[2] = picture_buf+y_size*5/4; //V  //AVFrame PTS  picture->pts=i;  int got_picture = 0;  //编码  int ret = avcodec_encode_video2(pCodecCtx,&pkt,picture,&got_picture);  if(ret<0)  {  cout<<"encoder fail!"<<endl;  goto end;  }  if(got_picture == 1)  {  cout<<"encoder success!"<<endl;  // parpare packet for muxing  pkt.stream_index = video_st->index;  av_packet_rescale_ts(&pkt, pCodecCtx->time_base, video_st->time_base);  pkt.pos = -1;  ret = av_interleaved_write_frame(pFormatCtx,&pkt);  av_free_packet(&pkt);  }  }  //[8]  //[9] --Flush encoder  int ret = flush_encoder(pFormatCtx,0);  if(ret < 0)  {  cout<<"flushing encoder failed!"<<endl;  goto end;  }  //[9]  //[10] --写文件尾  av_write_trailer(pFormatCtx);  //[10]  end:  //释放内存  if(video_st)  {  avcodec_close(video_st->codec);  av_free(picture);  av_free(picture_buf);  }  if(pFormatCtx)  {  avio_close(pFormatCtx->pb);  avformat_free_context(pFormatCtx);  }  fclose(in_file);  return 0;  
}  int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index)  
{  int ret;  int got_frame;  AVPacket enc_pkt;  if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &  CODEC_CAP_DELAY))  return 0;  while (1) {  printf("Flushing stream #%u encoder\n", stream_index);  enc_pkt.data = NULL;  enc_pkt.size = 0;  av_init_packet(&enc_pkt);  ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,  NULL, &got_frame);  av_frame_free(NULL);  if (ret < 0)  break;  if (!got_frame)  {ret=0;break;}  cout<<"success encoder 1 frame"<<endl;  // parpare packet for muxing  enc_pkt.stream_index = stream_index;  av_packet_rescale_ts(&enc_pkt,  fmt_ctx->streams[stream_index]->codec->time_base,  fmt_ctx->streams[stream_index]->time_base);  ret = av_interleaved_write_frame(fmt_ctx, &enc_pkt);  if (ret < 0)  break;  }  return ret;  
} 

在步骤9中已说明要完成最后的格式封装(mux),需要对AVPacket的参数进行设置。具体设置项如下:
       // parpare packet for muxing  
        enc_pkt.stream_index = stream_index;  
        av_packet_rescale_ts(&enc_pkt,  
        fmt_ctx->streams[stream_index]->codec->time_base,  
        fmt_ctx->streams[stream_index]->time_base);  
包括设置AVPacket所属的码流index,

2、将pts(显示时间戳)和dts(编码时间戳)和duration(nextpts - curpts)按照不同time_base(时间基准)进行转换。

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

更多推荐

基于FFmpeg将pcm+rgb编码并封装MP4

本文发布于:2024-02-16 17:48:12,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1690958.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:pcm   FFmpeg   rgb

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!