历史上的今天
返回首页

历史上的今天

今天是:2025年12月21日(星期日)

2022年12月21日 | FFMPEG视频编解码流程 H.264硬件编解码实现

2022-12-21 来源:elecfans

目前,智能手机、PDA和平板电脑等越来越多的嵌入式设备支持高清视频采集和播放功能,高清视频的采集或播放功能正广泛用于游戏设备、监控设备、视频会议设备和数字网络电视等嵌入式系统中。这些功能的实现建立在高性能视频硬件编解码技术基础之上。本文阐述了基于FFmpeg的H.264视频硬件编解码在S3C6410处理器上的实现方法,为数字娱乐、视频监控和视频通信系统开发过程中的高清视频硬件编解码的实现提供参考。


FFmpeg[1]是一个开源免费跨平台的视频和音频流方案,属于自由软件。它包含非常先进的音频/视频编解码库libavcodec,提供了录制、转换以及流化音视频的完整解决方案。FFmpeg支持MPEG4、FLV等40多种编码,以及AVI、ASF等90多种解码。目前国内较为流行的播放器暴风影音和国外较为流行的Mplayer在音频/视频编解码方面都用到了FFmpeg。


S3C6410[2]是三星公司推出的应用处理器芯片,基于ARM11架构,主频最高可达800 MHz。它具有多媒体硬件加速功能,其中包括大于30 fps的MPEG4 SP、H.264/263 BP和VC1(WMV9)多种视频硬件编解码,可用于手机、平板电脑和游戏机等手持移动设备和其他高性能嵌入式设备。国产手机魅族M8的处理器使用的就是S3C6410。


虽然FFmpeg提供了简单的应用程序编程接口(API),可以很方便地实现多种格式的视频软件编解码[3],但是软件编解码在处理复杂视频编解码(如H.264)时无法运用到处理速度不快、内存空间不多的嵌入式环境中。为了在资源有限的嵌入式环境下使用FFmpeg实现复杂视频编解码,下面在分析FFmpeg视频编码流程和S3C6410处理器视频编解码方法的基础上,阐述嵌入式Linux操作系统下基于FFmpeg的H.264硬件编解码在S3C6410处理器上的实现方法。


1 FFmpeg视频编解码流程

FFmpeg主要有encode/decode、muxer/demuxer和内存操作3个模块。encode/decode模块用于音视频的编码和解码,存放在libavcodec子目录中;muxer/demuxer模块用于音频和视频的合并与分离(也称混合器模块),存放在libavformat目录中;内存等常用模块存放于libavuTIl目录中。下面以解码过程为例分析FFmpeg视频编解码流程。


解码基本流程共分4步:

① 注册所有可能用到的编解码器和混合器。av_register_all(void)函数中通过执行 REGISTER_MUXDEMUX(X,x)和REGISTER_ENCDEC(X,x),把所有FFmpeg支持的混合器和编解码器相关信息以链式的结构存放在内存中。

② 打开视频文件。av_open_input_file(AVFormatContext **ic_ptr,const char *filename,AVInputFormat *fmt,int buf_size,AVFormatParameters *ap)函数中侦测文件的格式,根据文件格式从链式的混合器中找到相对应的混合器(demuxer)并分离出视频信息。

③ 获取视频信息。通过av_find_stream_info(AVFormatContext *ic)函数获取视频格式。根据视频格式,在链式的视频解码器中找到相应的视频解码器,并通过avcodec_open(AVCodecContext *avctx,AVCodec *codec)函数将解码器打开用于下一步视频的解码。

④ 解码一帧视频,通过 avcodec_decode_video(AVCodecContext *avctx,AVFrame *picture,int *got_picture_ptr,const uint8_t *buf,int buf_size)函数解码一帧视频。

FFmpeg的编码过程与解码过程类似,不同的是第3步根据要求编码的格式在链式的视频编码器中找到相应的视频编码器,并执行编码过程。

通过以上对FFmpeg视频编解码流程分析可以知道,为了在FFmpeg中添加自定义的视频编解码器,并在程序运行时使用这个编解码器,关键在于如下两点:

