利用FFMPEG与X264实现帧内预测模式的隐写算法(2)

本文详细介绍了如何在FFMPEG与X264中实现帧内预测模式的隐写算法。通过修改FFMPEG的AVCodecContext和x264_t结构体,将隐写信息嵌入编码过程,并在编码代价计算中提取隐藏信息。主要涉及函数包括x264_macroblock_analyse、x264_intra_rd等,以及编码代价的提取和IPM的修改。
摘要由CSDN通过智能技术生成

前言

由于这个领域我以后不打算深入,而且课题也只是交个毕设而已,所以代码中有些地方图方便直接开了全局变量,这样破坏了ffmpeg原有的封装结构。这门这代码只作学习和理论实践使用,千万别拿这个直接去投入开源使用,如果想跟着我改的朋友也请给原ffmpeg代码留好备份。所有代码会等毕设结题后公开。


正文

上一篇文章中我们介绍了H264相关的知识和基于帧内预测模式的算法。这一片我们会具体在ffmpeg与x264中实现上一篇文章中2.4中所描述的算法


1. ffmpeg与x264对于一帧的处理

这个ffmpeg与x264的更多内容请参考雷霄骅的博客,我们这里直接讲需要了解的结构体和函数。

1.1 ffmpeg中几个结构体的概念

AVFormatContext:记录多媒体文件格式的相关信息,一般来说对应一个多媒体文件的信息。

AVInputFormat/AVOutputFormat:描述一种多媒体格式的信息,一般来说对应一种多媒体文件的信息,如MP4,FLV,MP3等。

AVStream:多媒体文件中的一段多媒体流。比如我们一般的视频文件里面有视频流,音频流,甚至是字幕流等等,每个流都会对应一个AVCodecContext。

AVCodecContext:这是个比较重要的结构体,比较抽象,大致记录的编解码器的一些特征,比如有几个声道,视频宽高等等。还会存一部分需要编解码的数据。可以看做视频画面与编解码器中间的一个过渡用的结构体。每个AVCodecContext都对应一个AVCodec

AVCodec:实实在在的被实例化编解码器。其中包含编解码类别,需要编解码的信息,编解码所用的函数指针等等。

AVFrame:视频中一帧画面,包括画面的yuv信息,以及对应需要播放的音频或字母等,用于直接播放的数据结构。

AVPacket:一个基本的数据包,可以解码成AVFrame。一个AVFrame可能由多个AVPacket解码组成,但是一个AVPacket最多包含一个AVFrame


1.2 部分函数调用结构

int avcodec_encode_video2(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr):

将一个AVFrame编码成AVPacket,优先装进AVCodecContext。如果AVCodecContet装不下了,会弹出一个AVPacketd到avpkt,并将got_packet_ptr置1。这是最外层的编码API函数。


int AVCodecContext*->codec->encode2(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr)

位于avcodec_encode_video2中,通过 ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr);调用

这个是AVCodecContext中对应Codec的编码函数,encode2是一个函数指针,指向编码函数。

实际上就是将视频信息交给编码器后调用编码器的编码函数。


static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, int *got_packet)

这个其实就是AVCodecContext*->codec->encode2所指向的函数(编码器为H264编码器)

可以发现这个函数位于libx264.c中,尚且还是在ffmpeg中的函数,但是在libx264.c中include的头文件中已经发现了x264的头文件,也就是说这里就是ffmpeg调用x264的分界点了。

int  x264_encoder_encode( x264_t *h, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_in, x264_picture_t *pic_out )

x264的最外层API,用于将一帧画面编码成H264格式的一帧,这个函数很长。我们只直在里面找到x264_slices_write()这个函数

x264_slices_write():一边编码一边写入帧的数据(与之对应的还有些文件头和写文件尾的函数)

x264_stack_align( x264_slice_write, h ):这是x264_slices_write()中的一句话,可以理解成x264_slice_write(h)。函数原型是static intptr_t x264_slice_write( x264_t *h ),这样写是为了字节对齐。这个函数写一帧的数据。在里面我们能找到x264_macroblock_analyse( h );这句话

void x264_macroblock_analyse( x264_t *h ) : 这个函数用于分析一个16*16的宏块。我会详细讲一讲这个函数。

1.3 void x264_macroblock_analyse( x264_t *h )


我们会发现其中有个x264_mb_analysis_t类型,这个类型用于存储分析结果,对于一个宏块我们需要分析的内容有,该16*16宏块如何划分,划分之后每个更小的宏块用什么IPM预测等等信息。

我们能发现其中有一行Do the analysis的注释。

在这行注释上面,主要设置了这行的量化系数。我们主要分析这行下面的一部分。


通过VS将代码折叠我们能更好地观察它的结构,这里首先判断这宏块属于哪种帧,对于每种帧,用具体不同的策略来分析,将分析结果保存在analysis中,最后分析完将分析结果反馈给出来决定这个帧之后如何编码

所以如果我们要篡改其中的IPM,就需要篡改它的analysis,使其最后按照我们指定的IPM去进行编码,这里我们继续展开SLICE_TYPE_I的情况

    if( h->sh.i_type == SLICE_TYPE_I )
    {
	intra_analysis:
        if( analysis.i_mbrd )
            x264_mb_init_fenc_cache( h, analysis.i_mbrd >= 2 );
		
        if( analysis.i_mbrd )
            x264_intra_rd( h, &analysis, COST_MAX );

        i_cost = analysis.i_satd_i16x16;
        h->mb.i_type = I_16x16;
        COPY2_IF_LT( i_cost, analysis.i_satd_i4x4, h->mb.i_type, I_4x4 );
        COPY2_IF_LT( i_cost, analysis.i_satd_i8x8, h->mb.i_type, I_8x8 );
        if( analysis.i_satd_pcm < i_cost )
            h->mb.i_type = I_PCM;
	else if (analysis.i_mbrd >= 2)
	{
		x264_intra_rd_refine(h, &analysis);
	}
    }


注:COPY2_IF_LT是个宏定义,相当于Copy if little,ji如果新的划分方法更优,就copy成新的方法。

其中x264_mb_analyse_intra(), x264_intra_rd(), x264_intra_rd_refine(),都是用来计算不同划分方式的编码代价的。其中I_16x16表示该宏块不划分,I_4x4表示宏块划分成16个4*4宏块,I_8x8表示划分成4个8*8宏块。


当SLICE_TYPE_I这个分支执行完&#x

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值