`
yls708pq
  • 浏览: 13872 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

在ffmpeg中增加libavformat库所支持格式的媒体文件

阅读更多

在ffmpeg中增加libavformat库所支持格式的媒体文件
2011年05月11日
  如何输出一个libavformat库所支持格式的媒体文件,具体步骤如下:
  (1)av_register_all(),初始化 libavcodec库,并注册所有的编解码器和格式。
  (2)guess_format(),根据文件名来获取输出文件格式,默认为mpeg。
  (3)av_alloc_format_context() 分配输出媒体内容。
  ov->oformat = fmt;
  snprintf( oc->filename, sizeof(oc->filename), "%s", filename );
  (4)add_video_stream()使用默认格式的编解码器来增加一个视频流,并初始化编解码器。
  (4.1)av_new_stream()增加一个新的流到一个媒体文件。
  (4.2)初始化编解码器:
  c = st->codec;
  c->codec_id = codec_id;
  c->codec_type = CODEC_TYPE_VIDEO;
  c->bit_rate = 400000;
  c->width = 352;
  c->height = 288;
  c->time_base.den = STREAM_FRAME_RATE; //每秒25副图像
  c->time_base.num = 1;
  c->gop_size = 12;
  c->pix_fmt = STREAM_PIX_FMT; //默认格式为PIX_FMT_YUV420P
  …… ……
  (5)av_set_parameters()设置输出参数,即使没有参数,该函数也必须被调用。
  (6)dump_format()输出格式信息,用于调试。
  (7)open_video()打开视频编解码器并分配必要的编码缓存。
  (7.1)avcodec_find_encoder()寻找c->codec_id指定的视频编码器。
  (7.2)avcodec_open()打开编码器。
  (7.3)分配视频输出缓存:
  video_outbuf_size = 200000;
  video_outbuf = av_malloc( video_outbuf_size );
  (7.4)picture = alloc_picture()分配原始图像。
  (7.4.1)avcodec_alloc_frame()分配一个AVFrame并设置默认值。
  (7.4.2)size = avpicture_get_size()计算对于给定的图片格式以及宽和高,所需占用多少内存。
  (7.4.3)picture_buf = av_malloc( size )分配所需内存。
  (7.4.4)avpicture_fill()填充AVPicture的域。
  (7.5)可选。如果输出格式不是YUV420P,那么临时的YUV420P格式的图像也是需要的,由此再转换为我们所需的格式,因此需要为临时的YUV420P图像分配缓存:
  tmp_picture = alloc_picture()
  说明:tmp_picture,picture,video_outbuf。如果输出格式为YUV420P,则直接通过avcodec_encode_video()函数将picture缓存中的原始图像编码保存到video_outbuf缓存中;如果输出格式不是YUV420P,则需要先通过sws_scale()函数,将YUV420P格式转换为目标格式,此时tmp_picture缓存存放的是YUV420P格式的图像,而picture缓存为转换为目标格式后保存的图像,进而再将 picture缓存中的图像编码保存到video_outbuf缓存中。
  (8)url_fopen()打开输出文件,如果需要的话。
  (9)av_write_header()写流动头部。
  (10)LOOP循环{
  计算当前视频时间video_pts
  是否超时退出循环?
  write_video_frame()视频编码
  }
  (10.1)write_video_frame()
  如果图片不是YUV420P,则需要用 sws_scale()函数先进行格式转换。
  若需要原始图像:
  av_init_packet()初始化一个包的选项域。
  av_write_frame()向输出媒体文件写一个包,该包会包含一个视频帧。
  若需要编码图像:
  avcodec_encode_video() 编码一视频帧。
  av_init_packet()
  av_write_frame()
  (11)close_video()关闭每个编解码器。
  (12)av_write_trailer()写流的尾部。
  (13)释放资源
  av_freep()释放 AVFormatContext下的AVStream->AVCodecContext和AVStream:
  for( i = 0; i nb_streams; i++ ){
  av_freep( &oc->streams[i]->codec );
  av_freep( &oc->streams[i] );
  }
  url_fclose()关闭输出文件。
  av_free()释放 AVFormatContext。
  apiexample.c例子教我们如何去利用ffmpeg 库中的api函数来自己编写编解码程序。
  (1)首先,main函数中一开始会去调用avcodec_init()函数,该函数的作用是初始化libavcodec,而我们在使用avcodec库时,该函数必须被调用。
  (2)avcodec_register_all()函数,注册所有的编解码器(codecs),解析器(parsers)以及码流过滤器(bitstream filters)。当然我们也可以使用个别的注册函数来注册我们所要支持的格式。
  (3)video_encode_example()函数用于视频编码,由图可知,所有的编码工作都在该函数内完成。
  (4)avcodec_find_encoder()函数用于查找一个与codec ID相匹配的已注册的编码器。
  (5)avcodec_alloc_context() 函数用于分配一个AVCodecContext并设置默认值,如果失败返回NULL,并可用av_free()进行释放。
  (6)avcodec_alloc_frame()函数用于分配一个AVFrame并设置默认值,如果失败返回NULL,并可用av_free()进行释放。
  (7)设置参数:
  设置采样参数,即比特率。
  c->bit_rate = 400000;
  设置分辨率,必须是2的倍数。
  c->width = 352;
  c->height = 288;
  设置帧率。
  c->time_base = (AVRational){1,25}; 该帧率为25,其实timebase = 1/framerate,花括号内分别为分子和分母。
  设置GOP大小。
  c->gop_size = 10; 该值表示每10帧会插入一个I帧(intra frame)。
  设置B帧最大数。
  c->max_b_frames = 1; 该值表示在两个非B帧之间,所允许插入的B帧的最大帧数。
  设置像素格式。
  c->pix_fmt = PIX_FMT_YUV420P; 该值将像素格式设置为YUV420P。
  (8)avcodec_open()函数用给定的 AVCodec来初始化AVCodecContext。
  (9)接着是打开文件,f = fopen( filename, "wb" );
  (10)分配图像和输出缓存。
  申请100KB左右的内存作为输出缓存。
  outbuf_size = 100000;
  outbuf = malloc( outbuf_size );
  根据帧的大小来确定YUV420所占内存大小,一个像素,RGB格式占用3个字节,而YUV420格式只占用两个字节。YUV420格式是指,每个像素都保留一个Y(亮度)分量,而在水平方向上,不是每行都取U和V分量,而是一行只取U分量,则其接着一行就只取V分量,以此重复,所以420不是指没有V,而是指一行采样只取U,另一行采样只取V。在取U和 V时,每两个Y之间取一个U或V。但从4x4矩阵列来看,每4个矩阵点Y区域中,只有一个U和V,所以它们的比值是4:1。所以对于一个像素,RGB需要 8 * 3 = 24位,即占3个字节;而YUV420P,8 + 8/4 + 8/4 = 12位,即占2个字节,其中8指Y分量,8/4指U和V分量。
  size = c->width * c->height;
  picture_buf = malloc( (size * 3) / 2 );
  picture->data[0] = picture_buf;
  picture->data[1] = picture->data[0] + size;
  picture->data[2] = picture->data[1] + size / 4;
  picture->linesize[0] = c->width;
  picture->linesize[1] = c->width / 2;
  picture->linesize[2] = c->width / 2;
  其中,data[0]存放Y,data[1] 存放U,data[2]存放V【FixMe】。linesize[0]表示Y分量的宽度,linesize[1]表示U分量的宽度,linesize[2]表示V分量的宽度。
  (11)编码一秒钟的视频,帧率为25,所以需要循环 25次,每次编码一帧。
  (11.1)准备一幅伪图像,即自己自定义往里面塞数据。
  for(y=0;yheight;y++){
  for(x=0;xwidth;x++){
  picture->data[0][y*picture->linesize[0]+x]=x+y+i*3;
  }
  }
  for(y=0;yheight/2;y++){
  for(x=0;xwidth/2;x++){
  picture->data[1][y*picture->linesize[1]+x]=128+y+i*2 ;
  picture->data[2][y*picture->linesize[2]+x]=64+x+i*5;
  }
  }
  (11.2)avcodec_encode_video()从picture中编码一帧视频数据,并存入到outbuf中,而期间所使用的编码器为c。
  (11.3)将编码完的数据写入到文件里。
  (12)对延时的帧数据进行编码。因为像MPEG4 中,I帧、P帧和B帧之间存在一定的延时【FixMe】。同样是avcodec_encode_video(),然后写入文件。
  (13)添加结束代码,使其成为一个真正的mpeg文件。
  outbuf[0] = 0x00;
  outbuf[1] = 0x00;
  outbuf[2] = 0x01;
  outbuf[3] = 0xb7;
  fwrite( outbuf, 1, 4, f );
  这个结束代码表示什么???
  (14)释放资源。
  fclose(f);
  free(picture_buf);
  free(outbuf);
  avcodec_close(c);
  av_free(c);
  av_free(picture);
  为什么需要两个库文件 libavformat 和 libavcodec :许多视频文件格式(AVI就是一个最好的例子)实际上并没有明确指出应该使用哪种编码来解析音频和视频数据;它们只是定义了音频流和视频流(或者,有可 能是多个音频视频流)如何被绑定在一个文件里面。这就是为什么有时候,当你打开了一个AVI文件时,你只能听到声音,却不能看到图象--因为你的系统没有 安装合适的视频解码器。所以, libavformat 用来处理解析视频文件并将包含在其中的流分离出来, 而libavcodec 则处理原始音频和视频流的解码。
  打开视频文件:
  首先第一件事情--让我们来看看怎样打开一个视频文件并从中得到流。我们要做的第 一件事情就是初始化libavformat/libavcodec:
  av_register_all();
  这一步注册库中含有的 所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。要注意你只需调用一次 av_register_all(),所以,尽可能的在你的初始代码中使用它。如果你愿意,你可以仅仅注册个人的文件格式和编码,不过,通常你不得不这么 做却没有什么原因。
  下一步,打开文件:
  AVFormatContext *pFormatCtx;
  const char      *filename="myvideo.mpg";
  // 打开视频文件
  if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0)
  handle_error(); // 不能打开此文件
  最后三个参数描述了文件格式,缓冲区大小(size)和格式参数;我们通过简单地指明NULL或0告诉 libavformat 去自动探测文件格式并且使用默认的缓冲区大小。请在你的程序中用合适的出错处理函数替换掉handle_error()。
  下 一步,我们需要取出包含在文件中的流信息:
  // 取出流信息
  if(av_find_stream_info(pFormatCtx)nb_streams; i++)
  if(pFormatCtx->streams->codec.codec_type==CODEC_TYPE _VIDEO)
  {
  videoStream=i;
  break;
  }
  if(videoStream==-1)
  handle_error(); // Didn't find a video stream
  // 得到视频流编码上下文的指针
  pCodecCtx=&pFormatCtx->streams[videoStream]->codec;
  好 了,我们已经得到了一个指向视频流的称之为上下文的指针。但是我们仍然需要找到真正的编码器打开它。
  AVCodec *pCodec;
  //  寻 找视频流的解码器
  pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
  if(pCodec==NULL)
  handle_error(); // 找不到解码器
  // 通知解码器我们能够处理截断的bit流--ie,
  // bit流帧边界可以在包中
  if(pCodec->capabilities & CODEC_CAP_TRUNCATED)
  pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;
  // 打开解码器
  if(avcodec_open(pCodecCtx, pCodec)frame_rate,分母在 pCodecCtx->frame_rate_base 中。在用不同的视频文件测试库时,我注意到一些编码器(很显然ASF)似乎并不能正确的给予赋值( frame_rate_base 用1代替1000)。下面给出修复补丁:
  //  加入这句话来纠正某些编码器产生的帧速错误
  if(pCodecCtx->frame_rate>1000 && pCodecCtx->frame_rate_base==1)
  pCodecCtx->frame_rate_base=1000;
  注意即使将来这个bug解决了,留下这几句话也并没有什么坏 处。视频不可能拥有超过1000fps的帧速。
  只剩下一件事情要做了:给视频帧分配空间以便存储解码后的图片:
  AVFrame *pFrame;
  pFrame=avcodec_alloc_frame();
  就这样,现在我们开始解码这些视频。
  解 码视频帧
  就像我前面提到过的,视频文件包含数个音频和视频流,并且他们各个独自被分开存储在固定大小的包里。我们要做的就是使用 libavformat依次读取这些包,过滤掉所有那些视频流中我们不感兴趣的部分,并把它们交给 libavcodec 进行解码处理。在做这件事情时,我们要注意这样一个事实,两帧之间的边界也可以在包的中间部分。
  听起来很复杂?幸运的是,我们在一个例程中封装了 整个过程,它仅仅返回下一帧:
  bool GetNextFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx,
  int videoStream, AVFrame *pFrame)
  {
  static AVPacket packet;
  static int      bytesRemaining=0;
  static uint8_t  *rawData;
  static bool     fFirstTime=true;
  Int bytesDecoded;
  Int frameFinished;
  //  我 们第一次调用时,将 packet.data 设置为NULL指明它不用释放了
  if(fFirstTime)
  {
  fFirstTime=false;
  packet.data=NULL;
  }
  // 解码直到成功解码完整的一帧
  while(true)
  {
  //  除非解码完毕,否则一直在当前包中工作
  while(bytesRemaining > 0)
  {
  //  解码下一块数据
  bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame,
  &frameFinished, rawData, bytesRemaining);
  // 出错了?
  if(bytesDecoded pix_fmt, pCodecCtx->width, pCodecCtx->height);
  // 处理视频帧(存盘等等)
  DoSomethingWithTheImage(pFrameRGB);
  }
  RGB 图象pFrameRGB (AVFrame *类型)的空间分配如下:
  AVFrame *pFrameRGB;
  int     numBytes;
  uint8_t *buffer;
  // 分配一个AVFrame 结构的空间
  pFrameRGB=avcodec_alloc_frame();
  if(pFrameRGB==NULL)
  handle_error();
  // 确认所需缓冲区大小并且分配缓冲区空间
  numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
  pCodecCtx->height);
  buffer=new uint8_t[numBytes];
  // 在pFrameRGB中给图象位面赋予合适的缓冲区
  avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
  pCodecCtx->width, pCodecCtx->height);
  清除
  好了,我们已经处理了我们的视 频,现在需要做的就是清除我们自己的东西:
  // 释放 RGB 图象
  delete [] buffer;
  av_free(pFrameRGB);
  // 释放YUV 帧
  av_free(pFrame);
  // 关闭解码器(codec)
  avcodec_close(pCodecCtx);
  // 关闭视频文件
  av_close_input_file(pFormatCtx);
分享到:
评论
1 楼 Khan.Lau 2013-02-01  
To decode an MPEG-2 bitstream, the processor should execute the following tasks, in
order:
1. Initialize the horizontal, horizontal sync, vertical, vertical sync and video mode
registers with reasonable defaults. Clear osd enable, picture hdr intr en and
frame end intr en. Set the video ch intr en flag.
2. Start feeding the MPEG-2 bitstream to the stream data port of the decoder.
3. The decoder will issue an interrupt when video resolution or frame rate changes.
Whenever the decoder issues an interrupt, clear the interrupt by reading the status
register. Read the size, display size and frame rate registers. Calculate a new
modeline, change dot clock frequency if necessary, and write the new video timing
parameters to the horizontal, horizontal sync, vertical, vertical sync and video mode
registers.
4. At bitstream end, pad the stream with 8 times hex 000001b7, the sequence end
code (ISO/IEC 13818-2, par. 6.2.1, Start Codes).

注意第四点

相关推荐

    FFmpeg基础库编程开发

    9.3.1 Mplayer支持的格式 427 9.3.2 Mplayer 中头文件的功能分析 427 9.3.3 MPlayer.main 主流程简要说明 428 9.3.4 Mplayer源码分析 429 第十章 开发实例 436 第十一章 mp4文件封装协议分析 436 11.1 概述 436 11.2...

    FFmpeg相关库文件 用于与FFmpeg相关内容的开发

    FFmpeg是一套可以用来记录、转换数字音频、...FFmpeg视频采集功能非常强大,不仅可以采集视频采集卡或USB摄像头的图像,还可以进行屏幕录制,同时还支持以RTP方式将视频流传送给支持RTSP的流媒体服务器,支持直播应用。

    ffmpeg最详细的资料

    ffmpeg中有五个库文件,libavcodec,libavformat,libavutil,libswscale,libpostproc,其中库libavcodec,libavformat用于对媒体文件进行处理,如格式的转换;libavutil是一个通用的小型函数库,该库中实现了CRC...

    ffmpeg

    也支持时间平移 ffplay 用 SDL和FFmpeg库开发的一个简单的媒体播放器 libavcodec 一个包含了所有FFmpeg音视频编解码器的库.为了保证最优性能和高可复用性,大多数编解码器从头开发的. libavformat 一个...

    使用FFMPEG+easydarwin把本地摄像头进行rtsp推流指令

    最近在学习ffmpeg的基本操作,ffmpeg功能非常强大,可以运行音频和视频多种格式的录影、转换、流功能,包含了libavcodec——这是一个用于多个项目中音频和视频的解码器库,以及libavformat——一个音频与视频格式...

    ffmpeg基础开发资料自总结

    适合fresh man上手 ffmpeg 库。 目录 ------------------------- 目录 第一章 多媒体概念介绍 6 1.1 视频格式 6 1.1.1 常见格式 6 1.2 音频格式 8 1.2.1 常见格式 9 1.2.2 比较 14 1.3 字幕格式 14 1.3.1 外挂...

    ffmpeg-2.6::最新的ffmpeg原始码2.6强大的流媒体开发工具支持多种流媒体格式的编解码以及流媒体服务器的实现-最新的2.6源ffmpeg强大的流媒体开发工具支持多种流媒体格式编解码器和流媒体服务器,达到

    FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 libavutil...

    FFmpeg:https:git.ffmpeg.orgffmpeg.git的镜像

    FFmpeg自述文件 FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。 图书馆 libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本I / O访问。 libavutil...

    git://source.ffmpeg.org/ffmpeg.git的镜像-C/C++开发

    FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。 库libavcodec提供了更广泛的编解码器实现FFmpeg自述FFmpeg是一组库和工具,用于处理多媒体内容,例如音频,视频...

    LAV Filters v0.72.0-3 Nightly.zip

    软件基于 ffmpeg 项目中的 libavformat/libavcodec 库,旨在最终取代播放链中的绝大部分滤镜,能播放所有的格式和任何现代媒体。软件除了通用的 DXVA2 外,也特别支持 NVIDIA CUDA 和 Intel QuickSync。软件简单全面...

    DirectShow分离器和音视解码器(LAV Filters) v0.60.1.rar

    DirectShow分离器和音视解码器(LAV Filters)汉化版是一组基于 ffmpeg 项目中的 libavformat/libavcodec 库的 DirectShow 分离器和音视解码器,几乎允许您在 DirectShow 播放器中播放任何格式。 LAV Filters 包含三...

    FFmpeg:https的镜像

    FFmpeg自述文件 FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。 图书馆 libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 ...

    ffmpeg:FFmpeg + ONNXRunTime

    FFmpeg自述文件 FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。 图书馆 libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 ...

    ffmpeg:https:git.ffmpeg.orgffmpeg.git

    FFmpeg自述文件 FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。 图书馆 libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本I / O访问。 libavutil...

    ffmpeg:ffmpeg.org贡献的英特尔开发人员暂存区

    FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本I / O访问。 libavutil包括...

    LAVFilters-0.75.1-Installer.exe

    lav filters 是一组基于 ffmpeg 项目中的 libavformat/libavcodec 库的 directshow 分离器和音视频解码器,几乎允许您在 directshow 播放器中播放任何格式的媒体文件!

    ffmpeg_libnvenc:这是FFmpeg的内部版本,支持NVIDIA的NVENC SIP块进行H.264硬件加速视频编码

    FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 libavutil...

    thumbnailer:转到媒体缩略图

    依存关系转到> = 1.10 C11编译器制作pkg-config pthread ffmpeg> = 4.1库(libavcodec,libavutil,libavformat,libswscale) 注意: ffmpeg应该与所有依赖项库一起编译为您要处理的格式。 在大多数Linux发行版中,...

    ppsspp-ffmpeg:PPSSPP中使用的FFMPEG精简版。 包括h.264,atrac3 +,AAC和其他一些编解码器

    FFmpeg自述文件FFmpeg是用于处理多媒体内容(例如音频,视频,字幕和相关元数据)的库和工具的集合。图书馆libavcodec提供了更多编解码器的实现。 libavformat实现流协议,容器格式和基本的I / O访问。 libavutil...

    Lav Filiters 解码器安装包

    LAV Filters中文版是一款非常不错的视频...LAV Filters中文版是一组基于 ffmpeg 项目中的 libavformat/libavcodec 库的 directshow 分离器和音视频解码器,几乎允许您在 directshow 播放器中播放任何格式的媒体文件!

Global site tag (gtag.js) - Google Analytics