FFmpeg Decode(解码)

ffmpeg的解码过程 上面步骤结束后,就可以调用API。 阅读详情

首发地址,会更错

本文来自官方例子doc/examples/decode_audio.c 和 doc/examples/decode_video.c。

FFmpeg解码流程

流程

其中,AVFormatContext、AVPacket等重要的结构体请看:FFmpeg重要结构体(转自雷神) 。

官方例子【Audio】

/**
 * @author 秦城季
 * @email xhunmon@126.com
 * @Blog https://qincji.gitee.io
 * @date 2021/01/06
 * description: 来自官方例子:doc/examples/decode_audio.c  <br>
 */
#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

static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,
                   FILE *outfile)
{
    int i, ch;
    int ret, data_size;

    //把AVPacket数据送去解码器
    ret = avcodec_send_packet(dec_ctx, pkt);
    if (ret < 0) {
        fprintf(stderr, "Error submitting the packet to the decoder\n");
        exit(1);
    }

    /* read all the output frames (in general there may be any number of them */
    while (ret >= 0) {
        //接到解码后的<AVPacket数据>,读取到AVFrame中
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            exit(1);
        }
        /*下面是得到解码后的裸流数据进行处理,根据裸流数据的特征做相应的处理,如AAC解码后是PCM,h264解码后是YUV等。*/
        data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
        if (data_size < 0) {
            /* This should not occur, checking just for paranoia */
            fprintf(stderr, "Failed to calculate data size\n");
            exit(1);
        }
        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);
    }
}

int main(int argc, char **argv)
{
    const char *outfilename, *filename;
    const AVCodec *codec;
    AVCodecContext *c= 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;

    filename    = "source/Kobe.aac";
    outfilename = "output/Kebe.pcm";

    pkt = av_packet_alloc();

    //通过AVCodecID查找到相应的AVCodec,类型需要输入文件解码类型保持一致
    codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    //获取解析器上下文
    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "Parser not found\n");
        exit(1);
    }

    //获取解码器器上下文
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }

    //初始化码器器上下文
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    f = fopen(filename, "rb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
    outfile = fopen(outfilename, "wb+");
    if (!outfile) {
        av_free(c);
        exit(1);
    }

    /* decode until eof */
    data      = inbuf;
    data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);

    //读取输入文件
    while (data_size > 0) {
        if (!decoded_frame) {
            if (!(decoded_frame = av_frame_alloc())) {
                fprintf(stderr, "Could not allocate audio frame\n");
                exit(1);
            }
        }

        //把输入的文件流进行解析,输出到AVPacket中
        ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
                               data, data_size,
                               AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (ret < 0) {
            fprintf(stderr, "Error while parsing\n");
            exit(1);
        }
        data      += ret;
        data_size -= ret;

        if (pkt->size){
            //这部分主要是把上面AVPacket数据进行解码,最后得到裸流数据输出到AVFrame中
            decode(c, pkt, decoded_frame, outfile);
        }

        if (data_size < AUDIO_REFILL_THRESH) {
            memmove(inbuf, data, data_size);
            data = inbuf;
            len = fread(data + data_size, 1,
                        AUDIO_INBUF_SIZE - data_size, f);
            if (len > 0)
                data_size += len;
        }
    }

    /* flush the decoder */
    pkt->data = NULL;
    pkt->size = 0;
    decode(c, pkt, decoded_frame, outfile);

    fclose(outfile);
    fclose(f);

    avcodec_free_context(&c);
    av_parser_close(parser);
    av_frame_free(&decoded_frame);
    av_packet_free(&pkt);

    return 0;
}
  • 测试文件下载地址: Kobe.aac 。
  • 解析后的pcm原始数据可以使用ffplay或者audition等工具播放,而播放前必须先知道该原始数据的初始化信息,如通道数、位宽等,可以使用ffprobe进行查看解码前的aac文件,如:
qincji:usr mac$ ffprobe ./VAFFmpeg/source/Kobe.aac 
ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developers
  built with Apple clang version 11.0.0 (clang-1100.0.33.12)
  configuration: 
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
[aac @ 0x7fec2b005200] Estimating duration from bitrate, this may be inaccurate
Input #0, aac, from '/Users/Qincji/Desktop/develop/android/project/va/VAFFmpeg/source/Kobe.aac':
  Duration: 00:00:29.90, bitrate: 34 kb/s
    Stream #0:0: Audio: aac (HE-AAC), 44100 Hz, stereo, fltp, 34 kb/s
  • 使用audition输入出文件实例:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I6Fvxf22-1610869697162)(11_decode/decode-audio.jpg)]

