ffmpeg

it2024-03-28  51


title: ffmpeg_sample解读_decode_audio date: 2020-10-21 10:15:02 tags: [读书笔记] typora-copy-images-to: ./imgs typora-root-url: ./imgs


概括

Ffmpeg 项目中的 实例解读. 把他移到安卓项目中来开发了.

这个项目是解码音频数据,输入aac格式的文件.最后生成pcm格式的文件

总结. 就是每次从文件中读取一部分数据(20480),然后把这部分数据解析成packet. 然后在送入解码器解码成frame.最后写到文件中,然后在继续从文件中读取数据,解析packet.生成frame.

附一个网上的图,他讲的也很好.附上源连接

博客:https://www.jianshu.com/p/d77718947e21

源码讲解

/** * @file * audio decoding with libavcodec API example * * @example decode_audio.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libavutil/frame.h> #include <libavutil/mem.h> #include <libavcodec/avcodec.h> #include "../macro.h" #define AUDIO_INBUF_SIZE 20480 #define AUDIO_REFILL_THRESH 4096 static int get_format_from_sample_fmt(const char **fmt, enum AVSampleFormat sample_fmt) { int i; struct sample_fmt_entry { enum AVSampleFormat sample_fmt; const char *fmt_be, *fmt_le; } sample_fmt_entries[] = { {AV_SAMPLE_FMT_U8, "u8", "u8"}, {AV_SAMPLE_FMT_S16, "s16be", "s16le"}, {AV_SAMPLE_FMT_S32, "s32be", "s32le"}, {AV_SAMPLE_FMT_FLT, "f32be", "f32le"}, {AV_SAMPLE_FMT_DBL, "f64be", "f64le"}, }; *fmt = NULL; for (i = 0; i < FF_ARRAY_ELEMS(sample_fmt_entries); i++) { struct sample_fmt_entry *entry = &sample_fmt_entries[i]; if (sample_fmt == entry->sample_fmt) { *fmt = AV_NE(entry->fmt_be, entry->fmt_le); return 0; } } LOGE(stderr, "sample format %s is not supported as output format\n", av_get_sample_fmt_name(sample_fmt)); return -1; } static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame, FILE *outfile) { int i, ch; int ret, data_size; //把文件解析的数据发送到解码器中 /* send the packet with the compressed data to the decoder */ ret = avcodec_send_packet(dec_ctx, pkt); if (ret < 0) {// 这里出错 LOGE(stderr, "Error submitting the packet to the decoder\n%s", ret); exit(1); } /* read all the output frames (in general there may be any number of them */ while (ret >= 0) { //获取解码出的frame .一个packet可能会解码出许多的frame. ret = avcodec_receive_frame(dec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return;//出错或者到了末尾 else if (ret < 0) { LOGE(stderr, "Error during decoding\n"); exit(1); } //根据采样位数拿到采样格式拿到采样位数 data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt); LOGE("采样位数 %d", data_size); if (data_size < 0) {//4 /* This should not occur, checking just for paranoia */ LOGE(stderr, "Failed to calculate data size\n"); exit(1); } for (i = 0; i < frame->nb_samples; i++)//每个声道的样本数 nb_samples 1021 channels 2 for (ch = 0; ch < dec_ctx->channels; ch++) //所有的通道 fwrite(frame->data[ch] + data_size * i, 1, data_size, outfile); } } /** * 解码音频文件 输出 pcm 源文件 * @param argc * @param argv * @return avcodec_find_decoder:根据指定的AVCodecID查找注册的解码器。 av_parser_init:初始化AVCodecParserContext。 avcodec_alloc_context3:为AVCodecContext分配内存。 avcodec_open2:打开解码器。 av_parser_parse2:解析获得一个Packet。 avcodec_send_packet:将AVPacket压缩数据给解码器。 avcodec_receive_frame:获取到解码后的AVFrame数据。 av_get_bytes_per_sample: 获取每个sample中的字节数 输入 /storage/emulated/0/1.aac 输出 /storage/emulated/0/1.pcm 播放 ffplay -f f32le -ac 2 -ar 48000 /storage/emulated/0/1.pcm 博客:https://www.jianshu.com/p/d77718947e21 */ int decode_audio_main(int argc, char **argv) { const char *outfilename, *filename; const AVCodec *codec; AVCodecContext *codecContext = NULL; AVCodecParserContext *parser = NULL; int len, ret; FILE *f, *outfile; uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];// 数据加偏移,防止一次读到末尾 uint8_t *data; size_t data_size; AVPacket *pkt; AVFrame *decoded_frame = NULL; enum AVSampleFormat sfmt; int n_channels = 0; const char *fmt; if (argc <= 2) { LOGE(stderr, "Usage: %s <input file> <output file>\n", argv[0]); exit(0); } filename = argv[1]; outfilename = argv[2]; //初始化一个packet,用来接收解封装后的数据 pkt = av_packet_alloc(); /* find the MPEG audio decoder */ //找到解码器,指定类型的音频解码器 这里指的AAC 因为输入文件是aac codec = avcodec_find_decoder(AV_CODEC_ID_AAC); if (!codec) { LOGE(stderr, "Codec not found\n"); exit(1); } //通过解码器找到paser上下文 // AVCodecParser:用于解析输入的数据流并把它分成一帧一帧的压缩编码数据。比较形象的说法就是把长长的一段连续的数据“切割”成一段段的数据 parser = av_parser_init(codec->id); if (!parser) { LOGE(stderr, "Parser not found\n"); exit(1); } //c创建解码器上下文,用给定的解码器. 初始化默认参数 codecContext = avcodec_alloc_context3(codec); if (!codecContext) { LOGE(stderr, "Could not allocate audio codec context\n"); exit(1); } /* open it *///打开解码器上下文.我理解就是通过解码器进行合适的参数设置 if (avcodec_open2(codecContext, codec, NULL) < 0) { LOGE(stderr, "Could not open codec\n"); exit(1); } //打开读入文件,指定读入方式 f = fopen(filename, "rb"); if (!f) { LOGE(stderr, "Could not open %s\n", filename); exit(1); } //写出的文件 outfile = fopen(outfilename, "wb"); if (!outfile) { av_free(codecContext); exit(1); } /* decode until eof */ data = inbuf; //从f中读取20k的数据到inbuf中,返回读了多少进来 data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f); LOGE("data_size is %d ", data_size); while (data_size > 0) { if (!decoded_frame) { //分配1帧用来解析数据,这是存放解码后的数据 if (!(decoded_frame = av_frame_alloc())) { LOGE(stderr, "Could not allocate audio frame\n"); exit(1); } } //把从文件中读取到的20k的数据的data中转到packet中,最后更新data和data.size,此时数据在packet中, //最后三个参数是pts. dts pos ret 表示解析了多少数据 ret = av_parser_parse2(parser, codecContext, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (ret < 0) { LOGE(stderr, "Error while parsing\n"); exit(1); } //这里表示已经从data中解析了多少数据到packet中 data += ret; data_size -= ret; //表示已经解析了多少数据 if (pkt->size) //把解析出的数据解码后放到frame里,然后输出 decode(codecContext, pkt, decoded_frame, outfile); //这里表示data剩余的数据已经小于 4096这个阈值.需要继续从文件中读取一部分数据. if (data_size < AUDIO_REFILL_THRESH) { // 从data中拷贝 data_size 的数据到inbuf 中,也就是把data剩余的数据留下.已经解析的都删掉. memmove(inbuf, data, data_size); data = inbuf; //在继续从文件中读入数据.因为data此时不是完全空的,所以要减去已用的空间 len = fread(data + data_size, 1, AUDIO_INBUF_SIZE - data_size, f); if (len > 0) data_size += len; //表示从文件中读取完成后,还有多少可以解析的数据 } } //到这里文件的数据都写完了. 需要刷新解码器把未完成的packet都解码出来 /* flush the decoder */ pkt->data = NULL; pkt->size = 0; decode(codecContext, pkt, decoded_frame, outfile); //接下来是打印格式 .因为pcm只有音频数据.无法播放.需要指定相关数据 /* print output pcm infomations, because there have no metadata of pcm */ sfmt = codecContext->sample_fmt; if (av_sample_fmt_is_planar(sfmt)) { const char *packed = av_get_sample_fmt_name(sfmt); LOGE("Warning: the sample format the decoder produced is planar " "(%s). This example will output the first channel only.\n", packed ? packed : "?"); sfmt = av_get_packed_sample_fmt(sfmt); } n_channels = codecContext->channels; if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0) goto end; LOGE("Play the output audio file with the command:\n" "ffplay -f %s -ac %d -ar %d %s\n", fmt, n_channels, codecContext->sample_rate, outfilename); //-f 格式, -ac 通道数 -ar pcm采样率 //ffplay -f f32le -ac 2 -ar 48000 /storage/emulated/0/1.pcm end: fclose(outfile); fclose(f); avcodec_free_context(&codecContext); av_parser_close(parser); av_frame_free(&decoded_frame); av_packet_free(&pkt); return 0; }

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

最新回复(0)