① 根据FFmpeg对编解码器的描述,实现自定义编解码器。

② 通过REGISTER_ENCDEC(X,x)函数将自定义的视频编解码器添加到视频编解码器链中。在获取视频信息时,保证需要编码或解码的视频能找到视频编解码器链中自定义的视频编解码器。


2 S3C6410处理器视频编解码方法

S3C6410视频编解码软件架构[4]如图1所示。底层为操作系统空间,上层为用户空间,视频编解码器通过驱动和操作系统以设备文件的形式使用,使用的方法和普通文件一样,包括文件打开和关闭、文件读写和输入/输出控制(ioctl,input/output control)。

FFMPEG视频编解码流程 H.264硬件编解码实现

图1 S3C6410视频编解码软件架构

具体操作方法如下:

① 通过open函数打开编解码器设备文件;

② 使用mmap方法在用户空间和驱动空间之间映射输入/输出缓存空间,这样做的好处是可以快速进行数据输入/输出;

③ 通过ioctl设备编解码参数,初始化编解码器;

④ 输入数据,通过ioctl执行编解码过程,输出数据;

⑤ 通过close方法关闭编解码器设备文件。

值得注意的是,无论编码还是解码,处理的数据都是以一帧帧的形式操作的,所以第4步是一个不断循环的过程,直到所有数据处理完成。另外,虽然编解码器以设备文件的形式使用,但是它不能使用标准的文件读写操作,查看编解码的设备驱动可以发现,其文件读写函数是空的,这一点三星公司的开发文档并没有说明。


3 H.264硬件编解码实现

FFmpeg的H.264硬件编解码[5]实现就是自定义一个视频编解码器,加入到FFmpeg库中。这个视频编解码器使用S3C6410处理视频硬件编解码功能来实现H.264的视频编码和解码过程,这样使用FFmpeg库的多媒体程序可以用访问FFmpeg其他编解码器一样的方法使用这个自定义的编解码器。添加自定义编解码器的关键是根据FFmpeg中对编解码的描述定义编解码器,并实现定义中的相关函数。


在libavcodec/avcodec.h中的AVCodec结构体是定义FFmpeg编解码器的关键结构体,包括编解码器的名字、类型(声音/视频)、编解码器的识别号(CodecID)、支持格式和一些用于初始化、编码、解码和关闭的函数指针。


typedef struct AVCodec {

const char *name;

enum CodecType type;

enum CodecID id;

int priv_data_size;

int (*init)(AVCodecContext *);

int (*encode)(AVCodecContext *,uint8_t *buf,int buf_size,void *data);

int (*close)(AVCodecContext *);

int (*decode)(AVCodecContext *,void *outdata,int *outdata_size,

uint8_t *buf,int buf_size);

int capabiliTIes;

struct AVCodec *next;

void (*flush)(AVCodecContext *);

const AVRaTIonal *supported_framerates;

const enum PixelFormat *pix_fmts;

} AVCodec;

H.264硬件编解码器定义如下:

AVCodec s3cx264_encoder = {

.name=“s3cx264”,

.type=AVMEDIA_TYPE_VIDEO,

.id=CODEC_ID_H264,

.init=X264_init,

.encode=X264_frame,

.decode=X264_decode,

.close=X264_close,

};

解码器的名字为s3cx264,类型为视频。CodecID为H264,表示这个解码器用于H.264视频编解码。初始化、编码、解码和关闭函数指针分别指向X264_init、X264_frame、X264_decodec和X264_close函数。


添加s3cx264编解码器到编解器链中,关键是通过修改libavcodec/allcodecs.c文件实现,修改如下:

REGISTER_ENCDEC (ASV1,asv1);

REGISTER_ENCDEC (S3CX264,s3cx264);

//添加s3cx264编解码器

REGISTER_ENCDEC (ASV2,asv2);

这样,在程序运行时调用av_register_all(void)函数后,就可以把自定义的编解码器s3cx264添加到FFmpeg存放在内存中的解编码器链中。值得提出的是,对同一个视频格式FFmpeg有多个编解码器与之相对应。如H.264格式的视频,FFmpeg本身就带有对应的软解码器,现在添加了硬解码器,为了避免不确定是哪一个解码器在执行,可以把自定义的硬件编解码器在注册时放在注册过程的最前面,这样编解码器在添加到解编器链中时就会放在靠前的位置,查找时就可以优于软件解码器找到硬解码器。


把硬件编解码器s3cx264注册到编解码器链后,还要完成X264_init、X264_frame、X264_decodec和X264_close函数,编解码器才能正常工作。以下结合前面对S3C6410视频编解码过程的分析,以编码为例详细阐述实现过程。


定义X264Context结构体,保存设备文件描述符、编码参数和输入/输出地址等信息,用于FFmpeg模块间数据的传递:

typedef struct X264Context {

int dev_fd;

uint8_t *addr;

s3c_mfc_enc_init_arg_t enc_init;

s3c_mfc_enc_exe_arg_t enc_exe;

s3c_mfc_get_buf_addr_arg_t get_buf_addr;

uint8_t *in_buf,*out_buf;

AVFrame out_pic;

} X264Context;

X264_init实现的是编码器初始化过程, 用于编码器设备文件的打开、内存空间的映射、编码参数设置和获取编解码数据输入/输出地址。

staTIc av_cold int X264_init(AVCodecContext *avctx){

X264Context *x4 = avctx》priv_data;

//打开编码器设备文件

x4》dev_fd = open(MFC_DEV_NAME,O_RDWR|O_NDELAY);

//内存空间映射

x4》addr = (uint8_t *) mmap(0,BUF_SIZE,PROT_READ |PROT_WRITE,MAP_SHARED,x4》dev_fd,0);

//编码参数设置

ioctl(x4》dev_fd,S3C_MFC_IOCTL_MFC_H264_ENC_INIT,&x4》enc_init);

//获取输入/输出地址

x4》get_buf_addr.in_usr_data = (int)x4》addr;

ioctl(x4》dev_fd,S3C_MFC_IOCTL_MFC_GET_YUV_BUF_ADDR,&x4》get_buf_addr);

x4》in_buf = (uint8_t *)x4》get_buf_addr.out_buf_addr;

x4》get_buf_addr.in_usr_data = (int)x4》addr;

ioctl(x4》dev_fd,S3C_MFC_IOCTL_MFC_GET_LINE_BUF_ADDR,&x4》get_buf_addr);

x4》out_buf = (uint8_t *)x4》get_buf_addr.out_buf_addr;

return 0;

}


ioctl的参数为S3C_MFC_IOCTL_MFC_H264_ENC_INIT,表示使用H.264编码。

X264_frame函数执行编码过程。需要注意的是data参数保存了需要编码的数据,是一个四维的数组,要把它转换成一维数组用于S3C6410编码器输入。另外,编码数据存在空的情况,也就是空帧。这是需要处理的,方法是返回“0”,表示没有输出数据,否则程序运行时会出现段错误。


static int X264_frame(AVCodecContext *ctx,uint8_t *buf,int bufsize,void *data){

……

//空间转换

if(frame){

memcpy(x4》in_buf,frame》data[0],ctx》width*ctx》height);

memcpy(x4》in_buf+ctx》width*ctx》height,frame》data[1],ctx》width*ctx》height/4);

memcpy(x4》in_buf+ctx》width*ctx》height+ctx》width*ctx》height/4,frame》data[2],

ctx》width*ctx》height/4);

}

else

return 0;//空帧,返回

//执行编码过程

ioctl(x4》dev_fd,S3C_MFC_IOCTL_MFC_H264_ENC_EXE,&x4》enc_exe);

//编码数据输出

bufsize = x4》enc_exe.out_encoded_size;

memcpy(buf,x4》out_buf,bufsize);

……

return bufsize;

}

X264_close关闭函数用于编码结束后的资源释放,包括取消空间映射和关闭设备文件。

