您的位置:首页 > 其它

libav/FFMPEG 视频音频编解码库使用

2015-12-30 16:50 926 查看

简介

在开始的时候,使用一个v4l2的lib example代码,用起来不错,但是后来使用sony的相机之后发现需要解码(MJPG),但是试了很多方法都不行,其中包括opencv的videoCapture, 使用ffmpeg解码,后来采用libav,的方式可以成功。

Libav是ffmpeg的devel版本。

在使用的时候包含

LIBS += -L -lavcodec -lavformat -lavutil -lavdevice -lswscale -lx264


由于调用的是c程序,因此需要使用extern c.

#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#define UINT64_C

extern "C" {
#include <libavformat/avformat.h>
#include "libavcodec/avcodec.h"
#include "libavutil/mathematics.h"
#include "libavutil/samplefmt.h"
#include "libswscale/swscale.h"
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
}


使用流程

1. 注册

av_register_all();
avdevice_register_all();
avcodec_register_all();
avformat_network_init();


ffmpeg注册复用器,编码器等的函数
av_register_all()
。该函数在所有基于ffmpeg的应用程序中几乎都是第一个被调用的。只有调用了该函数,才能使用复用器,编码器等。

ffmpeg注册编解码器等的函数
avcodec_register_all()
(注意不是
av_register_all()
,那是注册所有东西的)。该函数在所有基于ffmpeg的应用程序中几乎都是第一个被调用的。只有调用了该函数,才能使用编解码器等。

在使用libavdevice之前,必须先运行
avdevice_register_all()
对设备进行注册,否则就会出错。
avdevice_register_all()
的注册方式。

《最简单的基于FFmpeg的AVDevice例子(读取摄像头)》

http://blog.csdn.net/leixiaohua1020/article/details/39702113

2.内存操作

内存操作的常见函数位于libavutil\mem.c中。FFmpeg开发中最常使用的几个函数:
av_malloc(),av_realloc(),av_mallocz(),av_calloc(),av_free(),av_freep()


av_malloc()是FFmpeg中最常见的内存分配函数。

一个介绍基本函数流程的jpeg

http://img.my.csdn.net/uploads/201503/12/1426134989_1189.jpg



3.主循环之前的配置

过程开始于avformat_open_input,因此该函数的重要性不可忽视。

在该函数中,FFMPEG完成了:

输入输出结构体AVIOContext的初始化;

输入数据的协议(例如RTMP,或者file)的识别(通过一套评分机制):1判断文件名的后缀 2读取文件头的数据进行比对;

使用获得最高分的文件协议对应的URLProtocol,通过函数指针的方式,与FFMPEG连接(非专业用词);

4.主循环

剩下的就是调用该URLProtocol的函数进行open,read等操作了

FFMPEG中结构体很多。最关键的结构体可以分成以下几类:

a)解协议(http,rtsp,rtmp,mms)

AVIOContext,URLProtocol,URLContext主要存储视音频使用的协议的类型以及状态。URLProtocol存储输入视音频使用的封装格式。每种协议都对应一个URLProtocol结构。(注意:FFMPEG中文件也被当做一种协议“file”)

b)解封装(flv,avi,rmvb,mp4)

AVFormatContext主要存储视音频封装格式中包含的信息;AVInputFormat存储输入视音频使用的封装格式。每种视音频封装格式都对应一个AVInputFormat 结构。

c)解码(h264,mpeg2,aac,mp3)

每个AVStream存储一个视频/音频流的相关数据;每个AVStream对应一个AVCodecContext,存储该视频/音频流使用解码方式的相关数据;每个AVCodecContext中对应一个AVCodec,包含该视频/音频对应的解码器。每种解码器都对应一个AVCodec结构。

d) 存数据

视频的话,每个结构一般是存一帧;音频可能有好几帧

解码前数据:AVPacket

解码后数据:AVFrame

他们之间的对应关系如下所示:



具体函数

初始化和销毁