官方例子【Video】

/**
 * @author 秦城季
 * @email xhunmon@126.com
 * @Blog https://qincji.gitee.io
 * @date 2021/01/06
 * description: 来自官方例子:doc/examples/decode_video.c  <br>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern "C"{
#include <libavcodec/avcodec.h>
}
#define INBUF_SIZE 4096


static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
                   FILE *outfile)
{
    int ret;

    ret = avcodec_send_packet(dec_ctx, pkt);
    if (ret < 0) {
        fprintf(stderr, "Error sending a packet for decoding\n");
        exit(1);
    }

    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            exit(1);
        }

        printf("saving frame %3d\n", dec_ctx->frame_number);
        fflush(stdout);

        //根据自己解码出来的数据格式进行处理
        fwrite(frame->data[0],1,frame->width*frame->height,outfile);//Y
        fwrite(frame->data[1],1,frame->width*frame->height/4,outfile);//U
        fwrite(frame->data[2],1,frame->width*frame->height/4,outfile);//V
    }
}

int main(int argc, char **argv)
{
    const char *filename, *outfilename;
    const AVCodec *codec;
    AVCodecParserContext *parser;
    AVCodecContext *c= NULL;
    FILE *f;
    AVFrame *frame;
    uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data;
    size_t   data_size;
    int ret;
    AVPacket *pkt;

    filename    = "source/Kobe.h264";
    outfilename = "output/Kobe.yuv";

    FILE *outfile = fopen(outfilename, "wb");

    pkt = av_packet_alloc();
    if (!pkt)
        exit(1);

    /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
    memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

    /* find the MPEG-1 video decoder */
    codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }

    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "parser not found\n");
        exit(1);
    }

    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }

    /* For some codecs, such as msmpeg4 and mpeg4, width and height
       MUST be initialized there because this information is not
       available in the bitstream. */

    /* open it */
    if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    f = fopen(filename, "rb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }

    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }

    while (!feof(f)) {
        /* read raw data from the input file */
        data_size = fread(inbuf, 1, INBUF_SIZE, f);
        if (!data_size)
            break;

        /* use the parser to split the data into frames */
        data = inbuf;
        while (data_size > 0) {
            ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
                                   data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
            if (ret < 0) {
                fprintf(stderr, "Error while parsing\n");
                exit(1);
            }
            data      += ret;
            data_size -= ret;

            if (pkt->size)
                decode(c, frame, pkt, outfile);
        }
    }

    /* flush the decoder */
    decode(c, frame, NULL, outfile);

    fclose(f);

    av_parser_close(parser);
    avcodec_free_context(&c);
    av_frame_free(&frame);
    av_packet_free(&pkt);

    return 0;
}
  • 测试文件下载地址: Kobe.h264 。
  • 解析后的yuv原始数据可以使用ffplay等工具播放,而播放前必须先知道该原始数据的宽高等信息,可以使用ffprobe进行查看解码前的h264文件,如:
qincji:usr mac$ ffprobe /Users/Qincji/Desktop/develop/android/project/va/VAFFmpeg/source/Kobe.h264 
ffprobe version 4.2.2 Copyright (c) 2007-2019 the FFmpeg developers
  built with Apple clang version 11.0.0 (clang-1100.0.33.12)
  configuration: 
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
Input #0, h264, from '/Users/Qincji/Desktop/develop/android/project/va/VAFFmpeg/source/Kobe.h264':
  Duration: N/A, bitrate: N/A
    Stream #0:0: Video: h264 (Main), yuv420p(progressive), 384x216 [SAR 1:1 DAR 16:9], 15 fps, 15 tbr, 1200k tbn, 30 tbc
  • 使用ffplay播放:

ffplay -f rawvideo -video_size 384x216 output/Kobe.yuv

输入出文件实例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y7P1pdYF-1610869697165)(11_decode/decode-video.jpg)]



基于FFmpeg实现简单的视频解码器 但这些像素并不是连续存储的,每行有效像素之后存储了一些无效像素,经过该函数处理,去掉无效数据。FFmpeg解码涉及的知识点比较多,很容易被函数和结构体搞定不知所错,我们先从整体上对解码流程有个认知,画了张解码流程图,如下。avcodec_find_decoder 根据视频流信息的codec_id找到对应的解码器_通过上一小节,我们了解了FFmpeg解码流程和关键的结构体,这一小节我们来实践。视频显示的流程,就是将像素数据“画”在屏幕上的过程。输出解码前的h264码流、输出解码后的YUV信息。 阅读详情

