- 简介
如下图所示,解码就是将H264、AAC等数据解析为原始YUV、PCM数据。
2:ffmpeg流程分析
1):音频解码
2):视频解码
3:主要函数
(1):av_parser_parse2:解析获得一个packet
函数原型:
/**
* Parse a packet.
*
* @param s parser context.
* @param avctx codec context.
* @param poutbuf set to pointer to parsed buffer or NULL if not yet finished.
* @param poutbuf_size set to size of parsed buffer or zero if not yet finished.
* @param buf input buffer.
* @param buf_size buffer size in bytes without the padding. I.e. the full buffer
size is assumed to be buf_size + AV_INPUT_BUFFER_PADDING_SIZE.
To signal EOF, this should be 0 (so that the last frame
can be output).
* @param pts input presentation timestamp.
* @param dts input decoding timestamp.
* @param pos input byte position in stream.
* @return the number of bytes of the input bitstream used.
*/
int av_parser_parse2(AVCodecParserContext *s,
AVCodecContext *avctx,
uint8_t **poutbuf, int *poutbuf_size,
const uint8_t *buf, int buf_size,
int64_t pts, int64_t dts,
int64_t pos);
参数 poutbuf:这里指向解码后数据存放的位置,通常为AVPacket下的data即可;
参数poutbuf_size:解码数据的数据大小,相对为AVPacket下的size即可;
参数buf:读取到的待解析的数据;
参数buf_size:待解析数据大小。
4:代码实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C"
{
#include <libavutil/frame.h>
#include <libavutil/mem.h>
#include <libavcodec/avcodec.h>
}
#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096
using namespace std;
static void print_sample_format(const AVFrame *frame)
{
printf("ar-samplerate: %uHz\n", frame->sample_rate);
printf("ac-channel: %u\n", frame->channels);
printf("f-format: %u\n", frame->format);// 格式需要注意,实际存储到本地文件时已经改成交错模式
}
static void decode(AVCodecContext *ctx,AVPacket *pkt,AVFrame *frame,FILE *outfile)
{
int i, ch;
int ret, data_size;
//7:解析出的数据包发送给解码器
ret = avcodec_send_packet(ctx,pkt);
if(ret == AVERROR(EAGAIN))
{
printf("Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
}else if(ret < 0)
{
printf("Error submitting the packet to the decoder, err");
return;
}
while(ret >= 0)
{
//8:接受解码后的数据帧
ret = avcodec_receive_frame(ctx,frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0)
{
printf("Error during decoding\n");
exit(1);
}
//9:获取单个sample占用字节
data_size = av_get_bytes_per_sample(ctx->sample_fmt);
if(data_size < 0)
{
printf("failedto caculate data size\n");
exit(1);
}
static int s_print_format = 0;
if(s_print_format == 0)
{
s_print_format = 1;
print_sample_format(frame);
}
for (i = 0; i < frame->nb_samples; i++)
{
for (ch = 0; ch < ctx->channels; ch++) // 交错的方式写入, 大部分float的格式输出
//10:文件写入
fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
}
}
}
int main()
{
FILE *infile = NULL;
FILE *outfile = NULL;
int ret;
uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data = NULL;
size_t data_size = 0;
AVCodecContext *codec_ctx;
AVCodec *codec;
AVCodecParserContext *parse;
enum AVCodecID audio_codec_id = AV_CODEC_ID_AAC;
//1:查找指定AAC音频解码器
codec = avcodec_find_decoder(audio_codec_id);
if(codec == NULL)
{
return -1;
}
//2:初始化解码器ID对应裸流解析器
parse = av_parser_init(codec->id);
if(!parse)
{
return -1;
}
//3:解码器上下文分配
codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx) {
return -1;
}
//4:打开解码器和关联解码器上下文
if(avcodec_open2(codec_ctx,codec,NULL)<0)
{
return -1;
}
infile = fopen("believe.aac", "rb");
if (!infile) {
printf("fopen strInFile");
return -1;
}
// 打开输出文件
outfile = fopen("believe.pcm", "wb");
if (!outfile) {
printf("fopen");
return -1;
}
//5:读取文件解码
data = inbuf;
data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, infile);
AVFrame *decodec_frame=NULL;
AVPacket *pkt;
pkt = av_packet_alloc();
while(data_size > 0)
{
if(!decodec_frame)
{
decodec_frame = av_frame_alloc();
if(!decodec_frame)
{
printf("could not allocate frame");
return -1;
}
}
//6:解析出一个数据包
ret = av_parser_parse2(parse,codec_ctx,&pkt->data,&pkt->size,data,data_size,AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);
if(ret < 0)
{
printf("error while parsing\n");
return -1;
}
data +=ret;
data_size -= ret;
if(pkt->size)
{
decode(codec_ctx,pkt,decodec_frame,outfile);
}
if(data_size < AUDIO_REFILL_THRESH)
{
//将还未读取剩余的数据重新拷贝到inbuf起始位置
memmove(inbuf,data,data_size);
data = inbuf;
//Data_size是之前还剩的数据长度,从起始位置到data_size位置是上一次已经读过的
int len = fread(data+data_size,1,AUDIO_INBUF_SIZE - data_size, infile) ;
if (len > 0)
data_size += len;
}
}
pkt->data = NULL;
pkt->size = 0;
decode(codec_ctx, pkt, decodec_frame, outfile);
fclose(outfile);
fclose(infile);
avcodec_free_context(&codec_ctx);
//av_parser_close(parser);
// av_frame_free(&decoded_frame);
av_packet_free(&pkt);
printf("main finish, please enter Enter and exit\n");
return 0;
}
#include "decode_vedio.h"
#define VIDEO_INBUF_SIZE 20480
#define VIDEO_REFILL_THRESH 4096
decode_vedio::decode_vedio()
{
}
int decode_vedio::mainEntrance()
{
AVCodec *codec = NULL;
AVCodecContext *ctx = NULL;
AVCodecParserContext *parse = NULL;
AVPacket *pkt = NULL;
AVFrame *frame = NULL;
int ret = 0;
FILE *p_inFile =NULL;
FILE *p_outFile =NULL;
uint8_t inbuf[VIDEO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data = NULL;
size_t data_size = 0;
p_inFile = fopen("source_766x322_10s.h264","rb");
p_outFile = fopen("source_766x322_10s.yuv","wb");
if(p_inFile == NULL || p_outFile == NULL)
{
fprintf(stderr,"p_inFile\p_outFile open failed\n");
return -1;
}
enum AVCodecID decode_vedio_id = AV_CODEC_ID_H264;
pkt = av_packet_alloc();
if(pkt == NULL)
{
fprintf(stderr,"av_packet_alloc failed\n");
return -1;
}
//1:查找解码器
codec = avcodec_find_decoder(decode_vedio_id);
if(codec == NULL)
{
fprintf(stderr,"find decoder failed\n");
return -1;
}
//2:解析器初始化
parse = av_parser_init(codec->id);
if(parse ==NULL)
{
fprintf(stderr,"av_parser_init failed\n");
return -1;
}
//3:分配解码器上下文
ctx = avcodec_alloc_context3(codec);
if(ctx ==NULL)
{
fprintf(stderr,"avcodec_alloc_context3 failed\n");
return -1;
}
//4:解码器关联上下文
ret = avcodec_open2(ctx,codec,NULL);
if(ret < 0)
{
fprintf(stderr,"avcodec_open2 failed\n");
return -1;
}
//5:读取文件
//data = inbuf;
//data_size = fread(inbuf,1,VIDEO_INBUF_SIZE,p_inFile);
data = inbuf;
data_size = fread(inbuf, 1, VIDEO_INBUF_SIZE, p_inFile);
while(data_size > 0)
{
if(frame == NULL)
{
frame = av_frame_alloc();
if(frame == NULL)
{
fprintf(stderr,"av_frame_alloc failed\n");
return -1;
}
}
//6:解析出一个完整数据包
ret = av_parser_parse2(parse,ctx,&pkt->data,&pkt->size,data,data_size,AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);
if (ret < 0)
{
fprintf(stderr,"av_parser_parse2 failed\n");
return -1;
}
data += ret;
data_size -= ret;
if(pkt->size)
{
decode(ctx,frame,pkt,p_outFile);
}
if(data_size < VIDEO_REFILL_THRESH);//如果数据量少于设定值,则重新读取
{
memmove(inbuf,data,data_size);//将之前剩余的数据拷贝到buffer的起始位置,使得buffer存放剩余的数据
data = inbuf;
//重新读取数据,从data_size处开始存放,读取数据长度为VIDEO_INBUF_SIZE - data_size
int len = fread(data+data_size,1,VIDEO_INBUF_SIZE - data_size,p_inFile);
if(len > 0)
data_size += len;
}
}
/* 冲刷解码器 */
pkt->data = NULL; // 让其进入drain mode
pkt->size = 0;
decode(ctx, frame,pkt, p_outFile);
return 0;
}
void decode_vedio::decode(AVCodecContext *ctx,AVFrame *frame, AVPacket *pkt, FILE *outFile)
{
int ret;
ret = avcodec_send_packet(ctx,pkt);
if(ret == AVERROR(EAGAIN))
{
fprintf(stderr,"egain\n");
}else if(ret < 0)
{
fprintf(stderr,"avcodec_send_packet failed\n");
return;
}
while(ret >= 0)
{
ret = avcodec_receive_frame(ctx,frame);
if(ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
fprintf(stderr,"avcodec_receive_frame EAGAIN or AVERROR_EOF\n");
return;
}else if(ret < 0)
{
fprintf(stderr,"avcodec_receive_frame failed\n");
return;
}
//写入y分量,data[0],linesize[0]表示存放y分量
for(int y = 0;y < frame->height;y++)
{
fwrite(frame->data[0]+y*frame->linesize[0],1,frame->width,outFile);
}
//写入u分量,data[1],linesize[1]表示存放u分量
for(int u = 0;u < frame->height/2;u++)
{
fwrite(frame->data[1]+u*frame->linesize[1],1,frame->width/2,outFile);
}
//写入v分量,data[2],linesize[2]表示存放v分量
for(int v = 0;v < frame->height/2;v++)
{
fwrite(frame->data[2]+v*frame->linesize[2],1,frame->width/2,outFile);
}
}
}