使用FFMPEG SDK解码流数据

【转自】http://blog.163.com/cho_ku2000/blog/static/913118220111120111030371/

                http://blog.163.com/cho_ku2000/blog/#m=0&t=3&c=ffmpeg

本文以H264视频流为例,讲解解码流数据的步骤。

为突出重点,本文只专注于讨论解码视频流数据,不涉及其它(如开发环境的配置等)。如果您需要这方面的信息,请和我联系。


准备变量

定义AVCodecContext。如果您使用类,可以定义成类成员。我这里定义成全局变量。

static AVCodecContext * g_pCodecCtx = NULL;

定义一个AVFrame,AVFrame描述一个多媒体帧。解码后的数据将被放在其中。

static AVFrame * g_pavfFrame = NULL;


初始化解码器

现在开始初始化您的解码器。我把整个初始化过程包在了一个函数里,除非您有更好的主意,我建议您也这么做。函数长得象这样:

BOOL H264_Init()

{



}

初始化libavcodec,MMPEG要求,这个函数一定要第一个被调用:

avcodec_init();

挂上所有的codec。也许只挂一个H264的codec就行,我没试过:

av_register_all();

得到H264的解码器:

AVCodec * pCodec = avcodec_find_decoder(CODEC_ID_H264);

创建一个AVCodecContext,并用默认值初始化:

g_pCodecCtx = avcodec_alloc_context();

更改g_pCodecCtx的一些成员变量的值,您应该从解码方得到这些变量值:

g_pCodecCtx->time_base.num = 1; //这两行:一秒钟25帧

g_pCodecCtx->time_base.den = 25;

g_pCodecCtx->bit_rate = 0; //初始化为0

g_pCodecCtx->frame_number = 1; //每包一个视频帧

g_pCodecCtx->codec_type = CODEC_TYPE_VIDEO;

g_pCodecCtx->width = 704; //这两行:视频的宽度和高度

g_pCodecCtx->height = 576;

打开codec。如果打开成功的话,分配AVFrame:

if(avcodec_open(g_pCodecCtx, pCodec) >= 0)

{

g_pavfFrame = avcodec_alloc_frame();// Allocate video frame

}

列出完整的初始化解码库的代码:

image


解码

如果您只要求解成YUV 420I数据,只需一次调用就可以了:

avcodec_decode_video(g_pCodecCtx, g_pavfFrame, (int *)&nGot, (unsigned __int8 *)pSrcData, dwDataLen);

这里,nGot用来返回解码成功与否,avcodec_decode_video调用完成后,如果nGot不等于0,则表示解码成功,否则未解出视频帧。

pSrcData是待解的H264编码的一段数据流,dwDataLen表示该段数据流的长度,单位是byte。

解码后的视频帧(YUV数据)被存入g_pavfFrame,g_pavfFrame->data[0]、g_pavfFrame->data[1]、g_pavfFrame->data[2]即是YUV数据。下面的示例代码把YUV数据压在了一块内存里,排列方式为:

YY

YY

U

V

该函数有返回值:如果解码成功,则返回本次解码使用的码流字节数,否则返回0。为简单起见,我这里假设pSrcData只包含一个视频帧。

同样,出于模块化的要求和代码维护的方便,我把解码动作也包在了一个函数里:

BOOL H264_Decode(const PBYTE pSrcData, const DWORD dwDataLen, PBYTE pDeData, int * pnWidth, int * pnHeight)

pSrcData – 待解码数据

dwDataLen – 待解码数据字节数

pDeData – 用来返回解码后的YUV数据

pnWidth, pnHeight – 用来返回视频的长度和宽度

下面列出完整的代码:

image


释放解码器

以上其实已经完成了本文的任务,但从负责任的角度,要善始善终嘛。

释放的过程没什么好说的,一看就明白。同样,我也把它们包在了一个函数里:

image

(抱歉的很,文章本来是用Word写的,代码块是一个个文本框,但贴到这里却变成了图片。)


 下面是如果解码以及将 YV12 数据转换成
32 位 ARGB 数据的代码
#include "avcodec.h"
 #include "h264decoder.h";

 typedef unsigned char byte_t;
 typedef unsigned int uint_t;

struct AVCodec *fCodec = NULL; // Codec
struct AVCodecContext *fCodecContext = NULL; // Codec Context
struct AVFrame *fVideoFrame = NULL; // Frame

int fDisplayWidth = 0;
int fDisplayHeight = 0;
int *fColorTable = NULL;

