FFmpeg 基本操作摘要(一) (转流、解码、编码)

#PS:要转载请注明出处,本人版权所有

#PS:这个只是 《 我自己 》理解,如果和你的

#原则相冲突,请谅解,勿喷

背景

近一段时间来,自己的主要工作都在是在做数据源端(设备端)音视频传输、编解码相关的事情,于是逐渐又和FFmpeg打上了交到,用到了其中的一些常见的内容,同时也解决了一些不常见的事情。
同时这里表达对“”“”雷神“”“”的敬意(一路走好,雷神),其blog上相关的FFmpeg教程是我学习FFmpeg的启蒙教程,其的blog可以不夸张的说,是国内非常非常非常好非常非常非常好非常非常非常好的音视频相关入门教程了,建议搞音视频的人可以多参考。这里提供一个链接指向雷神的博客地址,供大家参考(需从零学习FFmpeg,请参考其FFmpeg专栏):https://blog.csdn.net/leixiaohua1020

FFmpeg 相关内容简介

FFmpeg常用结构体
AVFormatContext   
//Format I/O context.(FFmpeg中的IO流的格式容器,具体来说就是用来记录打开的流的格式相关信息,列如:记录打开的rtsp流中的音视频流格式信息、MP4
文件的流格式信息等等)
AVCodecContext    //FFmpeg中记录编码器相关信息的容器

AVCodec  //FFmpeg 中编码器容器

AVFrame //This structure describes decoded (raw) audio or video data.(FFmpeg中用于描述裸音视频数据的容器,列如:视频中的YUV数据,音频中的PCM数据)

AVPacket  // This structure stores compressed data. It is typically exported by demuxers
 //and then passed as input to decoders, or received as output from encoders and
 //then passed to muxers.(此结构体用于描述音视频编码后的数据,主要用在把编码后的数据送入解码器或者接收编码器输出的数据,列如:视频中的H264数据, 音频中的:G711A数据)

SwsContext //图像缩放和变换的关键容器
FFmpeg中常见调用
void av_register_all(void); // Initialize libavformat and register all the muxers, demuxers and
 // protocols. If you do not call this function, then you can select
 // exactly which formats you want to support.
 //FFmpeg 中用于初始化libavformat 库,注册音视频复用器,各种协议等等。 这里需要注意的是,许多没有用到了内容都注册了,可能造成浪费,若需要指定注册哪些内容,可参考av_register_output_format()、av_register_input_format()


int avformat_network_init(void); //此函数用于初始化网络相关的组件,列如:你要解析RTSP流,需要先调用此函数
/**
 * Do global initialization of network components. This is optional,
 * but recommended, since it avoids the overhead of implicitly
 * doing the setup for each session.
 *
 * Calling this function will become mandatory if using network
 * protocols at some major version bump.
 */

AVFormatContext *avformat_alloc_context(void); //为AVFormatContext分配空间
void avformat_free_context(AVFormatContext *s); //释放AVFormatContext空间

int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);//Open an input stream and read the header.


int avio_open(AVIOContext **s, const char *url, int flags); // 用于初始化AVIOContext,此结构体用于文件的读写操作
 //* Create and initialize a AVIOContext for accessing the
 //* resource indicated by url.

int avio_close(AVIOContext *s); //释放一个AVIOContext对象

int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options); //读取流数据,分析流信息
//Read packets of a media file to get stream information. 

void av_dump_format(AVFormatContext *ic,
                    int index,
                    const char *url,
                    int is_output);//就是打印一下AVFormatContext 里面存放的一些我们关心的信息
 /*
  * Print detailed information about the input or output format, such as
 * duration, bitrate, streams, container, programs, metadata, side data,
 * codec and time base.
*/


AVCodec *avcodec_find_decoder(enum AVCodecID id);查找一个解码器
//Find a registered decoder with a matching codec ID.

int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);//初始化一个编码器,初始化此结构体AVCodecContext 

/*
 * Initialize the AVCodecContext to use the given AVCodec. Prior to using this
 * function the context has to be allocated with avcodec_alloc_context3().
*/

int avcodec_close(AVCodecContext *avctx);//释放AVCodecContext 空间

int av_read_frame(AVFormatContext *s, AVPacket *pkt);//返回下一帧,数据(编码后的数据)存放到pkt里面,这里会给AVPacket 分配空间,此处一定要释放,否则将会发生内存泄漏
//Return the next frame of a stream.

int av_new_packet(AVPacket *pkt, int size);//给AVPacket buf成员分配空间,同理上文
/*
 * Allocate the payload of a packet and initialize its fields with
 * default values.
*/
void av_free_packet(AVPacket *pkt);//释放AVPacket 