下文简单分析一下上述几个结构体的初始化和销毁函数。这些函数列表如下。

结构体初始化销毁
AVFormatContextavformat_alloc_context()avformat_free_context()
AVIOContextavio_alloc_context()
AVStreamavformat_new_stream()
AVCodecContextavcodec_alloc_context3()
AVFrameav_frame_alloc()av_frame_free()
/av_image_fill_arrays()
AVPacketav_init_packet()av_free_packet()
/av_new_packet()
avformat_open_input()
。该函数用于打开多媒体数据并且获得一些相关的信息。它的声明位于libavformat\avformat.h,如下所示。

int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);


ps:函数调用成功之后处理过的AVFormatContext结构体。

file:打开的视音频流的URL。

fmt:强制指定AVFormatContext中AVInputFormat的。这个参数一般情况下可以设置为NULL,这样FFmpeg可以自动检测AVInputFormat。

dictionay:附加的一些选项,一般情况下可以设置为NULL。

函数执行成功的话,其返回值大于等于0。

avformat_find_stream_info()。该函数可以读取一部分视音频数据并且获得一些相关的信息。avformat_find_stream_info()的声明位于libavformat\avformat.h,如下所示。



设置解码格式

code and encode

avcodec_find_encoder()
avcodec_find_decoder()


avcodec_find_encoder()用于查找FFmpeg的编码器,avcodec_find_decoder()用于查找FFmpeg的解码器。

我的编码器使用了x264库,如果使用ffmpeg,可以参考

http://blog.csdn.net/leixiaohua1020/article/details/25430425

avcodec_open2()


所做的工作,如下所列:

(1)为各种结构体分配内存(通过各种av_malloc()实现)。

(2)将输入的AVDictionary形式的选项设置到AVCodecContext。

(3)其他一些零零碎碎的检查,比如说检查编解码器是否处于“实验”阶段。

(4)如果是编码器,检查输入参数是否符合编码器的要求

(5)调用AVCodec的init()初始化具体的解码器。

主循环

av_read_frame()


av_read_frame()的作用是读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码(例如H.264中一帧压缩数据通常对应一个NAL)。

对该函数源代码的分析是很久之前做的了,现在翻出来,用博客记录一下。

上代码之前,先参考了其他人对av_read_frame()的解释,在此做一个参考:

通过av_read_packet(***),读取一个包,需要说明的是此函数必须是包含整数帧的,不存在半帧的情况,以ts流为例,是读取一个完整的PES包(一个完整pes包包含若干视频或音频es包),读取完毕后,通过av_parser_parse2(***)分析出视频一帧(或音频若干帧),返回,下次进入循环的时候,如果上次的数据没有完全取完,则st = s->cur_st;不会是NULL,即再此进入av_parser_parse2(***)流程,而不是下面的av_read_packet(**)流程,这样就保证了,如果读取一次包含了N帧视频数据(以视频为例),则调用av_read_frame(***)N次都不会去读数据,而是返回第一次读取的数据,直到全部解析完毕。

av_read_frame()的声明位于libavformat\avformat.h,如下所示。

avcodec_decode_video2()


ffmpeg中的avcodec_decode_video2()的作用是解码一帧视频数据。输入一个压缩编码的结构体AVPacket,输出一个解码后的结构体AVFrame。该函数的声明位于libavcodec\avcodec.h,如下所示。从代码中可以看出,avcodec_decode_video2()主要做了以下几个方面的工作:

(1)对输入的字段进行了一系列的检查工作:例如宽高是否正确,输入是否为视频等等。

(2)通过ret = avctx->codec->decode(avctx, picture, got_picture_ptr,&tmp)这句代码,调用了相应AVCodec的decode()函数,完成了解码操作。

(3)对得到的AVFrame的一些字段进行了赋值,例如宽高、像素格式等等。

其中第二部是关键的一步,它调用了AVCodec的decode()方法完成了解码。AVCodec的decode()方法是一个函数指针,指向了具体解码器的解码函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: