Decodificación FFMPEG de H264 a YUV
1. Descarga de código fuente
Boge ha abierto esta parte del código en github, descárguelo en github. Enlace de
descarga: https://github.com/wangfengbo2020/ffmped_decode_h264_to_yuv
(Cometí un error aquí, ffmpeg se escribió como ffmped, gracias por su identificación y correcciones posteriores)
Teniendo en cuenta la pequeña cantidad de código fuente, todavía lo
publico aquí: videodecoder.c
#include <stdio.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include "videodecoder.h"
struct AVCodecContext *pAVCodecCtxDecoder = NULL;
struct AVCodec *pAVCodecDecoder;
struct AVPacket mAVPacketDecoder;
struct AVFrame *pAVFrameDecoder = NULL;
struct SwsContext* pImageConvertCtxDecoder = NULL;
struct AVFrame *pFrameYUVDecoder = NULL;
int ffmpeg_init_video_decoder(AVCodecParameters *codecParameters)
{
if (!codecParameters) {
printf("Source codec context is NULL."); //printf需替换为printf
return -1;
}
ffmpeg_release_video_decoder();
avcodec_register_all();
pAVCodecDecoder = avcodec_find_decoder(codecParameters->codec_id);
if (!pAVCodecDecoder) {
printf("Can not find codec:%d\n", codecParameters->codec_id);
return -2;
}
pAVCodecCtxDecoder = avcodec_alloc_context3(pAVCodecDecoder);
if (!pAVCodecCtxDecoder) {
printf("Failed to alloc codec context.");
ffmpeg_release_video_decoder();
return -3;
}
if (avcodec_parameters_to_context(pAVCodecCtxDecoder, codecParameters) < 0) {
printf("Failed to copy avcodec parameters to codec context.");
ffmpeg_release_video_decoder();
return -3;
}
if (avcodec_open2(pAVCodecCtxDecoder, pAVCodecDecoder, NULL) < 0){
printf("Failed to open h264 decoder");
ffmpeg_release_video_decoder();
return -4;
}
av_init_packet(&mAVPacketDecoder);
pAVFrameDecoder = av_frame_alloc();
pFrameYUVDecoder = av_frame_alloc();
return 0;
}
int ffmpeg_init_h264_decoder()
{
avcodec_register_all();
AVCodec *pAVCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!pAVCodec){
printf("can not find H264 codec\n");
return -1;
}
AVCodecContext *pAVCodecCtx = avcodec_alloc_context3(pAVCodec);
if (pAVCodecCtx == NULL) {
printf("Could not alloc video context!\n");
return -2;
}
AVCodecParameters *codecParameters = avcodec_parameters_alloc();
if (avcodec_parameters_from_context(codecParameters, pAVCodecCtx) < 0) {
printf("Failed to copy avcodec parameters from codec context.");
avcodec_parameters_free(&codecParameters);
avcodec_free_context(&pAVCodecCtx);
return -3;
}
int ret = ffmpeg_init_video_decoder(codecParameters);
avcodec_parameters_free(&codecParameters);
avcodec_free_context(&pAVCodecCtx);
return ret;
}
int ffmpeg_release_video_decoder() {
if (pAVCodecCtxDecoder != NULL) {
avcodec_free_context(&pAVCodecCtxDecoder);
pAVCodecCtxDecoder = NULL;
}
if (pAVFrameDecoder != NULL) {
av_packet_unref(&mAVPacketDecoder);
av_free(pAVFrameDecoder);
pAVFrameDecoder = NULL;
}
if (pFrameYUVDecoder) {
av_frame_unref(pFrameYUVDecoder);
av_free(pFrameYUVDecoder);
pFrameYUVDecoder = NULL;
}
if (pImageConvertCtxDecoder) {
sws_freeContext(pImageConvertCtxDecoder);
}
av_packet_unref(&mAVPacketDecoder);
return 0;
}
int ffmpeg_decode_h264(unsigned char *inbuf, int inbufSize, int *framePara, unsigned char **outRGBBuf, unsigned char *outYUVBuf)
{
if (!pAVCodecCtxDecoder || !pAVFrameDecoder || !inbuf || inbufSize<=0 || !framePara || (!outRGBBuf && !outYUVBuf)) {
return -1;
}
av_frame_unref(pAVFrameDecoder);
av_frame_unref(pFrameYUVDecoder);
framePara[0] = framePara[1] = 0;
mAVPacketDecoder.data = inbuf;
mAVPacketDecoder.size = inbufSize;
int ret = avcodec_send_packet(pAVCodecCtxDecoder, &mAVPacketDecoder);
if (ret == 0) {
ret = avcodec_receive_frame(pAVCodecCtxDecoder, pAVFrameDecoder);
if (ret == 0) {
framePara[0] = pAVFrameDecoder->width;
framePara[1] = pAVFrameDecoder->height;
if (outYUVBuf) {
//*outYUVBuf = (unsigned char *)pAVFrameDecoder->data;
memcpy(outYUVBuf, pAVFrameDecoder->data, sizeof(pAVFrameDecoder->data));
framePara[2] = pAVFrameDecoder->linesize[0];
framePara[3] = pAVFrameDecoder->linesize[1];
framePara[4] = pAVFrameDecoder->linesize[2];
} else if (outRGBBuf) {
pFrameYUVDecoder->data[0] = outRGBBuf;
pFrameYUVDecoder->data[1] = NULL;
pFrameYUVDecoder->data[2] = NULL;
pFrameYUVDecoder->data[3] = NULL;
int linesize[4] = {
pAVCodecCtxDecoder->width * 3, pAVCodecCtxDecoder->height * 3, 0, 0 };
pImageConvertCtxDecoder = sws_getContext(pAVCodecCtxDecoder->width, pAVCodecCtxDecoder->height,
AV_PIX_FMT_YUV420P,
pAVCodecCtxDecoder->width,
pAVCodecCtxDecoder->height,
AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR,
NULL, NULL, NULL);
sws_scale(pImageConvertCtxDecoder, (const uint8_t* const *) pAVFrameDecoder->data, pAVFrameDecoder->linesize, 0, pAVCodecCtxDecoder->height, pFrameYUVDecoder->data, linesize);
sws_freeContext(pImageConvertCtxDecoder);
}
return 1;
} else if (ret == AVERROR(EAGAIN)) {
return 0;
} else {
return -1;
}
}
return 0;
}
videodecoder.h
#include <libavcodec/avcodec.h>
/**
视频流解码器初始化
@param ctx 解码参数结构体AVCodecParameters
@see ffmpeg_init_video_decoder,此为解码H264视频流
@return 初始化成功返回0,否则<0
*/
int ffmpeg_init_video_decoder(AVCodecParameters *ctx);
/**
H264视频流解码器初始化
@return 初始化成功返回0,否则<0
*/
int ffmpeg_init_h264_decoder(void);
/**
释放解码器
@return 初始化成功返回0,否则<0
*/
int ffmpeg_release_video_decoder(void);
//return 0:暂未收到解码数据,-1:解码失败,1:解码成功
/**
解码视频流数据
@param inbuf 视频裸流数据
@param inbufSize 视频裸流数据大小
@param framePara 接收帧参数数组:{width,height,linesize1,linesiz2,linesize3}
@param outRGBBuf 输出RGB数据(若已申请内存)
@param outYUVBuf 输出YUV数据(若已申请内存)
@return 成功返回解码数据帧大小,否则<=0
*/
int ffmpeg_decode_h264(unsigned char * inbuf, int inbufSize, int *framePara, unsigned char **outRGBBuf, unsigned char *outYUVBuf);
decode264.c
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include "videodecoder.h"
/* H264 source data, I frame */
#define H264_TEST_FILE "./Iframe4test.h264"
/* Target yuv data, yuv 420 */
#define TARGET_YUV_FILE "./target420.yuv"
int g_paramBuf[8] = {
0};
char g_inputData[1*1024*1024] = {
0};
char g_ouputData[4*1024*1024] = {
0};
int read_raw_data(char *path, char *data, int maxBuf)
{
FILE *fp = NULL;
int len = 0;
fp = fopen(path, "r");
if (NULL != fp)
{
memset(data, 0x0, maxBuf);
len = fread(data, 1, maxBuf, fp);
fclose(fp);
}
return len;
}
int write_raw_data_to_file(char *path, char *data, int dataLen)
{
FILE *fp = NULL;
int len = 0;
int left = dataLen;
int cnts = 0;
if (NULL == data || 0 == dataLen)
{
printf("para error return\n");
return 0;
}
if (access(path, F_OK) == 0)
{
remove(path);
}
fp = fopen(path, "ab+");
if (NULL != fp)
{
while(left)
{
if (left >= 4096)
{
len += fwrite((void *)data+cnts*4096, 1, 4096, fp);
cnts++;
left -= 4096;
sync();
}
else
{
len += fwrite(data+cnts*4096, 1, left, fp);
break;
}
}
fclose(fp);
}
return len;
}
int main()
{
int dataLen = 0;
int index = 150;
unsigned char *yuvData[10] = {
NULL};
char *rgbData = NULL;
//Step 1: init
int ret = ffmpeg_init_h264_decoder();
printf("Step 1 finished, ret = %d\n", ret);
//Step 2: get YUV data
dataLen = read_raw_data(H264_TEST_FILE, g_inputData, sizeof(g_inputData));
if (0 == dataLen)
{
printf("Step 2 failed, Read YUV data error\n");
return;
}
printf("Step 2 finished, readDatalen = %d\n", dataLen);
//Step 3: decode
ret = ffmpeg_decode_h264(g_inputData, dataLen, g_paramBuf, NULL, yuvData);
printf("Step 3 finished, decode ret = %d width = %d height = %d\n", ret, g_paramBuf[0], g_paramBuf[1]);
//Put the data to the target file according to the format 420.
memcpy(g_ouputData, (char *)(yuvData[0]), g_paramBuf[0]*g_paramBuf[1]);
memcpy(g_ouputData + g_paramBuf[0]*g_paramBuf[1] , (char *)(yuvData[1]), (g_paramBuf[0]*g_paramBuf[1])/4);
memcpy(g_ouputData + ((g_paramBuf[0]*g_paramBuf[1]))*5/4 , (char *)(yuvData[2]), (g_paramBuf[0]*g_paramBuf[1])/4);
//Step 4: write yuv data to file
ret = write_raw_data_to_file(TARGET_YUV_FILE, g_ouputData, g_paramBuf[0]*g_paramBuf[1]*3/2);
printf("Step 4 write finish len = %d\n", ret);
return 0;
}
build.sh
ffmpeginstall=/home/eric/avtest/ffmpeg_install
x264install=/home/eric/avtest/x264_install
x265install=/home/eric/avtest/x265_install
ffmpeglib=$ffmpeginstall/lib
ffmpeginc=$ffmpeginstall/include
x264lib=$x264install/lib
x265lib=$x265install/lib
gcc -o decode264 decodeH264.c videodecoder.c -L$ffmpeglib -L$x264lib -L$x265lib -I$ffmpeginc -lavformat -lavcodec -lavutil -lswscale -lswresample -lx264 -lx265 -lm -lpthread -ldl -lstdc++
2. Análisis del código fuente
2.1 Introducción al proyecto
- build.sh: compilar script, sin parámetros
- videodecoder.h: API de transcodificación ffmpeg
- videodecoder.c: Implementación de la API de transcodificación ffmpeg
- decodeH264.c: demostración
2.2 Operación del proyecto
(1) Abra y modifique el archivo build.sh.
Modifique la ruta anterior. Consulte el blog anterior de Boge para obtener información sobre la compilación de los archivos anteriores.
(2) Agregar el archivo de destino (marco H264 I)
puede colocar el marco H264 I probado en el directorio raíz del proyecto y nombrarlo como se indicó anteriormente.
(3) Compilar el proyecto
sh build.sh
Después de que la compilación sea normal, genere decode264 en el directorio raíz
(4) Ejecute el archivo ejecutable
./decode264
Si se muestra el proceso anterior, la operación es normal y luego se genera el archivo target420.yuv bajo la grabación de seguimiento.
(5) Archivos generados por la prueba
ffplay -f rawvideo -video_size 1920x1080 target420.yuv