decode_audio.c 解读/decode_video.c 解读

----------------------------------------
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 信息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
av_frame_alloc函数用于为AVFrame结构体分配内存空间,AVFrame结构体用于存储解码后的音视频数据。在音频解码中,可以通过调用该函数为AVFrame结构体分配内存空间,然后将解码后的音频数据存储到该结构体中,最后再进行后续的处理操作。 具体使用方法如下: ```c AVFrame *frame = av_frame_alloc(); if (!frame) { // 内存分配失败处理 } ``` 其中,AVFrame结构体的定义如下: ```c typedef struct AVFrame { /** * pointers to the data planes/channels. * This might be different from the first allocated byte */ uint8_t *data[AV_NUM_DATA_POINTERS]; /** * For video, size in bytes of each picture line. * For audio, size in bytes of each plane. */ int linesize[AV_NUM_DATA_POINTERS]; /** * pointers to the start of each picture line. * This is used for both video and audio. */ uint8_t **extended_data; /** * width and height of the video frame */ int width, height; /** * number of audio samples (per channel) described by this frame */ int nb_samples; /** * format of the frame, -1 if unknown or unset * Values correspond to enum AVPixelFormat for video frames, * enum AVSampleFormat for audio) */ int format; /** * 1 -> keyframe, 0-> not */ int key_frame; /** * Picture type of the frame. */ enum AVPictureType pict_type; /** * Quality (between 1 (good) and FF_LAMBDA_MAX (bad)). */ int quality; /** * for AV_PIX_FMT_*_BE formats, linesize alignment */ int64_t best_effort_timestamp; /** * PTS copied from the AVPacket that was decoded to produce this frame. */ int64_t pkt_pts; /** * DTS copied from the AVPacket that triggered returning this frame. */ int64_t pkt_dts; /** * duration of the corresponding packet, expressed in * AVStream->time_base units, 0 if unknown. */ int64_t pkt_duration; uint64_t metadata; int decode_error_flags; /** * number of audio channels */ int channels; /** * size of the corresponding packet containing the compressed * frame. It is set to a negative value if unknown. */ int pkt_size; /** * data type */ enum AVSampleFormat sample_fmt; /** * presentation timestamp in timebase units * (time when frame should be shown to user) * If AV_NOPTS_VALUE then frame_rate = 1/time_base will be assumed. */ int64_t pts; /** * reordered PTS from the last AVPacket that has been input into the decoder */ int64_t best_effort_timestamp; /** * sample aspect ratio (0 if unknown) * - encoding: Set by user. * - decoding: Set by libavcodec. */ AVRational sample_aspect_ratio; /** * motion vector table, used for MPEG1/2/4 and H.263 decoding */ struct AVPanScan *pan_scan; /** * macroblock decision mode */ int coded_picture_number; int display_picture_number; /** * quality (between 1 (good) and FF_LAMBDA_MAX (bad)) */ int quality; /** * buffer age (1->was last buffer and dint change, 2->..., ...) */ int age; /** * is this picture used as reference */ int reference; /** * QP table */ int8_t *qp_table_buf; /** * QP store stride */ int qp_table_linesize; /** * QP values for each macroblock */ int8_t *qp_table; /** * For hwaccel-format frames, this should be a reference to the * AVHWFramesContext describing the frame. * For normal-format frames, this should be NULL. */ AVBufferRef *hw_frames_ctx; } AVFrame; ``` 需要注意的是,AVFrame结构体中的成员变量很多,具体使用哪些成员变量取决于具体的应用场景和需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值