----------------------------------------
date: 2022-04-01
author:hjjdebug
----------------------------------------
decode_audio.c 解读
----------------------------------------
decode_audio, 它能把一个音频编码文件解出成原始音频数据,并写到文件中.
原始音频数据怎样播放? 您可以用ffplay, 但要传递给它参数,见后.
下面演示了如何从文件中读取,分析和写入文件的过程.
1. pkt = av_packet_alloc(); //分配一个包
2. codec = avcodec_find_decoder(AV_CODEC_ID_AAC); //查找一种codec
3. c = avcodec_alloc_context3(codec); 为某codec 分配一个上下文
4. if (avcodec_open2(c, codec, NULL) < 0) ; // 打开codec
5. 打开输入文件和输出文件
f = fopen(filename, "rb");
outfile = fopen(outfilename, "wb");
6. if (!(decoded_frame = av_frame_alloc())) //分配一个frame
7. 读数据到缓存
data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
AUDIO_INBUF_SIZE 可是20480, 缓存不小!
读到了20480数据,现在开始分析它.
8. parser = av_parser_init(codec->id); //初始化parser, 针对某种codec的parser
9. ret = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size,
data, data_size,
AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
输入参数:数据指针(data),数据大小(data_size)
输出参数:pkt->data, pkt->size //数据被分析到packet 中
其它参数:parser context, codec context, 均为输入参数.
返回值, 对aac 来讲返回固定的928 长度.
即每次分析928字节进入pkt,然后把data指针和data_size进行调整以便继续后续分析
10. decode(c, pkt, decoded_frame, outfile);
解码pkt 到decoded_frame 并写入outfile, 这是用户的代码了. 不过还是要调用ffmpeg 接口.
具体如下:
11. 解码过程,发送包,收获frame .
ret = avcodec_send_packet(dec_ctx, pkt);
ret = avcodec_receive_frame(dec_ctx, frame);
send_packet, receive_frame 将压缩包经codec 解开到 frame.
然后: 获取采样点的大小.(由sample format 得到)
data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
将frame 解出的原始数据写到文件.
for (i = 0; i < frame->nb_samples; i++)
for (ch = 0; ch < dec_ctx->channels; ch++)
fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
//frame 中包含几个channel的数据,由dec_ctx->channel 确定, 目前能支持到8个通道
//每个采样点的大小(data_size), 由采样格式确定 dec_ctx->sample_fmt
//而frame->nb_samples, 当然也是从dec_ctx中获取的,保存到frame 结构中了。
如此即可将解出的frame各channel数据交错写到输出文件中了.
12. 当缓冲的数据小于4096时,再从文件继续读数据塞满缓冲区, 直到data_size 消耗为0,解码完毕.
13 要想播放这个原始数据音频文件,需要记录下它的数据格式:
ret = get_format_from_sample_fmt(&fmt, sfmt))// 从sfmt 获取字符串format
sfmt 是源format, 是 dec_ctx->sample_fmt
ffplay -f f32le -ac 2 -ar 44100 Audio.data
f32le: aac 的采样格式 (sample_fmt)
44100: aac 的采样率 (sample_rate)
2 : aac 的采样通道数 (channels)
从此,你可以了解到音频解码的流程.
这里还要补充一点, 代码在 parse 到 packet 之后, dec_ctx的信息才补充完整,channels,sample_rate 才有了值!
是的,这是因为 codec 本身并不知道有多少channels, sample_rate是多少,codec 只负责解码,它知道数据 pix的format,
但收到数据包后ctx就不一样了, ctx 会存储具体的数据流信息如channels,sample_rate等,就是所谓的流信息
decode_video.c 解读
----------------------------------------
相较decode_audio.c,
一致的地方都是: 用指定的codec 来打开codec_ctx.
具体为由codecID找到codec, 分配codec_ctx, 打开codec,初始化parser,
分配包,分配frame,打开输入文件.
分析一下不同的地方
1. 首先这个inbuf 的buffer size 只有4096, 而不是音频的20480
2. 没有打开输出文件.每一个frame 对应一个输出文件.
3. 从输入文件读入一次数据4096, 分析缓存中数据消耗4096,但可能并
不能构成一个pkt, pkt->size=0, 会继续读入数据,分析数据,直到pkt->size!=0.
哇!这一次一下子成了25709, 可见也是多个4096 供给的.
然后进行decode.
有可能把packet 发过去,并不能解出frame, 而是EAGAIN, 还需要继续供给packet
parser 分析每次消耗并不一定是4096, 它以能形成packet为准则.
构不成frame 也没问题,反正dec_ctx 会记录.
解出了frame, 由dec_ctx->frame_number 构成输出文件名,将frame 中的数据写入到文件.
数据要素是frame->data, frame->linesize,frame->width,frame-height.
frame->data 是8个指针, 传第一个就够了.
frame->linesize是8个整数,传第一个就够了.
然后打开一个文件,记录下xsize,ysize, 然后书写二进制数据,按行写.每次写一行.
补充:
问1:parser 读到一个packet, 此时packet中有什么?
答1: parser可能要经过若干次数据输入才能形成一个packet,此时只有packet->data,packet-size是有效的.
问2:解码是send_packet, receive_frame, frame->width,frmae->height是如何得到的?
答2: 发过去一个packet, 可能并不能形成一个有效的frame,此时会返回EAGAIN,如果得到了frame,
此时也会填充好frame->width,height参数及data,linesize参数等,称为已经解出了frame,
此时再看ctx, 相关的数据也已经填充好. 可见视频是在send_packet 中更新codec_ctx 信息。