AVFrame *av_frame_alloc(void);//申请AVFrame空间 
void av_frame_free(AVFrame **frame);//释放AVFrame空间 

void *av_malloc(size_t size);//ffmpeg中申请一定大小的内存空间
void av_free(void *ptr);//释放空间
//av_malloc  av_free是对malloc和free的封装,本质就是基础内存操作

struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
                                  int dstW, int dstH, enum AVPixelFormat dstFormat,
                                  int flags, SwsFilter *srcFilter,
                                  SwsFilter *dstFilter, const double *param);//创建一个指定大小和颜色空间的SwsContext 
/*
 * Allocate and return an SwsContext. You need it to perform
 * scaling/conversion operations using sws_scale().
*/

int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
              const int srcStride[], int srcSliceY, int srcSliceH,
              uint8_t *const dst[], const int dstStride[]);//根据指定SwsContext ,来对原始图像进行变换
/*
 * Scale the image slice in srcSlice and put the resulting scaled
 * slice in the image in dst. A slice is a sequence of consecutive
 * rows in an image.
*/

int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
                         int *got_picture_ptr,
                         const AVPacket *avpkt);//对一帧数据进行解码
/ * Decode the video frame of size avpkt->size from avpkt->data into picture.
 * Some decoders may support multiple frames in a single AVPacket, such
 * decoders would then just decode the first frame.
 * /
int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt,
                          const AVFrame *frame, int *got_packet_ptr);//对一帧数据进行编码

FFmpeg中需要注意的事项

FFmpeg中除了正常使用外,需要我们注意的就是内存泄漏的问题了。
这里我们需要关注几个问题:

/*
对于以下的结构体,一定要重点关注,申请了内存,一定要释放,否则在循环调用一些内容时,一定会发生内存泄漏。
AVFormatContext   
AVCodecContext    
AVCodec  
AVFrame 
AVPacket  

其中我们需要核心关注AVPacket这个结构体,这个结构体很容易让我们懵逼,出现内存泄漏。
av_read_frame
av_new_packet
上面两个都会申请内存,需要我们释放内存
av_free_packet
av_packet_unref
由于AVPacket 设计为缓冲区计数的方式,实现了一定的数据共享,方便FFmpeg管理内存,但是这里也给我们埋下了地雷:那就是AVPacket的生命周期就不好确定了,因为你调用了以上的函数,而内存不一定释放了。关于AVPacket还有许多函数,比如av_copy_packet、av_packet_ref、av_init_packet等,使用它们一定要小心,如果需要深入研究,建议查看它们的源码,这样就能够对这些调用有了更深刻的理解。
*/

这里我再多说一句:由于FFmpeg2 和FFmpeg3有巨大的区别,很多东西也发生了变化,网上的教程很多是FFmpeg3以后的,使用的时候需要注意。同时,FFmpeg真的一不留神就发生内存泄漏了,需要我们注意。

FFmpeg 使用的一般流程

解码过程
    av_register_all  
    avformat_network_init
    avformat_alloc_context//初始化AVFormatContext
    avformat_open_input //打开流
    avformat_find_stream_info //读取流信息
    avcodec_find_decoder //根据上调用来确定编码器id,然后来查找相关编码器
    avcodec_open2 //打开编码器,初始化AVCodecContext    
    av_read_frame //读取一帧压缩数据
    avcodec_decode_video2 //解压一帧数据
    sws_scale //数据转换
    //后续对原始音视频数据进行相关处理
编码过程
    av_register_all  
    avformat_network_init
    avformat_alloc_context
    avio_open
    avformat_new_stream
    //这里通过相关方法把原始音视频数据准备好
    avcodec_find_decoder //根据上调用来确定编码器id,然后来查找相关编码器
    avcodec_open2 //打开编码器,初始化AVCodecContext   
    avcodec_encode_video2 //编码
    //后续处理编码后的事情 
转流过程
    av_register_all  
    avformat_network_init
    avformat_alloc_context//初始化AVFormatContext
    avformat_open_input //打开流
    avformat_find_stream_info //读取流信息
    avcodec_find_decoder //根据上调用来确定编码器id,然后来查找相关编码器
    avcodec_open2 //打开编码器,初始化AVCodecContext    
    av_read_frame //读取一帧压缩数据
    //这里对av_read_frame 中的pkt data数据域和size数据域进行处理即可

#PS:请尊重原创,不喜勿喷

#PS:要转载请注明出处,本人版权所有.

有问题请留言,看到后我会第一时间回复

猜你喜欢

转载自blog.csdn.net/u011728480/article/details/80249445