static av_cold int X264_close(AVCodecContext *avctx){

//取消空间映射

munmap(x4》addr,BUF_SIZE);

//关闭设备文件

close(x4》dev_fd);

return 0;

}

解码函数的实现过程类似于编码函数,包括空间转换、执行解码和解码数据输出。初始化时使用S3C_MFC_IOCTL_MFC_H264_DEC_INIT参数,执行时使用S3C_MFC_IOCTL_MFC_H264_ENC_EXE参数。
运行测试#e#


4 运行测试

s3cx264编解码器添加到FFmpeg后,可以通过以下方式测试:

① 用如下命令编译FFmpeg。

。/configure enablecrosscompile

arch=armv6 cpu=armv6

targetos=linux crossprefix

=/usr/local/arm/4.3.2/bin/

armlinux

② 运行 。/ffmpeg codecs查看可以找到s3cx264编解码器,如图2所示。

FFMPEG视频编解码流程 H.264硬件编解码实现

图2 FFmpeg显示s3cx264编解码器信息

③ 结合USB摄像头测试s3cx264编码。运行 。/ffmpeg s 320x240 r 50 f video4linux2 i /dev/video2 vcodec s3cx264 test.mp4 可以看到FFmpegg正使用s3cx264编码器将USB摄像头采集的数据编码压缩成test.mp4文件。test.mp4能够正常播放显示。


以上测试说明已经成功地将s3cx264硬件视频编码器添加到了FFmpeg中,能够编码视频数据,可以运用到其他使用FFmpeg库的多媒体程序中。


结语

对于多媒体开发来说,编解码时使用FFmpeg多媒体库是一个不错的选择,支持较多的音视频编解码,编程接口简单易用。了解FFmpeg编解码过程,熟悉FFmpeg硬件编解码器添加方法,对多媒体开发,尤其是资源有限的嵌入式多媒体开发有很大帮助。本文通过分析FFmpeg视频编解码过程和三星S3C6410处理器视频硬件编解码方法,在FFmpeg库中成功添加S3C6410硬件编解码器,使FFmpeg库具有H.264视频格式的硬件编解码能力,可运用于游戏设备、监控设备、视频会议设备和数字网络电视等嵌入式系统中,同时也为其他嵌入式设备添加别的视频格式的编解码器到FFmpeg多媒体库提供了参考。

推荐阅读

史海拾趣

Cypress Industries公司的发展小趣事

Cypress在半导体制造领域一直处于技术革新的前沿。公司不断引入新的工艺技术,从早期的0.8微米CMOS技术到后来的0.21微米工艺,不断推动产品性能的提升。这种对技术的持续投入和追求,使Cypress在行业内树立了良好的技术形象,并赢得了客户的广泛认可。

雅特力(Artery)公司的发展小趣事

雅特力深知人才是企业发展的核心力量。因此,公司始终重视人才培养和团队建设。雅特力通过招聘优秀的专业人才、开展内部培训、设立激励机制等方式,打造了一支高素质、高效率的团队。这支团队不仅具备丰富的技术知识和实践经验,还具备高度的创新意识和团队协作能力。正是有了这样一支优秀的团队,雅特力才能够在激烈的市场竞争中脱颖而出,实现持续稳健的发展。

Amphenol(安费诺)公司的发展小趣事

随着公司实力的不断增强,雅特力开始将目光投向全球市场。公司积极参与国际电子展会和论坛,与全球各地的客户和合作伙伴建立了广泛的联系。同时,雅特力还通过设立海外分支机构、开展跨国合作等方式,进一步拓展其全球市场。这些举措不仅提升了雅特力的品牌知名度和影响力,也为公司的长远发展奠定了坚实的基础。

Hirosugi-Keiki公司的发展小趣事

在发展过程中,雅特力积极寻求与其他企业的战略合作,以实现共赢发展。通过与上下游企业建立紧密的合作关系,雅特力不仅确保了供应链的稳定性和高效性,还获得了更多的技术支持和市场资源。同时,雅特力还注重与同行业企业的交流与合作,共同推动电子行业的进步与发展。这些战略合作不仅增强了雅特力的综合实力和市场竞争力,也为整个电子行业的繁荣做出了积极贡献。