相关推荐

Android中集成FFmpeg及NDK基础知识

写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的点击我的GitHub免费获取。

m0_61549674的博客 769

html5 decodeaudio,HTML5播放语音时 音频解析出错 Unable to decode audio data 无法解析音频数据...

window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;var audioContext = new window.AudioContext();function playSound(soundBuffer) {...

weixin_32314595的博客 3196

FFmpeg音频解码函数---avcodec_decode_audio4

/*@param avctx编解码器上下文  *@param [out] frame用于存储解码音频样本的AVFrame  *@param [out] got_frame_ptr如果没有帧可以解码则为零,否则为非零  *@param [in] avpkt包含输入缓冲区的输入AVPacket  *@return 如果在解码期间发生错误,则返回否定错误代码,否则返回从输入AVPacket消耗的字节数...

SmallBottle-CGWLMX的博客 4171

python 文本转语音播放失败_HTML5播放语音时 音频解析出错 Unable to decode audio data 无法解析音频数据...

window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;var audioContext = new window.AudioContext();function playSound(soundBuffer) {...

weixin_39975529的博客 2633

FFmpeg的H 264解码器源代码简单分析 宏块解码(Decode)部分-帧内宏块(Intra)

FFmpeg的H 264解码器源代码简单分析 宏块解码(Decode)部分-帧内宏块(Intra)

hffgjh的博客 1018

ffmpeg源码简析(八)解码 av_read_frame(),avcodec_decode_video2(),avformat_close_input()

1.av_read_frame()av_read_frame()的作用是读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码(例如H.264中一帧压缩数据通常对应一个NAL)。通过av_read_packet(***),读取一个包,需要说明的是此函数必须是包含整数帧的,不存在半帧的情况,

WangShuo的专栏 2616

FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧间宏块(Inter)

本文继续分析FFmpeg的H.264解码器的宏块解码(Decode)部分。上一篇文章已经记录了帧内预测宏块(Intra)的宏块解码,本文继续上一篇文章的内容,记录帧间预测宏块(Inter)的宏块解码。

雷霄骅(leixiaohua1020)的专栏 1万+

ffmpeg-4.1 hw_decode.c

ffmpeg例子hw_decode.c在硬解码rtsp流的时候会报错,因此做了如下修改: #include <stdio.h> #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libavutil/pixdesc.h> #include <li...

qq_14877637的博客 2997

FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧内宏块(Intra)...

===================================================== H.264源代码分析文章列表: 【编码 - x264】 x264源代码简单分析:概述 x264源代码简单分析:x264命令行工具(x264.exe) x264源代码简单分析...

chuifuhuo6864的博客 810

ffmpeg库 decode_video.c例子详解,读取视频编码文件后解码每帧保存为pgm图像

/** * @file * video decoding with libavcodec API example * * @example decode_video.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <libavcodec/avcodec.h> #define INBUF_SIZE 4096 /* example fo..

无极生太极 1071

【FFmpeg编程进阶】(17)解码packet数据 try_decode_frame() 代码分析

【FFmpeg编程进阶】(17)解码packet数据 try_decode_frame 代码分析一、try_decode_frame()二、解码一帧数据 avcodec_send_packet(avctx, &pkt)三、接收解码后的数据 avcodec_receive_frame(avctx, frame) 一、try_decode_frame() 判断是否已经打开 avcodec(条件为: AVCodecInternal 是否存在,found_decoder,codec_id) 如果未初始化

|~~~热爱生活、努力学习的小伙汁~~~| 1167

2023-04-18:ffmpeg中的hw_decode.c的功能是通过使用显卡硬件加速器(如 NVIDIA CUDA、Intel Quick Sync Video 等)对视频进行解码,从而提高解码效

本文介绍了如何使用Golang实现FFmpeg硬解码程序。通过对FFmpeg官方的HW Decode示例进行适当修改,我们成功地完成了设备类型检查、输入文件打开、解码器配置和输出文件处理等功能。此外,我们也介绍了如何在实际应用中使用FFmpeg库,并提供了一些代码片段供读者参考。

福大大架构师每日一题 2万+

【FFmpeg编程进阶】(14)FLV 解码函数 ff_h263_decode_frame() 代码分析 - 待更新

【FFmpeg编程进阶】(13)FLV 解码函数 ff_h263_decode_frame代码分析一、ff_h263_decode_frame() 在前面分析《【FFmpeg编程进阶】(11)avcodec_open2() 函数代码分析 - 打开Codec解码器》时, 我们涉及到了Codec 解码函数,由于代码比较长,我们再分一章来分析。 主要目的,还是以 FLV 解码器为例 ,来看下解码器解码时主要做了啥 先记录下TODO LIST,后面再更新。 # libavcodec\flvdec.c AVCodec

|~~~热爱生活、努力学习的小伙汁~~~| 767

FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧间宏块(Inter)...

===================================================== H.264源代码分析文章列表: 【编码 - x264】 x264源代码简单分析:概述 x264源代码简单分析:x264命令行工具(x264.exe) x264源代码简单分析...

chuifuhuo6864的博客 659

【FFmpeg编程进阶】(13)FLV 解码器初始化函数 ff_h263_decode_init() 代码分析 --- 待更新

【FFmpeg编程进阶】(13)ff_flv_decoder 初始化函数 ff_h263_decode_init 代码分析 --- 待更新一、ff_h263_decode_init() 在前面分析《【FFmpeg编程进阶】(11)avcodec_open2() 函数代码分析 - 打开Codec解码器》时, 我们涉及到了解码Codec init 初始化,由于代码比较长,我们再分一章来分析。 主要目的,还是以 FLV 解码器为例 ,来看下解码器主要做了啥,不用太精细,了解大概做了啥事就好了, 至于更精细的,我们

|~~~热爱生活、努力学习的小伙汁~~~| 686

windows10下 iperf3测试带宽

客户端输入:"iperf3.exe -u -c 服务器IP地址 -b 5g" //客户端给服务器发送udp数据包,最大带宽为5g。客户端输入:“iperf3.exe -c 服务器IP地址 -P 10“//表示客户端10路并行给服务器发送TCP包;上图可知:带宽是15Gbps(这里不知道为什么下面测试的UDP会比这里TCP慢 按道理UDP不可能比TCP慢的5.)3. 验证是否下载成功,在打开的窗口中输入“iperf3.exe -v”,会出现安装的版本,表示安装成功了。5. iperf3参数。

xiaoqixiaoguai的博客 5889

FFmpeg源代码简单分析-解码-avcodec_send_packet 和 avcodec_receive_frame 替代 avcodec_decode_video2

FFmpeg源代码简单分析-解码-avcodec_send_packet 和avcodec_receive_frame 替代avcodec_decode_video2

CHYabc123456hh的博客 989

Arcmap计算土方量教程

1. 创建现状TIN 结果数据: 2. 创建设计TIN 结果数据: 注意:现状TIN和设计TIN构建时候的坐标系不同,设置坐标系见最后的注意事项第1点,就是import(导入)那步骤,现状TIN构建时选择高程点数据或者等高线数据,设计TIN构建时候选择洱海面数据 3. TIN转栅格 改成你要存的数据库,注意中间不要有汉字 结果数据(设计TIN转栅格的同理) 4. 裁剪现状TIN转成的栅格 结果数据: 5. 计算土方量 结果数据: ...

欢迎关注,共同进步! 5014

FFmpeg Decode(解码)-my write

本文来自官方例子和。

小硕算法工程师 483

rk3588 Android HDMI IN热插拔解决

1、公司在使用 别的厂商的板卡遇到一个问题,开机我们的app自启就会闪退,后来定位发现是camera 的open出错了,这个问题的出现是因为没有插HDMI IN输入的问题导致的,所以需要对HDMI IN的热插拔进行检测,后面我把这个问题也顺利解决了。下面是我的尝试和使用的解决办法。3、话不多说,我用的是读取系统文件实现的,但是这个方法,需要有status文件的系统读权限,可以修改设备下的/vendor/etc/init/hw/init.rk3588.rc文件修改的权限,这个问题交给自己或者厂家协商解决。

qq_54089476的博客 1918

Ollama模型路径迁移实战:Windows/Mac/Linux三平台保姆级教程(附常见问题排查)

本文提供了一份详细的Ollama模型路径迁移教程,涵盖Windows、macOS和Linux三大平台。通过设置OLLAMA_MODELS环境变量或创建符号链接,指导用户将模型从默认路径安全迁移至新位置,以释放系统盘空间或提升性能。教程包含迁移前的准备、分平台步骤、迁移后的验证以及常见问题排查,确保迁移过程平滑可靠。

js777的博客 1184
上一篇: FFmpeg Remuxing(重新封装)
下一篇: FFmpeg Encode(编码)
秦城季
博客等级 码龄10年 46粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值