int avcodec_decode_video(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
const uint8_t *buf, int buf_size)
{
AVPacket avpkt;
av_init_packet(&avpkt);
avpkt.data = buf;
avpkt.size = buf_size;
// HACK for CorePNG to decode as normal PNG by default
avpkt.flags = AV_PKT_FLAG_KEY;
return avcodec_decode_video2(avctx, picture, got_picture_ptr, &avpkt);
}

#define RGB_V(v) ((v < 0) ? 0 : ((v > 255) ? 255 : v))

void DeleteYUVTable()
{
av_free(fColorTable);
}

void CreateYUVTable()
{
int i;
int u, v;
int *u_b_tab = NULL;
int *u_g_tab = NULL;
int *v_g_tab = NULL;
int *v_r_tab = NULL;

fColorTable = (int *)av_malloc(4 * 256 * sizeof(int));
u_b_tab = &fColorTable[0 * 256];
u_g_tab = &fColorTable[1 * 256];
v_g_tab = &fColorTable[2 * 256];
v_r_tab = &fColorTable[3 * 256];

for (i = 0; i < 256; i++) {
u = v = (i - 128);
u_b_tab[i] = (int) ( 1.772 * u);
u_g_tab[i] = (int) ( 0.34414 * u);
v_g_tab[i] = (int) ( 0.71414 * v);
v_r_tab[i] = (int) ( 1.402 * v);
}
}

/** YV12 To RGB888 */
void DisplayYUV_32(uint_t *displayBuffer, int videoWidth, int videoHeight, int outPitch)
{
int *u_b_tab = &fColorTable[0 * 256];
int *u_g_tab = &fColorTable[1 * 256];
int *v_g_tab = &fColorTable[2 * 256];
int *v_r_tab = &fColorTable[3 * 256];

// YV12: [Y:MxN] [U:M/2xN/2] [V:M/2xN/2]
byte_t* y = fVideoFrame->data[0];
byte_t* u = fVideoFrame->data[1];
byte_t* v = fVideoFrame->data[2];

int src_ystride = fVideoFrame->linesize[0];
int src_uvstride = fVideoFrame->linesize[1];

int i, line;
int r, g, b;

int ub, ug, vg, vr;

int width = videoWidth;
int height = videoHeight;

// 剪切边框
if (width > fDisplayWidth) {
width = fDisplayWidth;
y += (videoWidth - fDisplayWidth) / 2;
u += (videoWidth - fDisplayWidth) / 4;
v += (videoWidth - fDisplayWidth) / 4;
}

if (height > fDisplayHeight) {
height = fDisplayHeight;
}

for (line = 0; line < height; line++) {
byte_t* yoff = y + line * src_ystride;
byte_t* uoff = u + (line / 2) * src_uvstride;
byte_t* voff = v + (line / 2) * src_uvstride;
//uint_t* buffer = displayBuffer + (height - line - 1) * outPitch;
uint_t* buffer = displayBuffer + line * outPitch;

for (i = 0; i < width; i++) {
ub = u_b_tab[*uoff];
ug = u_g_tab[*uoff];
vg = v_g_tab[*voff];
vr = v_r_tab[*voff];

b = RGB_V(*yoff + ub);
g = RGB_V(*yoff - ug - vg);
r = RGB_V(*yoff + vr);

*buffer = 0xff000000 | b << 16 | g << 8 | r;

buffer++;
yoff ++;

if ((i % 2) == 1) {
uoff++;
voff++;
}
}
}
}

int avc_decode_init(int width, int height)
{
if (fCodecContext != NULL) {
return 0;
}
avcodec_init();
avcodec_register_all();
fCodec = avcodec_find_decoder(CODEC_ID_H264);

fDisplayWidth = width;
fDisplayHeight = height;

CreateYUVTable();

fCodecContext = avcodec_alloc_context();
avcodec_open(fCodecContext, fCodec);
fVideoFrame = avcodec_alloc_frame();

return 1;
}

int avc_decode_release()
{
if (fCodecContext) {
avcodec_close(fCodecContext);
free(fCodecContext->priv_data);
free(fCodecContext);
fCodecContext = NULL;
}

if (fVideoFrame) {
free(fVideoFrame);
fVideoFrame = NULL;
}

DeleteYUVTable();
return 1;
}

int avc_decode(char* buf, int nalLen, char* out)
{
byte_t* data = (byte_t*)buf;
int frameSize = 0;

int ret = avcodec_decode_video(fCodecContext, fVideoFrame, &frameSize, data, nalLen);
if (ret <= 0) {
return ret;
}

int width = fCodecContext->width;
int height = fCodecContext->height;
DisplayYUV_32((uint32_t*)out, width, height, fDisplayWidth);
return ret;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值