综上所述,雅特力公司通过技术创新、全球市场布局、企业社会责任、人才培养和战略合作等多个方面的努力,实现了从初创企业到行业领军者的华丽转身。这些故事不仅展现了雅特力的发展历程和成就,也揭示了其在电子行业中的重要地位和影响力。

HN Electronic Components GmbH & Co Kg公司的发展小趣事

背景:HN Electronics成立于20世纪90年代初,正值全球电子市场快速崛起的时期。公司由一群在半导体领域拥有丰富经验的工程师创立,他们看到了智能手机和消费电子产品市场的巨大潜力。

发展:初期,HN Electronics专注于研发高性能的电源管理芯片,通过技术创新解决了当时市场上存在的能效低、发热量大等问题。公司迅速获得了业界的认可,并与几家知名手机制造商建立了合作关系。

关键事件:1995年,HN Electronics成功推出其首款自主研发的电源管理芯片,该产品迅速成为市场热销产品,为公司赢得了第一桶金。

Asian Best Components Co Ltd公司的发展小趣事

随着国内市场的日益饱和,Asian Best Components Co Ltd开始将目光投向了广阔的国际市场。公司积极参加国际电子展会,与全球各地的客户建立了广泛的联系。通过深入了解不同市场的需求和特点,Asian Best不断调整产品策略,优化产品设计,成功打入多个国际市场。同时,公司还加强与国际同行的合作,共同推动电子行业的发展。

问答坊 | AI 解惑

送给学习VHDL的兄弟们

一本VHDL的书   感觉不错…

查看全部问答>

如何理解参数“全功率带宽”?

如何定义, 又如何使用“全功率带宽”?…

查看全部问答>

模拟集成电路三本圣经(书及答案)

模拟三本经典著作及答案,另外附送拉扎维的射频微电子(中文版) 这么经典的东西不用详细介绍了吧 拉扎维的《Design of Analog CMOS Integrated Circuits》书及答案 Gray和Meyer等人的《Analysis and Design of analog IC》书及答案 P.E.Allen ...…

查看全部问答>

用最简单的东西做你想要的-------功放

这里介绍一个设计小巧、线路简单但性能不错的三管音频放大器。其电路见附图。也许你在一些袖珍晶体管收音机可以看到一些与此类似的电路。 原理分析:   电路如图所示,输入极(9014)的基极工作电压等于两输出极三极管的中点电压,一般为电源电 ...…

查看全部问答>

LPC1114之SysTick计算

LPC1114的SysTick延时时间的计算方式如下图 我用这个延时做了一个LED闪烁的例子,现在共享给大家。下面是源代码,附件里有工程文件。 #include \"LPC11xx.h\"             & ...…

查看全部问答>

progisp V1.67安装不上??

我的电脑(台式)上装progisp167的USB驱动装不上,为什么? 我是从www.zhifengsoft.com上下载的,解压后不管是直接右击usbprog.inf安装还是插设备在向导里安装,都装不上。可是,同事的笔记本就能装! www.zhifengsoft.com的网站上也没有说怎么回 ...…

查看全部问答>

关于rs232表达错误,再问一下

已知硬件串口是好的,如何用数字式万用表测试串口是否有信号输出?…

查看全部问答>

我想买一台2440的GPS用来改装其他用途,要使用到几个IO和串口。大家觉得行得通吗?

还有,是不是把JTAG接口找出来,使用并口将win ce的bootLoader写入,然后用USB写入win ce系统就可以了。…

查看全部问答>

寻找商业投资战略合作伙伴

寻找商业投资战略合作伙伴,目前有大量国际资本操作 ,已经在国内。拟项目:拟在广西南宁市建立国内外汇市场投资机构。模式是以核心固定交易模式技术与交通银进行合作,运用核心技术和私募国际资本独立进行全球金融-外汇市场投资,也涉及黄金 股指 ...…

查看全部问答>