FFmpeg编解码demo

一、 FFmpeg解复用(解封装)Demo

#include <stdio.h>
#include <libavformat/avformat.h>


int main(int argc, char **argv)
{
    //打开网络流。这里如果只需要读取本地媒体文件,不需要用到网络功能,可以不用加上这一句
//    avformat_network_init();

    const char *default_filename = "believe.mp4";

    char *in_filename = NULL;

    if(argv[1] == NULL)
    {
        in_filename = default_filename;
    }
    else
    {
        in_filename = argv[1];
    }
    printf("in_filename = %s\n", in_filename);

    //AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
    AVFormatContext *ifmt_ctx = NULL;           // 输入文件的demux

    int videoindex = -1;        // 视频索引
    int audioindex = -1;        // 音频索引


    // 打开文件,主要是探测协议类型,如果是网络文件则创建网络链接
    int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
    if (ret < 0)  //如果打开媒体文件失败,打印失败原因
    {
        char buf[1024] = { 0 };
        av_strerror(ret, buf, sizeof(buf) - 1);
        printf("open %s failed:%s\n", in_filename, buf);
        goto failed;
    }

    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if (ret < 0)  //如果打开媒体文件失败,打印失败原因
    {
        char buf[1024] = { 0 };
        av_strerror(ret, buf, sizeof(buf) - 1);
        printf("avformat_find_stream_info %s failed:%s\n", in_filename, buf);
        goto failed;
    }

    //打开媒体文件成功
    printf_s("\n==== av_dump_format in_filename:%s ===\n", in_filename);
    av_dump_format(ifmt_ctx, 0, in_filename, 0);
    printf_s("\n==== av_dump_format finish =======\n\n");
    // url: 调用avformat_open_input读取到的媒体文件的路径/名字
    printf("media name:%s\n", ifmt_ctx->url);
    // nb_streams: nb_streams媒体流数量
    printf("stream number:%d\n", ifmt_ctx->nb_streams);
    // bit_rate: 媒体文件的码率,单位为bps
    printf("media average ratio:%lldkbps\n",(int64_t)(ifmt_ctx->bit_rate/1024));
    // 时间
    int total_seconds, hour, minute, second;
    // duration: 媒体文件时长,单位微妙
    total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE;  // 1000us = 1ms, 1000ms = 1秒
    hour = total_seconds / 3600;
    minute = (total_seconds % 3600) / 60;
    second = (total_seconds % 60);
    //通过上述运算,可以得到媒体文件的总时长
    printf("total duration: %02d:%02d:%02d\n", hour, minute, second);
    printf("\n");
    /*
     * 老版本通过遍历的方式读取媒体文件视频和音频的信息
     * 新版本的FFmpeg新增加了函数av_find_best_stream,也可以取得同样的效果
     */
    for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i++)
    {
        AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流
        //如果是音频流,则打印音频的信息
        if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
        {
            printf("----- Audio info:\n");
            // index: 每个流成分在ffmpeg解复用分析后都有唯一的index作为标识
            printf("index:%d\n", in_stream->index);
            // sample_rate: 音频编解码器的采样率,单位为Hz
            printf("samplerate:%dHz\n", in_stream->codecpar->sample_rate);
            // codecpar->format: 音频采样格式
            if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format)
            {
                printf("sampleformat:AV_SAMPLE_FMT_FLTP\n");
            }
            else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format)
            {
                printf("sampleformat:AV_SAMPLE_FMT_S16P\n");
            }
            // channels: 音频信道数目
            printf("channel number:%d\n", in_stream->codecpar->channels);
            // codec_id: 音频压缩编码格式
            if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id)
            {
                printf("audio codec:AAC\n");
            }
            else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id)
            {
                printf("audio codec:MP3\n");
            }
            else
            {
                printf("audio codec_id:%d\n", in_stream->codecpar->codec_id);
            }
            // 音频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
            if(in_stream->duration != AV_NOPTS_VALUE)
            {
                int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
                //将音频总时长转换为时分秒的格式打印到控制台上
                printf("audio duration: %02d:%02d:%02d\n",
                       duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));
            }
            else
            {
                printf("audio duration unknown");
            }

            printf("\n");

            audioindex = i; // 获取音频的索引
        }
        else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type)  //如果是视频流,则打印视频的信息
        {
            printf("----- Video info:\n");
            printf("index:%d\n", in_stream->index);
            // avg_frame_rate: 视频帧率,单位为fps,表示每秒出现多少帧
            printf("fps:%lffps\n", av_q2d(in_stream->avg_frame_rate));
            if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频压缩编码格式
            {
                printf("video codec:MPEG4\n");
            }
            else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频压缩编码格式
            {
                printf("video codec:H264\n");
            }
            else
            {
                printf("video codec_id:%d\n", in_stream->codecpar->codec_id);
            }
            // 视频帧宽度和帧高度
            printf("width:%d height:%d\n", in_stream->codecpar->width,
                   in_stream->codecpar->height);
            //视频总时长,单位为秒。注意如果把单位放大为毫秒或者微妙,音频总时长跟视频总时长不一定相等的
            if(in_stream->duration != AV_NOPTS_VALUE)
            {
                int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);
                printf("video duration: %02d:%02d:%02d\n",
                       duration_video / 3600,
                       (duration_video % 3600) / 60,
                       (duration_video % 60)); //将视频总时长转换为时分秒的格式打印到控制台上
            }
            else
            {
                printf("video duration unknown");
            }

            printf("\n");
            videoindex = i;
        }
    }

    AVPacket *pkt = av_packet_alloc();

    int pkt_count = 0;
    int print_max_count = 10;
    printf("\n-----av_read_frame start\n");
    while (1)
    {
        ret = av_read_frame(ifmt_ctx, pkt);
        if (ret < 0)
        {
            printf("av_read_frame end\n");
            break;
        }

        if(pkt_count++ < print_max_count)
        {
            if (pkt->stream_index == audioindex)
            {
                printf("audio pts: %lld\n", pkt->pts);
                printf("audio dts: %lld\n", pkt->dts);
                printf("audio size: %d\n", pkt->size);
                printf("audio pos: %lld\n", pkt->pos);
                printf("audio duration: %lf\n\n",
                       pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));
            }
            else if (pkt->stream_index == videoindex)
            {
                printf("video pts: %lld\n", pkt->pts);
                printf("video dts: %lld\n", pkt->dts);
                printf("video size: %d\n", pkt->size);
                printf("video pos: %lld\n", pkt->pos);
                printf("video duration: %lf\n\n",
                       pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));
            }
            else
            {
                printf("unknown stream_index:\n", pkt->stream_index);
            }
        }

        av_packet_unref(pkt);
    }

    if(pkt)
        av_packet_free(&pkt);
failed:
    if(ifmt_ctx)
        avformat_close_input(&ifmt_ctx);


    getchar(); //加上这一句,防止程序打印完信息马上退出
    return 0;
}

二、读取媒体文件,将aac帧提取出到本地。

#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>

#define ADTS_HEADER_LEN  7;

const int sampling_frequencies[] = {
    96000,  // 0x0
    88200,  // 0x1
    64000,  // 0x2
    48000,  // 0x3
    44100,  // 0x4
    32000,  // 0x5
    24000,  // 0x6
    22050,  // 0x7
    16000,  // 0x8
    12000,  // 0x9
    11025,  // 0xa
    8000   // 0xb
    // 0xc d e f是保留的
};

int adts_header(char * const p_adts_header, const int data_length,
                const int profile, const int samplerate,
                const int channels)
{

    int sampling_frequency_index = 3; // 默认使用48000hz
    int adtsLen = data_length + 7;

    int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
    int i = 0;
    for(i = 0; i < frequencies_size; i++)
    {
        if(sampling_frequencies[i] == samplerate)
        {
            sampling_frequency_index = i;
            break;
        }
    }
    if(i >= frequencies_size)
    {
        printf("unsupport samplerate:%d\n", samplerate);
        return -1;
    }

    p_adts_header[0] = 0xff;         //syncword:0xfff                          高8bits
    p_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bits
    p_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bit
    p_adts_header[1] |= (0 << 1);    //Layer:0                                 2bits
    p_adts_header[1] |= 1;           //protection absent:1                     1bit

    p_adts_header[2] = (profile)<<6;            //profile:profile               2bits
    p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index  4bits
    p_adts_header[2] |= (0 << 1);             //private bit:0                   1bit
    p_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels  高1bit

    p_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bits
    p_adts_header[3] |= (0 << 5);               //original:0                1bit
    p_adts_header[3] |= (0 << 4);               //home:0                    1bit
    p_adts_header[3] |= (0 << 3);               //copyright id bit:0        1bit
    p_adts_header[3] |= (0 << 2);               //copyright id start:0      1bit
    p_adts_header[3] |= ((adtsLen & 0x1800) >> 11);           //frame length:value   高2bits

    p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bits
    p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bits
    p_adts_header[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bits
    p_adts_header[6] = 0xfc;      //‭11111100‬       //buffer fullness:0x7ff 低6bits
    // number_of_raw_data_blocks_in_frame:
    //    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。

    return 0;
}

int main(int argc, char *argv[])
{
    int ret = -1;
    char errors[1024];

    char *in_filename = NULL;
    char *aac_filename = NULL;

    FILE *aac_fd = NULL;

    int audio_index = -1;
    int len = 0;


    AVFormatContext *ifmt_ctx = NULL;
    AVPacket pkt;

    // 设置打印级别
    av_log_set_level(AV_LOG_DEBUG);

    if(argc < 3)
    {
        av_log(NULL, AV_LOG_DEBUG, "the count of parameters should be more than three!\n");
        return -1;
    }

    in_filename = argv[1];      // 输入文件
    aac_filename = argv[2];     // 输出文件

    if(in_filename == NULL || aac_filename == NULL)
    {
        av_log(NULL, AV_LOG_DEBUG, "src or dts file is null, plz check them!\n");
        return -1;
    }

    aac_fd = fopen(aac_filename, "wb");
    if (!aac_fd)
    {
        av_log(NULL, AV_LOG_DEBUG, "Could not open destination file %s\n", aac_filename);
        return -1;
    }

    // 打开输入文件
    if((ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL)) < 0)
    {
        av_strerror(ret, errors, 1024);
        av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
               in_filename,
               ret,
               errors);
        return -1;
    }

    // 获取解码器信息
    if((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0)
    {
        av_strerror(ret, errors, 1024);
        av_log(NULL, AV_LOG_DEBUG, "failed to find stream information: %s, %d(%s)\n",
               in_filename,
               ret,
               errors);
        return -1;
    }

    // dump媒体信息
    av_dump_format(ifmt_ctx, 0, in_filename, 0);

    // 初始化packet
    av_init_packet(&pkt);

    // 查找audio对应的steam index
    audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if(audio_index < 0)
    {
        av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",
               av_get_media_type_string(AVMEDIA_TYPE_AUDIO),
               in_filename);
        return AVERROR(EINVAL);
    }

    // 打印AAC级别
    printf("audio profile:%d, FF_PROFILE_AAC_LOW:%d\n",
           ifmt_ctx->streams[audio_index]->codecpar->profile,
           FF_PROFILE_AAC_LOW);

    if(ifmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC)
    {
        printf("the media file no contain AAC stream, it's codec_id is %d\n",
               ifmt_ctx->streams[audio_index]->codecpar->codec_id);
        goto failed;
    }
    // 读取媒体文件,并把aac数据帧写入到本地文件
    while(av_read_frame(ifmt_ctx, &pkt) >=0 )
    {
        if(pkt.stream_index == audio_index)
        {
            char adts_header_buf[7] = {0};
            adts_header(adts_header_buf, pkt.size,
                        ifmt_ctx->streams[audio_index]->codecpar->profile,
                        ifmt_ctx->streams[audio_index]->codecpar->sample_rate,
                        ifmt_ctx->streams[audio_index]->codecpar->channels);
            fwrite(adts_header_buf, 1, 7, aac_fd);  // 写adts header , ts流不适用,ts流分离出来的packet带了adts header
            len = fwrite( pkt.data, 1, pkt.size, aac_fd);   // 写adts data
            if(len != pkt.size)
            {
                av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n",
                       len,
                       pkt.size);
            }
        }
        av_packet_unref(&pkt);
    }

failed:
    // 关闭输入文件
    if(ifmt_ctx)
    {
        avformat_close_input(&ifmt_ctx);
    }
    if(aac_fd)
    {
        fclose(aac_fd);
    }

    return 0;
}

三、从媒体文件中提取出h264视频帧到本地

#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>



static char err_buf[128] = {0};
static char* av_get_err(int errnum)
{
    av_strerror(errnum, err_buf, 128);
    return err_buf;
}

/*
AvCodecContext->extradata[]中为nalu长度
*   codec_extradata:
*   1, 64, 0, 1f, ff, e1, [0, 18], 67, 64, 0, 1f, ac, c8, 60, 78, 1b, 7e,
*   78, 40, 0, 0, fa, 40, 0, 3a, 98, 3, c6, c, 66, 80,
*   1, [0, 5],68, e9, 78, bc, b0, 0,
*/

//ffmpeg -i 2018.mp4 -codec copy -bsf:h264_mp4toannexb -f h264 tmp.h264
//ffmpeg 从mp4上提取H264的nalu h
int main(int argc, char **argv)
{
    AVFormatContext *ifmt_ctx = NULL;
    int             videoindex = -1;
    AVPacket        *pkt = NULL;
    int             ret = -1;
    int             file_end = 0; // 文件是否读取结束

    if(argc < 3)
    {
        printf("usage inputfile outfile\n");
        return -1;
    }
    FILE *outfp=fopen(argv[2],"wb");
    printf("in:%s out:%s\n", argv[1], argv[2]);

    // 分配解复用器的内存,使用avformat_close_input释放
    ifmt_ctx = avformat_alloc_context();
    if (!ifmt_ctx)
    {
        printf("[error] Could not allocate context.\n");
        return -1;
    }

    // 根据url打开码流,并选择匹配的解复用器
    ret = avformat_open_input(&ifmt_ctx,argv[1], NULL, NULL);
    if(ret != 0)
    {
        printf("[error]avformat_open_input: %s\n", av_get_err(ret));
        return -1;
    }

    // 读取媒体文件的部分数据包以获取码流信息
    ret = avformat_find_stream_info(ifmt_ctx, NULL);
    if(ret < 0)
    {
        printf("[error]avformat_find_stream_info: %s\n", av_get_err(ret));
        avformat_close_input(&ifmt_ctx);
        return -1;
    }

    // 查找出哪个码流是video/audio/subtitles
    videoindex = -1;
    // 推荐的方式
    videoindex = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if(videoindex == -1)
    {
        printf("Didn't find a video stream.\n");
        avformat_close_input(&ifmt_ctx);
        return -1;
    }

    // 分配数据包
    pkt = av_packet_alloc();
    av_init_packet(pkt);

    // 1 获取相应的比特流过滤器
    //FLV/MP4/MKV等结构中,h264需要h264_mp4toannexb处理。添加SPS/PPS等信息。
    // FLV封装时,可以把多个NALU放在一个VIDEO TAG中,结构为4B NALU长度+NALU1+4B NALU长度+NALU2+...,
    // 需要做的处理把4B长度换成00000001或者000001
    const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");
    AVBSFContext *bsf_ctx = NULL;
    // 2 初始化过滤器上下文
    av_bsf_alloc(bsfilter, &bsf_ctx); //AVBSFContext;
    // 3 添加解码器属性
    avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[videoindex]->codecpar);
    av_bsf_init(bsf_ctx);

    file_end = 0;
    while (0 == file_end)
    {
        if((ret = av_read_frame(ifmt_ctx, pkt)) < 0)
        {
            // 没有更多包可读
            file_end = 1;
            printf("read file end: ret:%d\n", ret);
        }
        if(ret == 0 && pkt->stream_index == videoindex)
        {
#if 0
            int input_size = pkt->size;
            int out_pkt_count = 0;
            if (av_bsf_send_packet(bsf_ctx, pkt) != 0) // bitstreamfilter内部去维护内存空间
            {
                av_packet_unref(pkt);   // 你不用了就把资源释放掉
                continue;       // 继续送
            }
            av_packet_unref(pkt);   // 释放资源
            while(av_bsf_receive_packet(bsf_ctx, pkt) == 0)
            {
                out_pkt_count++;
                // printf("fwrite size:%d\n", pkt->size);
                size_t size = fwrite(pkt->data, 1, pkt->size, outfp);
                if(size != pkt->size)
                {
                    printf("fwrite failed-> write:%u, pkt_size:%u\n", size, pkt->size);
                }
                av_packet_unref(pkt);
            }
            if(out_pkt_count >= 2)
            {
                printf("cur pkt(size:%d) only get 1 out pkt, it get %d pkts\n",
                       input_size, out_pkt_count);
            }
#else       // TS流可以直接写入
            size_t size = fwrite(pkt->data, 1, pkt->size, outfp);
            if(size != pkt->size)
            {
                printf("fwrite failed-> write:%u, pkt_size:%u\n", size, pkt->size);
            }
            av_packet_unref(pkt);
#endif
        }
        else
        {
            if(ret == 0)
                av_packet_unref(pkt);        // 释放内存
        }
    }
    if(outfp)
        fclose(outfp);
    if(bsf_ctx)
        av_bsf_free(&bsf_ctx);
    if(pkt)
        av_packet_free(&pkt);
    if(ifmt_ctx)
        avformat_close_input(&ifmt_ctx);
    printf("finish\n");

    return 0;
}

四、flv解封装

(1)FlvMetaData.cpp

#include "FlvMetaData.h"

FlvMetaData::FlvMetaData(uint8_t *meta, unsigned int length) {

    m_meta = meta;
    m_length = length;

    m_duration = 0;
    m_width = 0;
    m_height = 0;
    m_framerate = 0;
    m_videodatarate = 0;
    m_audiodatarate = 0;
    m_videocodecid = 0;
    m_audiosamplerate = 0;
    m_audiosamplesize = 0;
    m_stereo = false;

    parseMeta();
}

FlvMetaData::FlvMetaData(const FlvMetaData& r) {

    m_length = r.m_length;
    m_meta = new uint8_t[m_length];
    memcpy(m_meta, r.m_meta, m_length);

    m_duration = r.m_duration;
    m_width = r.m_width;
    m_height = r.m_height;
    m_framerate = r.m_framerate;
    m_videodatarate = r.m_videodatarate;
    m_audiodatarate = r.m_audiodatarate;
    m_videocodecid = r.m_videocodecid;
    m_audiosamplerate = r.m_audiosamplerate;
    m_audiosamplesize = r.m_audiosamplesize;
    m_stereo = r.m_stereo;
}

FlvMetaData&  FlvMetaData::operator=(const FlvMetaData& r) {

    if(this == &r) {
        return *this;
    }

    if(m_meta != NULL) {
        delete m_meta;
    }

    m_length = r.m_length;
    m_meta = new uint8_t[m_length];
    memcpy(m_meta, r.m_meta, m_length);

    m_duration = r.m_duration;
    m_width = r.m_width;
    m_height = r.m_height;
    m_framerate = r.m_framerate;
    m_videodatarate = r.m_videodatarate;
    m_audiodatarate = r.m_audiodatarate;
    m_videocodecid = r.m_videocodecid;
    m_audiosamplerate = r.m_audiosamplerate;
    m_audiosamplesize = r.m_audiosamplesize;
    m_stereo = r.m_stereo;

    return *this;
}

FlvMetaData::~FlvMetaData() {

    if(m_meta != NULL) {
        delete m_meta;
        m_meta = NULL;
    }
}

void FlvMetaData::parseMeta() {

    unsigned int arrayLen = 0;
    unsigned int offset = TAG_HEAD_LEN + 13;

    unsigned int nameLen = 0;
    double numValue = 0;
    bool boolValue = false;

    if(m_meta[offset++] == 0x08) {

        arrayLen |= m_meta[offset++];
        arrayLen = arrayLen << 8;
        arrayLen |= m_meta[offset++];
        arrayLen = arrayLen << 8;
        arrayLen |= m_meta[offset++];
        arrayLen = arrayLen << 8;
        arrayLen |= m_meta[offset++];

        //cerr << "ArrayLen = " << arrayLen << endl;
    } else {
        //TODO:
        cerr << "metadata format error!!!" << endl;
        return ;
    }

    for(unsigned int i = 0; i < arrayLen; i++) {

        numValue = 0;
        boolValue = false;

        nameLen = 0;
        nameLen |= m_meta[offset++];
        nameLen = nameLen << 8;
        nameLen |= m_meta[offset++];
        //cerr << "name length=" << nameLen << " ";

        char name[nameLen + 1];
#if DEBUG
        printf("\norign \n");
        for(unsigned int i = 0; i < nameLen + 3; i++) {
            printf("%x ", m_meta[offset + i]);
        }
        printf("\n");
#endif

        memset(name, 0, sizeof(name));
        memcpy(name, &m_meta[offset], nameLen);
        name[nameLen + 1] = '\0';
        offset += nameLen;
        //cerr << "name=" << name << " ";
#if DEBUG
        printf("memcpy\n");
        for(unsigned int i = 0; i < nameLen; i++) {
            printf("%x ", name[i]);
        }
        printf("\n");
#endif
        switch(m_meta[offset++]) {
        case 0x0: //Number type

            numValue = hexStr2double(&m_meta[offset], 8);
            offset += 8;
            break;

        case 0x1: //Boolean type
            if(offset++ != 0x00) {
                boolValue = true;
            }
            break;

        case 0x2: //String type
            nameLen = 0;
            nameLen |= m_meta[offset++];
            nameLen = nameLen << 8;
            nameLen |= m_meta[offset++];
            offset += nameLen;
            break;

        case 0x12: //Long string type
            nameLen = 0;
            nameLen |= m_meta[offset++];
            nameLen = nameLen << 8;
            nameLen |= m_meta[offset++];
            nameLen = nameLen << 8;
            nameLen |= m_meta[offset++];
            nameLen = nameLen << 8;
            nameLen |= m_meta[offset++];
            offset += nameLen;
            break;

        //FIXME:
        default:
            break;
        }

        if(strncmp(name, "duration", 8)	== 0) {
            m_duration = numValue;
        } else if(strncmp(name, "width", 5)	== 0) {
            m_width = numValue;
        } else if(strncmp(name, "height", 6) == 0) {
            m_height = numValue;
        } else if(strncmp(name, "framerate", 9) == 0) {
            m_framerate = numValue;
        } else if(strncmp(name, "videodatarate", 13) == 0) {
            m_videodatarate = numValue;
        } else if(strncmp(name, "audiodatarate", 13) == 0) {
            m_audiodatarate = numValue;
        } else if(strncmp(name, "videocodecid", 12) == 0) {
            m_videocodecid = numValue;
        } else if(strncmp(name, "audiosamplerate", 15) == 0) {
            m_audiosamplerate = numValue;
        } else if(strncmp(name, "audiosamplesize", 15) == 0) {
            m_audiosamplesize = numValue;
        } else if(strncmp(name, "audiocodecid", 12) == 0) {
            m_audiocodecid = numValue;
        } else if(strncmp(name, "stereo", 6) == 0) {
            m_stereo = boolValue;
        }

    }
}

double FlvMetaData::hexStr2double(const uint8_t* hex, const unsigned int length) {

    double ret = 0;
    char hexstr[length * 2];
    memset(hexstr, 0, sizeof(hexstr));

    for(unsigned int i = 0; i < length; i++) {
        sprintf(hexstr + i * 2, "%02x", hex[i]);
    }

    sscanf(hexstr, "%llx", (unsigned long long*)&ret);

    return ret;
}

double FlvMetaData::getDuration() {
    return m_duration;
}

double FlvMetaData::getWidth() {
    return m_width;
}

double FlvMetaData::getHeight() {
    return m_height;
}

double FlvMetaData::getFramerate() {
    return m_framerate;
}

double FlvMetaData::getVideoDatarate() {
    return m_videodatarate;
}

double FlvMetaData::getAudioDatarate() {
    return m_audiodatarate;
}

double FlvMetaData::getVideoCodecId() {
    return m_videocodecid;
}

double FlvMetaData::getAudioSamplerate() {
    return m_audiosamplerate;
}

double FlvMetaData::getAudioSamplesize() {
    return m_audiosamplesize;
}

double FlvMetaData::getAudioCodecId() {
    return m_audiocodecid;
}

bool FlvMetaData::getStereo() {
    return m_stereo;
}

(2) FlvMetaData.h

#ifndef FLVMETADATA_H
#define FLVMETADATA_H

#include <cstdint>

class FlvMetaData
{
    public:
    FlvMetaData(uint8_t *meta, uint32_t length);
    ~FlvMetaData();

    FlvMetaData(const FlvMetaData&);
    FlvMetaData& operator=(const FlvMetaData&);

    double getDuration();
    double getWidth();
    double getHeight();
    double getFramerate();
    double getVideoDatarate();
    double getAudioDatarate();
    double getVideoCodecId();
    double getAudioCodecId();
    double getAudioSamplerate();
    double getAudioSamplesize();
    bool getStereo();

private:
    //convert HEX to double
    double hexStr2double(const uint8_t* hex, const uint32_t length);
    void parseMeta();

private:
    uint8_t *m_meta;
    uint32_t m_length;

    double m_duration;
    double m_width;
    double m_height;
    double m_framerate;
    double m_videodatarate;
    double m_audiodatarate;
    double m_videocodecid;
    double m_audiocodecid;
    double m_audiosamplerate;
    double m_audiosamplesize;

    bool m_stereo;
};

#endif // FLVMETADATA_H

(3)FlvPrase.cpp

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <iostream>
#include <fstream>

#include "FlvParser.h"

using namespace std;

#define CheckBuffer(x) { if ((nBufSize-nOffset)<(x)) { nUsedLen = nOffset; return 0;} }

int CFlvParser::CAudioTag::_aacProfile;
int CFlvParser::CAudioTag::_sampleRateIndex;
int CFlvParser::CAudioTag::_channelConfig;

static const uint32_t nH264StartCode = 0x01000000;

CFlvParser::CFlvParser()
{
    _pFlvHeader = NULL;
    _vjj = new CVideojj();
}

CFlvParser::~CFlvParser()
{
    for (int i = 0; i < _vpTag.size(); i++)
    {
        DestroyTag(_vpTag[i]);
        delete _vpTag[i];
    }
    if (_vjj != NULL)
        delete _vjj;
}

int CFlvParser::Parse(uint8_t *pBuf, int nBufSize, int &nUsedLen)
{
    int nOffset = 0;

    if (_pFlvHeader == 0)
    {
        CheckBuffer(9);
        _pFlvHeader = CreateFlvHeader(pBuf+nOffset);
        nOffset += _pFlvHeader->nHeadSize;
    }

    while (1)
    {
        CheckBuffer(15); // nPrevSize(4字节) + Tag header(11字节)
        int nPrevSize = ShowU32(pBuf + nOffset);
        nOffset += 4;

        Tag *pTag = CreateTag(pBuf + nOffset, nBufSize-nOffset);
        if (pTag == NULL)
        {
            nOffset -= 4;
            break;
        }
        nOffset += (11 + pTag->_header.nDataSize);

        _vpTag.push_back(pTag);
    }

    nUsedLen = nOffset;
    return 0;
}

int CFlvParser::PrintInfo()
{
    Stat();

    cout << "vnum: " << _sStat.nVideoNum << " , anum: " << _sStat.nAudioNum << " , mnum: " << _sStat.nMetaNum << endl;
    cout << "maxTimeStamp: " << _sStat.nMaxTimeStamp << " ,nLengthSize: " << _sStat.nLengthSize << endl;
    cout << "Vjj SEI num: " << _vjj->_vVjjSEI.size() << endl;
    for (int i = 0; i < _vjj->_vVjjSEI.size(); i++)
        cout << "SEI time : " << _vjj->_vVjjSEI[i].nTimeStamp << endl;
    return 1;
}

int CFlvParser::DumpH264(const std::string &path)
{
    fstream f;
    f.open(path.c_str(), ios_base::out|ios_base::binary);

    vector<Tag *>::iterator it_tag;
    for (it_tag = _vpTag.begin(); it_tag != _vpTag.end(); it_tag++)
    {
        if ((*it_tag)->_header.nType != 0x09)
            continue;

        f.write((char *)(*it_tag)->_pMedia, (*it_tag)->_nMediaLen);
    }
    f.close();

    return 1;
}

int CFlvParser::DumpAAC(const std::string &path)
{
    fstream f;
    f.open(path.c_str(), ios_base::out | ios_base::binary);

    vector<Tag *>::iterator it_tag;
    for (it_tag = _vpTag.begin(); it_tag != _vpTag.end(); it_tag++)
    {
        if ((*it_tag)->_header.nType != 0x08)
            continue;

        CAudioTag *pAudioTag = (CAudioTag *)(*it_tag);
        if (pAudioTag->_nSoundFormat != 10)
            continue;

        if (pAudioTag->_nMediaLen!=0)
            f.write((char *)(*it_tag)->_pMedia, (*it_tag)->_nMediaLen);
    }
    f.close();

    return 1;
}

int CFlvParser::DumpFlv(const std::string &path)
{
    fstream f;
    f.open(path.c_str(), ios_base::out | ios_base::binary);

    // write flv-header
    f.write((char *)_pFlvHeader->pFlvHeader, _pFlvHeader->nHeadSize);
    uint32_t nLastTagSize = 0;


    // write flv-tag
    vector<Tag *>::iterator it_tag;
    for (it_tag = _vpTag.begin(); it_tag < _vpTag.end(); it_tag++)
    {
        uint32_t nn = WriteU32(nLastTagSize);
        f.write((char *)&nn, 4);

        //check duplicate start code
        if ((*it_tag)->_header.nType == 0x09 && *((*it_tag)->_pTagData + 1) == 0x01) {
            bool duplicate = false;
            uint8_t *pStartCode = (*it_tag)->_pTagData + 5 + _nNalUnitLength;
            //printf("tagsize=%d\n",(*it_tag)->_header.nDataSize);
            unsigned nalu_len = 0;
            uint8_t *p_nalu_len=(uint8_t *)&nalu_len;
            switch (_nNalUnitLength) {
            case 4:
                nalu_len = ShowU32((*it_tag)->_pTagData + 5);
                break;
            case 3:
                nalu_len = ShowU24((*it_tag)->_pTagData + 5);
                break;
            case 2:
                nalu_len = ShowU16((*it_tag)->_pTagData + 5);
                break;
            default:
                nalu_len = ShowU8((*it_tag)->_pTagData + 5);
                break;
            }
            /*
            printf("nalu_len=%u\n",nalu_len);
            printf("%x,%x,%x,%x,%x,%x,%x,%x,%x\n",(*it_tag)->_pTagData[5],(*it_tag)->_pTagData[6],
                    (*it_tag)->_pTagData[7],(*it_tag)->_pTagData[8],(*it_tag)->_pTagData[9],
                    (*it_tag)->_pTagData[10],(*it_tag)->_pTagData[11],(*it_tag)->_pTagData[12],
                    (*it_tag)->_pTagData[13]);
            */

            uint8_t *pStartCodeRecord = pStartCode;
            int i;
            for (i = 0; i < (*it_tag)->_header.nDataSize - 5 - _nNalUnitLength - 4; ++i) {
                if (pStartCode[i] == 0x00 && pStartCode[i+1] == 0x00 && pStartCode[i+2] == 0x00 &&
                        pStartCode[i+3] == 0x01) {
                    if (pStartCode[i+4] == 0x67) {
                        //printf("duplicate sps found!\n");
                        i += 4;
                        continue;
                    }
                    else if (pStartCode[i+4] == 0x68) {
                        //printf("duplicate pps found!\n");
                        i += 4;
                        continue;
                    }
                    else if (pStartCode[i+4] == 0x06) {
                        //printf("duplicate sei found!\n");
                        i += 4;
                        continue;
                    }
                    else {
                        i += 4;
                        //printf("offset=%d\n",i);
                        duplicate = true;
                        break;
                    }
                }
            }

            if (duplicate) {
                nalu_len -= i;
                (*it_tag)->_header.nDataSize -= i;
                uint8_t *p = (uint8_t *)&((*it_tag)->_header.nDataSize);
                (*it_tag)->_pTagHeader[1] = p[2];
                (*it_tag)->_pTagHeader[2] = p[1];
                (*it_tag)->_pTagHeader[3] = p[0];
                //printf("after,tagsize=%d\n",(int)ShowU24((*it_tag)->_pTagHeader + 1));
                //printf("%x,%x,%x\n",(*it_tag)->_pTagHeader[1],(*it_tag)->_pTagHeader[2],(*it_tag)->_pTagHeader[3]);

                f.write((char *)(*it_tag)->_pTagHeader, 11);
                switch (_nNalUnitLength) {
                case 4:
                    *((*it_tag)->_pTagData + 5) = p_nalu_len[3];
                    *((*it_tag)->_pTagData + 6) = p_nalu_len[2];
                    *((*it_tag)->_pTagData + 7) = p_nalu_len[1];
                    *((*it_tag)->_pTagData + 8) = p_nalu_len[0];
                    break;
                case 3:
                    *((*it_tag)->_pTagData + 5) = p_nalu_len[2];
                    *((*it_tag)->_pTagData + 6) = p_nalu_len[1];
                    *((*it_tag)->_pTagData + 7) = p_nalu_len[0];
                    break;
                case 2:
                    *((*it_tag)->_pTagData + 5) = p_nalu_len[1];
                    *((*it_tag)->_pTagData + 6) = p_nalu_len[0];
                    break;
                default:
                    *((*it_tag)->_pTagData + 5) = p_nalu_len[0];
                    break;
                }
                //printf("after,nalu_len=%d\n",(int)ShowU32((*it_tag)->_pTagData + 5));
                f.write((char *)(*it_tag)->_pTagData, pStartCode - (*it_tag)->_pTagData);
                /*
                printf("%x,%x,%x,%x,%x,%x,%x,%x,%x\n",(*it_tag)->_pTagData[0],(*it_tag)->_pTagData[1],(*it_tag)->_pTagData[2],
                        (*it_tag)->_pTagData[3],(*it_tag)->_pTagData[4],(*it_tag)->_pTagData[5],(*it_tag)->_pTagData[6],
                        (*it_tag)->_pTagData[7],(*it_tag)->_pTagData[8]);
                */
                f.write((char *)pStartCode + i, (*it_tag)->_header.nDataSize - (pStartCode - (*it_tag)->_pTagData));
                /*
                printf("write size:%d\n", (pStartCode - (*it_tag)->_pTagData) +
                        ((*it_tag)->_header.nDataSize - (pStartCode - (*it_tag)->_pTagData)));
                */
            } else {
                f.write((char *)(*it_tag)->_pTagHeader, 11);
                f.write((char *)(*it_tag)->_pTagData, (*it_tag)->_header.nDataSize);
            }
        } else {
            f.write((char *)(*it_tag)->_pTagHeader, 11);
            f.write((char *)(*it_tag)->_pTagData, (*it_tag)->_header.nDataSize);
        }

        nLastTagSize = 11 + (*it_tag)->_header.nDataSize;
    }
    uint32_t nn = WriteU32(nLastTagSize);
    f.write((char *)&nn, 4);

    f.close();

    return 1;
}

int CFlvParser::Stat()
{
    for (int i = 0; i < _vpTag.size(); i++)
    {
        switch (_vpTag[i]->_header.nType)
        {
        case 0x08:
            _sStat.nAudioNum++;
            break;
        case 0x09:
            StatVideo(_vpTag[i]);
            break;
        case 0x12:
            _sStat.nMetaNum++;
            break;
        default:
            ;
        }
    }

    return 1;
}

int CFlvParser::StatVideo(Tag *pTag)
{
    _sStat.nVideoNum++;
    _sStat.nMaxTimeStamp = pTag->_header.nTimeStamp;

    if (pTag->_pTagData[0] == 0x17 && pTag->_pTagData[1] == 0x00)
    {
        _sStat.nLengthSize = (pTag->_pTagData[9] & 0x03) + 1;
    }

    return 1;
}

CFlvParser::FlvHeader *CFlvParser::CreateFlvHeader(uint8_t *pBuf)
{
    FlvHeader *pHeader = new FlvHeader;
    pHeader->nVersion = pBuf[3];        // 版本号
    pHeader->bHaveAudio = (pBuf[4] >> 2) & 0x01;    // 是否有音频
    pHeader->bHaveVideo = (pBuf[4] >> 0) & 0x01;    // 是否有视频
    pHeader->nHeadSize = ShowU32(pBuf + 5);         // 头部长度

    pHeader->pFlvHeader = new uint8_t[pHeader->nHeadSize];
    memcpy(pHeader->pFlvHeader, pBuf, pHeader->nHeadSize);

    return pHeader;
}

int CFlvParser::DestroyFlvHeader(FlvHeader *pHeader)
{
    if (pHeader == NULL)
        return 0;

    delete pHeader->pFlvHeader;
    return 1;
}

/**
 * @brief 复制header + body
 * @param pHeader
 * @param pBuf
 * @param nLeftLen
 */
void CFlvParser::Tag::Init(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen)
{
    memcpy(&_header, pHeader, sizeof(TagHeader));
    // 复制标签头部信息 header
    _pTagHeader = new uint8_t[11];
    memcpy(_pTagHeader, pBuf, 11);      // 头部
    // 复制标签 body
    _pTagData = new uint8_t[_header.nDataSize];
    memcpy(_pTagData, pBuf + 11, _header.nDataSize);
}

CFlvParser::CVideoTag::CVideoTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser)
{
    // 初始化
    Init(pHeader, pBuf, nLeftLen);

    uint8_t *pd = _pTagData;
    _nFrameType = (pd[0] & 0xf0) >> 4;  // 帧类型
    _nCodecID = pd[0] & 0x0f;           // 视频编码类型
    // 开始解析
    if (_header.nType == 0x09 && _nCodecID == 7)
    {
        ParseH264Tag(pParser);
    }
}

/**
 * @brief  CAudioTag 音频Tag Data区域开始的第一个字节包含了音频数据的参数信息,
 * 从第二个字节开始为音频流数据,但第二个字节对于AAC也要判断是AAC sequence header还是AAC raw。
 * 第一个字节:SoundFormat 4bit 音频格式 0 = Linear PCM, platform endian
                        1 =ADPCM; 2 = MP3; 3 = Linear PCM, little endian
                        4 = Nellymoser 16-kHz mono ; 5 = Nellymoser 8-kHz mono
                        6 = Nellymoser;  7 = G.711 A-law logarithmic PCM
                        8 = G.711 mu-law logarithmic PCM; 9 = reserved
                        10 = AAC ; 11  Speex 14 = MP3 8-Khz
                        15 = Device-specific sound
              SoundRate 2bit 采样率 0 = 5.5-kHz; 1 = 11-kHz; 2 = 22-kHz; 3 = 44-kHz
                        对于AAC总是3。但实际上AAC是可以支持到48khz以上的频率。
              SoundSize 1bit 采样精度  0 = snd8Bit; 1 = snd16Bit
                        此参数仅适用于未压缩的格式,压缩后的格式都是将其设为1
              SoundType 1bit  0 = sndMono 单声道; 1 = sndStereo 立体声,双声道
                        对于AAC总是1
If the SoundFormat indicates AAC, the SoundType should be set to 1 (stereo) and the
SoundRate should be set to 3 (44 kHz). However, this does not mean that AAC audio in FLV
is always stereo, 44 kHz data. Instead, the Flash Player ignores these values and
extracts the channel and sample rate data is encoded in the AAC bitstream.
 * @param pHeader
 * @param pBuf
 * @param nLeftLen
 * @param pParser
 */
CFlvParser::CAudioTag::CAudioTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser)
{
    Init(pHeader, pBuf, nLeftLen);

    uint8_t *pd = _pTagData;
    _nSoundFormat = (pd[0] & 0xf0) >> 4;    // 音频格式
    _nSoundRate = (pd[0] & 0x0c) >> 2;      // 采样率
    _nSoundSize = (pd[0] & 0x02) >> 1;      // 采样精度
    _nSoundType = (pd[0] & 0x01);           // 是否立体声
    if (_nSoundFormat == 10)                // AAC
    {
        ParseAACTag(pParser);
    }
}

int CFlvParser::CAudioTag::ParseAACTag(CFlvParser *pParser)
{
    uint8_t *pd = _pTagData;

    // 数据包的类型:音频配置信息,音频数据
    int nAACPacketType = pd[1];

    // 如果是音频配置信息
    if (nAACPacketType == 0)    // AAC sequence header
    {
        // 解析配置信息
        ParseAudioSpecificConfig(pParser, pd); // 解析AudioSpecificConfig
    }
    // 如果是音频数据
    else if (nAACPacketType == 1)   // AAC RAW
    {
        // 解析音频数据
        ParseRawAAC(pParser, pd);
    }
    else
    {

    }

    return 1;
}

int CFlvParser::CAudioTag::ParseAudioSpecificConfig(CFlvParser *pParser, uint8_t *pTagData)
{
    uint8_t *pd = _pTagData;

    _aacProfile = ((pd[2]&0xf8)>>3);    // 5bit AAC编码级别
    _sampleRateIndex = ((pd[2]&0x07)<<1) | (pd[3]>>7);  // 4bit 真正的采样率索引
    _channelConfig = (pd[3]>>3) & 0x0f;                 // 4bit 通道数量
    printf("----- AAC ------\n");
    printf("profile:%d\n", _aacProfile);
    printf("sample rate index:%d\n", _sampleRateIndex);
    printf("channel config:%d\n", _channelConfig);

    _pMedia = NULL;
    _nMediaLen = 0;

    return 1;
}

int CFlvParser::CAudioTag::ParseRawAAC(CFlvParser *pParser, uint8_t *pTagData)
{
    uint64_t bits = 0;  // 占用8字节
    // 数据长度 跳过tag data的第一个第二字节
    int dataSize = _header.nDataSize - 2;   // 减去两字节的 audio tag data信息部分

    // 制作元数据
    WriteU64(bits, 12, 0xFFF);
    WriteU64(bits, 1, 0);
    WriteU64(bits, 2, 0);
    WriteU64(bits, 1, 1);
    WriteU64(bits, 2, _aacProfile - 1);
    WriteU64(bits, 4, _sampleRateIndex);
    WriteU64(bits, 1, 0);
    WriteU64(bits, 3, _channelConfig);
    WriteU64(bits, 1, 0);
    WriteU64(bits, 1, 0);
    WriteU64(bits, 1, 0);
    WriteU64(bits, 1, 0);
    WriteU64(bits, 13, 7 + dataSize);
    WriteU64(bits, 11, 0x7FF);
    WriteU64(bits, 2, 0);
    // WriteU64执行为上述的操作,最高的8bit还没有被移位到,实际是使用7个字节
    _nMediaLen = 7 + dataSize;
    _pMedia = new uint8_t[_nMediaLen];
    uint8_t p64[8];
    p64[0] = (uint8_t)(bits >> 56); // 是bits的最高8bit,实际为0
    p64[1] = (uint8_t)(bits >> 48); // 才是ADTS起始头 0xfff的高8bit
    p64[2] = (uint8_t)(bits >> 40);
    p64[3] = (uint8_t)(bits >> 32);
    p64[4] = (uint8_t)(bits >> 24);
    p64[5] = (uint8_t)(bits >> 16);
    p64[6] = (uint8_t)(bits >> 8);
    p64[7] = (uint8_t)(bits);

    memcpy(_pMedia, p64+1, 7);  // ADTS header, p64+1是从ADTS起始头开始
    memcpy(_pMedia + 7, pTagData + 2, dataSize); // AAC body

    return 1;
}

CFlvParser::CMetaDataTag::CMetaDataTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser)
{
    Init(pHeader, pBuf, nLeftLen);

    uint8_t *pd = _pTagData;
    m_amf1_type = ShowU8(pd+0);
    m_amf1_size = ShowU16(pd+1);

    if(m_amf1_type != 2)
    {
        printf("no metadata\n");
        return;
    }
    // 解析script
    if(strncmp((const char *)"onMetaData", (const char *)(pd + 3), 10) == 0)
        parseMeta(pParser);
}
double CFlvParser::CMetaDataTag::hexStr2double(const uint8_t* hex,
                                               const uint32_t length) {

    double ret = 0;
    char hexstr[length * 2];
    memset(hexstr, 0, sizeof(hexstr));

    for(uint32_t i = 0; i < length; i++)
    {
        sprintf(hexstr + i * 2, "%02x", hex[i]);
    }

    sscanf(hexstr, "%llx", (unsigned long long*)&ret);

    return ret;
}
int CFlvParser::CMetaDataTag::parseMeta(CFlvParser *pParser)
{
    uint8_t *pd = _pTagData;
    int dataSize = _header.nDataSize;

    uint32_t arrayLen = 0;
    uint32_t offset = 13; // Type + Value_Size + Value占用13字节

    uint32_t nameLen = 0;
    double doubleValue = 0;
    string strValue = "";
    bool boolValue = false;
    uint32_t valueLen = 0;
    uint8_t u8Value = 0;
    if(pd[offset++] == 0x08)    // 0x8 onMetaData
    {
        arrayLen = ShowU32(pd + offset);
        offset += 4;    //跳过 [ECMAArrayLength]占用的字节
        printf("ArrayLen = %d\n", arrayLen);
    }
    else
    {
        printf("metadata format error!!!");
        return -1;
    }

    for(uint32_t i = 0; i < arrayLen; i++)
    {

        doubleValue = 0;
        boolValue = false;
        strValue = "";
        // 读取字段长度
        nameLen = ShowU16(pd + offset);
        offset += 2;            // 跳过2字节字段长度

        char name[nameLen + 1]; // 获取字段名称

        memset(name, 0, sizeof(name));
        memcpy(name, &pd[offset], nameLen);
        name[nameLen + 1] = '\0';
        offset += nameLen;      // 跳过字段名占用的长度

        uint8_t amfType = pd[offset++];
        switch(amfType)    // 判别值的类型
        {
        case 0x0: //Number type, 就是double类型, 占用8字节
            doubleValue = hexStr2double(&pd[offset], 8);
            offset += 8;        // 跳过8字节
            break;

        case 0x1: //Boolean type, 占用1字节
            u8Value = ShowU8(pd+offset);
            offset += 1;        // 跳过1字节
            if(u8Value != 0x00)
                 boolValue = true;
            else
                 boolValue = false;
            break;

        case 0x2: //String type
            valueLen = ShowU16(pd + offset);
            offset += 2;    // 跳过2字节 length
            strValue.append(pd + offset, pd + offset + valueLen);
            strValue.append("");
            offset += valueLen; // 跳过字段的值的长度
            break;

        default:
            printf("un handle amfType:%d\n", amfType);
            break;
        }

        if(strncmp(name, "duration", 8)	== 0)
        {
            m_duration = doubleValue;
        }
        else if(strncmp(name, "width", 5)	== 0)
        {
            m_width = doubleValue;
        }
        else if(strncmp(name, "height", 6) == 0)
        {
            m_height = doubleValue;
        }
        else if(strncmp(name, "videodatarate", 13) == 0)
        {
            m_videodatarate = doubleValue;
        }
        else if(strncmp(name, "framerate", 9) == 0)
        {
            m_framerate = doubleValue;
        }
        else if(strncmp(name, "videocodecid", 12) == 0)
        {
            m_videocodecid = doubleValue;
        }
        else if(strncmp(name, "audiodatarate", 13) == 0)
        {
            m_audiodatarate = doubleValue;
        }
        else if(strncmp(name, "audiosamplerate", 15) == 0)
        {
            m_audiosamplerate = doubleValue;
        }
        else if(strncmp(name, "audiosamplesize", 15) == 0)
        {
            m_audiosamplesize = doubleValue;
        }
        else if(strncmp(name, "stereo", 6) == 0)
        {
            m_stereo = boolValue;
        }
        else if(strncmp(name, "audiocodecid", 12) == 0)
        {
            m_audiocodecid = doubleValue;
        }
        else if(strncmp(name, "major_brand", 11) == 0)
        {
            m_major_brand = strValue;
        }
        else if(strncmp(name, "minor_version", 13) == 0)
        {
            m_minor_version = strValue;
        }
        else if(strncmp(name, "compatible_brands", 17) == 0)
        {
            m_compatible_brands = strValue;
        }
        else if(strncmp(name, "encoder", 7) == 0)
        {
            m_encoder = strValue;
        }
        else if(strncmp(name, "filesize", 8) == 0)
        {
            m_filesize = doubleValue;
        }
    }


    printMeta();
    return 1;
}

void CFlvParser::CMetaDataTag::printMeta()
{
    printf("\nduration: %0.2lfs, filesize: %.0lfbytes\n", m_duration, m_filesize);

    printf("width: %0.0lf, height: %0.0lf\n", m_width, m_height);
    printf("videodatarate: %0.2lfkbps, framerate: %0.0lffps\n", m_videodatarate, m_framerate);
    printf("videocodecid: %0.0lf\n", m_videocodecid);

    printf("audiodatarate: %0.2lfkbps, audiosamplerate: %0.0lfKhz\n",
           m_audiodatarate, m_audiosamplerate);
    printf("audiosamplesize: %0.0lfbit, stereo: %d\n", m_audiosamplesize, m_stereo);
    printf("audiocodecid: %0.0lf\n", m_audiocodecid);

    printf("major_brand: %s, minor_version: %s\n", m_major_brand.c_str(), m_minor_version.c_str());
    printf("compatible_brands: %s, encoder: %s\n\n", m_compatible_brands.c_str(), m_encoder.c_str());
}

CFlvParser::Tag *CFlvParser::CreateTag(uint8_t *pBuf, int nLeftLen)
{
    // 开始解析标签头部
    TagHeader header;
    header.nType = ShowU8(pBuf+0);  // 类型
    header.nDataSize = ShowU24(pBuf + 1);   // 标签body的长度
    header.nTimeStamp = ShowU24(pBuf + 4);  // 时间戳 低24bit
    header.nTSEx = ShowU8(pBuf + 7);        // 时间戳的扩展字段, 高8bit
    header.nStreamID = ShowU24(pBuf + 8);   // 流的id
    header.nTotalTS = (uint32_t)((header.nTSEx << 24)) + header.nTimeStamp;
    // 标签头部解析结束

//    cout << "total TS : " << header.nTotalTS << endl;
//    cout << "nLeftLen : " << nLeftLen << " , nDataSize : " << header.nDataSize << endl;
    if ((header.nDataSize + 11) > nLeftLen)
    {
        return NULL;
    }

    Tag *pTag;
    switch (header.nType) {
    case 0x09:  // 视频类型的Tag
        pTag = new CVideoTag(&header, pBuf, nLeftLen, this);
        break;
    case 0x08:  // 音频类型的Tag
        pTag = new CAudioTag(&header, pBuf, nLeftLen, this);
        break;
    case 0x12:  // script Tag
        pTag = new CMetaDataTag(&header, pBuf, nLeftLen, this);
        break;
    default:    // script类型的Tag
        pTag = new Tag();
        pTag->Init(&header, pBuf, nLeftLen);
    }

    return pTag;
}

int CFlvParser::DestroyTag(Tag *pTag)
{
    if (pTag->_pMedia != NULL)
        delete []pTag->_pMedia;
    if (pTag->_pTagData!=NULL)
        delete []pTag->_pTagData;
    if (pTag->_pTagHeader != NULL)
        delete []pTag->_pTagHeader;

    return 1;
}

int CFlvParser::CVideoTag::ParseH264Tag(CFlvParser *pParser)
{
    uint8_t *pd = _pTagData;
    /*
    ** 数据包的类型
    ** 视频数据被压缩之后被打包成数据包在网上传输
    ** 有两种类型的数据包:视频信息包(sps、pps等)和视频数据包(视频的压缩数据)
    */
    int nAVCPacketType = pd[1];
    int nCompositionTime = CFlvParser::ShowU24(pd + 2);

    // 如果是视频配置信息
    if (nAVCPacketType == 0)    // AVC sequence header
    {
        ParseH264Configuration(pParser, pd);
    }
    // 如果是视频数据
    else if (nAVCPacketType == 1) // AVC NALU
    {
        ParseNalu(pParser, pd);
    }
    else
    {

    }
    return 1;
}
/**
 * @brief
AVCDecoderConfigurationRecord {
    uint32_t(8) configurationVersion = 1;  [0]
    uint32_t(8) AVCProfileIndication;       [1]
    uint32_t(8) profile_compatibility;      [2]
    uint32_t(8) AVCLevelIndication;         [3]
    bit(6) reserved = ‘111111’b;            [4]
    uint32_t(2) lengthSizeMinusOne;         [4] 计算方法是 1 + (lengthSizeMinusOne & 3),实际计算结果一直是4
    bit(3) reserved = ‘111’b;                   [5]
    uint32_t(5) numOfSequenceParameterSets; [5] SPS 的个数,计算方法是 numOfSequenceParameterSets & 0x1F,实际计算结果一直为1
    for (i=0; i< numOfSequenceParameterSets; i++) {
        uint32_t(16) sequenceParameterSetLength ;   [6,7]
        bit(8*sequenceParameterSetLength) sequenceParameterSetNALUnit;
    }
    uint32_t(8) numOfPictureParameterSets;      PPS 的个数,一直为1
    for (i=0; i< numOfPictureParameterSets; i++) {
        uint32_t(16) pictureParameterSetLength;
        bit(8*pictureParameterSetLength) pictureParameterSetNALUnit;
    }
}

_nNalUnitLength 这个变量告诉我们用几个字节来存储NALU的长度,如果NALULengthSizeMinusOne是0,
那么每个NALU使用一个字节的前缀来指定长度,那么每个NALU包的最大长度是255字节,
这个明显太小了,使用2个字节的前缀来指定长度,那么每个NALU包的最大长度是64K字节,
也不一定够,一般分辨率达到1280*720 的图像编码出的I帧,可能大于64K;3字节是比较完美的,
但是因为一些原因(例如对齐)没有被广泛支持;因此4字节长度的前缀是目前使用最多的方式
 * @param pParser
 * @param pTagData
 * @return
 */
int CFlvParser::CVideoTag::ParseH264Configuration(CFlvParser *pParser, uint8_t *pTagData)
{
    uint8_t *pd = pTagData;
    // 跨过 Tag Data的VIDEODATA(1字节) AVCVIDEOPACKET(AVCPacketType(1字节) 和CompositionTime(3字节) 4字节)
    // 总共跨过5个字节

    // NalUnit长度表示占用的字节
    pParser->_nNalUnitLength = (pd[9] & 0x03) + 1;  // lengthSizeMinusOne 9 = 5 + 4

    int sps_size, pps_size;
    // sps(序列参数集)的长度
    sps_size = CFlvParser::ShowU16(pd + 11);        // sequenceParameterSetLength 11 = 5 + 6
    // pps(图像参数集)的长度
    pps_size = CFlvParser::ShowU16(pd + 11 + (2 + sps_size) + 1);   // pictureParameterSetLength

    // 元数据的长度
    _nMediaLen = 4 + sps_size + 4 + pps_size;   // 添加start code
    _pMedia = new uint8_t[_nMediaLen];
    // 保存元数据
    memcpy(_pMedia, &nH264StartCode, 4);
    memcpy(_pMedia + 4, pd + 11 + 2, sps_size);
    memcpy(_pMedia + 4 + sps_size, &nH264StartCode, 4);
    memcpy(_pMedia + 4 + sps_size + 4, pd + 11 + 2 + sps_size + 2 + 1, pps_size);

    return 1;
}

int CFlvParser::CVideoTag::ParseNalu(CFlvParser *pParser, uint8_t *pTagData)
{
    uint8_t *pd = pTagData;
    int nOffset = 0;

    _pMedia = new uint8_t[_header.nDataSize+10];
    _nMediaLen = 0;
    // 跨过 Tag Data的VIDEODATA(1字节) AVCVIDEOPACKET(AVCPacketType和CompositionTime 4字节)
    nOffset = 5; // 总共跨过5个字节 132 - 5 = 127 = _nNalUnitLength(4字节)  + NALU(123字节)
    //                                           startcode(4字节)  + NALU(123字节) = 127
    while (1)
    {
        // 如果解析完了一个Tag,那么就跳出循环
        if (nOffset >= _header.nDataSize)
            break;
        // 计算NALU(视频数据被包装成NALU在网上传输)的长度,
        // 一个tag可能包含多个nalu, 所以每个nalu前面有NalUnitLength字节表示每个nalu的长度
        int nNaluLen;
        switch (pParser->_nNalUnitLength)
        {
        case 4:
            nNaluLen = CFlvParser::ShowU32(pd + nOffset);
            break;
        case 3:
            nNaluLen = CFlvParser::ShowU24(pd + nOffset);
            break;
        case 2:
            nNaluLen = CFlvParser::ShowU16(pd + nOffset);
            break;
        default:
            nNaluLen = CFlvParser::ShowU8(pd + nOffset);
        }
        // 获取NALU的起始码
        memcpy(_pMedia + _nMediaLen, &nH264StartCode, 4);
        // 复制NALU的数据
        memcpy(_pMedia + _nMediaLen + 4, pd + nOffset + pParser->_nNalUnitLength, nNaluLen);
        // 解析NALU
//        pParser->_vjj->Process(_pMedia+_nMediaLen, 4+nNaluLen, _header.nTotalTS);
        _nMediaLen += (4 + nNaluLen);
        nOffset += (pParser->_nNalUnitLength + nNaluLen);
    }

    return 1;
}

(4) FlvParse.h

#ifndef FLVPARSER_H
#define FLVPARSER_H

#include <iostream>
#include <vector>
#include <stdint.h>
#include "Videojj.h"

using namespace std;

typedef unsigned long long uint64_t;

class CFlvParser
{
public:
    CFlvParser();
    virtual ~CFlvParser();

    int Parse(uint8_t *pBuf, int nBufSize, int &nUsedLen);
    int PrintInfo();
    int DumpH264(const std::string &path);
    int DumpAAC(const std::string &path);
    int DumpFlv(const std::string &path);

private:
    // FLV头
    typedef struct FlvHeader_s
    {
        int nVersion; // 版本
        int bHaveVideo; // 是否包含视频
        int bHaveAudio; // 是否包含音频
        int nHeadSize;  // FLV头部长度
        /*
        ** 指向存放FLV头部的buffer
        ** 上面的三个成员指明了FLV头部的信息,是从FLV的头部中“翻译”得到的,
        ** 真实的FLV头部是一个二进制比特串,放在一个buffer中,由pFlvHeader成员指明
        */
        uint8_t *pFlvHeader;
    } FlvHeader;

    // Tag头部
    struct TagHeader
    {
        int nType;      // 类型
        int nDataSize;  // 标签body的大小
        int nTimeStamp; // 时间戳
        int nTSEx;      // 时间戳的扩展字节
        int nStreamID;  // 流的ID,总是0

        uint32_t nTotalTS;  // 完整的时间戳nTimeStamp和nTSEx拼装

        TagHeader() : nType(0), nDataSize(0), nTimeStamp(0), nTSEx(0), nStreamID(0), nTotalTS(0) {}
        ~TagHeader() {}
    };

    class Tag
    {
    public:
        Tag() : _pTagHeader(NULL), _pTagData(NULL), _pMedia(NULL), _nMediaLen(0) {}
        void Init(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen);

        TagHeader _header;
        uint8_t *_pTagHeader;   // 指向标签头部
        uint8_t *_pTagData;     // 指向标签body,原始的tag data数据
        uint8_t *_pMedia;       // 指向标签的元数据,改造后的数据
        int _nMediaLen;         // 数据长度
    };

    class CVideoTag : public Tag
    {
    public:
        /**
         * @brief CVideoTag
         * @param pHeader
         * @param pBuf 整个tag的起始地址
         * @param nLeftLen
         * @param pParser
         */
        CVideoTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser);

        int _nFrameType;    // 帧类型
        int _nCodecID;      // 视频编解码类型
        int ParseH264Tag(CFlvParser *pParser);
        int ParseH264Configuration(CFlvParser *pParser, uint8_t *pTagData);
        int ParseNalu(CFlvParser *pParser, uint8_t *pTagData);
    };

    class CAudioTag : public Tag
    {
    public:
        CAudioTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser);

        int _nSoundFormat;  // 音频编码类型
        int _nSoundRate;    // 采样率
        int _nSoundSize;    // 精度
        int _nSoundType;    // 类型

        // aac
        static int _aacProfile;     // 对应AAC profile
        static int _sampleRateIndex;    // 采样率索引
        static int _channelConfig;      // 通道设置

        int ParseAACTag(CFlvParser *pParser);
        int ParseAudioSpecificConfig(CFlvParser *pParser, uint8_t *pTagData);
        int ParseRawAAC(CFlvParser *pParser, uint8_t *pTagData);
    };

    class CMetaDataTag : public Tag
    {
    public:
        CMetaDataTag(TagHeader *pHeader, uint8_t *pBuf, int nLeftLen, CFlvParser *pParser);

        double hexStr2double(const unsigned char* hex, const unsigned int length);
        int parseMeta(CFlvParser *pParser);
        void printMeta();

        uint8_t m_amf1_type;
        uint32_t m_amf1_size;
        uint8_t m_amf2_type;
        unsigned char *m_meta;
        unsigned int m_length;

        double m_duration;
        double m_width;
        double m_height;
        double m_videodatarate;
        double m_framerate;
        double m_videocodecid;

        double m_audiodatarate;
        double m_audiosamplerate;
        double m_audiosamplesize;
        bool m_stereo;
        double m_audiocodecid;

        string m_major_brand;
        string m_minor_version;
        string m_compatible_brands;
        string m_encoder;

        double m_filesize;
    };


    struct FlvStat
    {
        int nMetaNum, nVideoNum, nAudioNum;
        int nMaxTimeStamp;
        int nLengthSize;

        FlvStat() : nMetaNum(0), nVideoNum(0), nAudioNum(0), nMaxTimeStamp(0), nLengthSize(0){}
        ~FlvStat() {}
    };



    static uint32_t ShowU32(uint8_t *pBuf) { return (pBuf[0] << 24) | (pBuf[1] << 16) | (pBuf[2] << 8) | pBuf[3]; }
    static uint32_t ShowU24(uint8_t *pBuf) { return (pBuf[0] << 16) | (pBuf[1] << 8) | (pBuf[2]); }
    static uint32_t ShowU16(uint8_t *pBuf) { return (pBuf[0] << 8) | (pBuf[1]); }
    static uint32_t ShowU8(uint8_t *pBuf) { return (pBuf[0]); }
    static void WriteU64(uint64_t & x, int length, int value)
    {
        uint64_t mask = 0xFFFFFFFFFFFFFFFF >> (64 - length);
        x = (x << length) | ((uint64_t)value & mask);
    }
    static uint32_t WriteU32(uint32_t n)
    {
        uint32_t nn = 0;
        uint8_t *p = (uint8_t *)&n;
        uint8_t *pp = (uint8_t *)&nn;
        pp[0] = p[3];
        pp[1] = p[2];
        pp[2] = p[1];
        pp[3] = p[0];
        return nn;
    }

    friend class Tag;

private:

    FlvHeader *CreateFlvHeader(uint8_t *pBuf);
    int DestroyFlvHeader(FlvHeader *pHeader);
    Tag *CreateTag(uint8_t *pBuf, int nLeftLen);
    int DestroyTag(Tag *pTag);
    int Stat();
    int StatVideo(Tag *pTag);
    int IsUserDataTag(Tag *pTag);

private:

    FlvHeader* _pFlvHeader;
    vector<Tag *> _vpTag;
    FlvStat _sStat;
    CVideojj *_vjj;

    // H.264
    int _nNalUnitLength;

};

#endif // FLVPARSER_H

(5)main()

#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <fstream>

#include "FlvParser.h"

using namespace std;

void Process(fstream &fin, const char *filename);

int main(int argc, char *argv[])
{
    cout << "Hi, this is FLV parser test program!\n";

    if (argc != 3)
    {
        cout << "FlvParser.exe [input flv] [output flv]" << endl;
        return 0;
    }

    fstream fin;
    fin.open(argv[1], ios_base::in | ios_base::binary);
    if (!fin)
        return 0;

    Process(fin, argv[2]);

    fin.close();

    return 1;
}

void Process(fstream &fin, const char *filename)
{
    CFlvParser parser;

    int nBufSize = 2*1024 * 1024;
    int nFlvPos = 0;
    uint8_t *pBuf, *pBak;
    pBuf = new uint8_t[nBufSize];
    pBak = new uint8_t[nBufSize];

    while (1)
    {
        int nReadNum = 0;
        int nUsedLen = 0;
        fin.read((char *)pBuf + nFlvPos, nBufSize - nFlvPos);
        nReadNum = fin.gcount();
        if (nReadNum == 0)
            break;
        nFlvPos += nReadNum;

        parser.Parse(pBuf, nFlvPos, nUsedLen);
        if (nFlvPos != nUsedLen)
        {
            memcpy(pBak, pBuf + nUsedLen, nFlvPos - nUsedLen);
            memcpy(pBuf, pBak, nFlvPos - nUsedLen);
        }
        nFlvPos -= nUsedLen;
    }
    parser.PrintInfo();
    parser.DumpH264("parser.264");
    parser.DumpAAC("parser.aac");

    //dump into flv
    parser.DumpFlv(filename);

    delete []pBak;
    delete []pBuf;
}

(6) vadbg.cpp

#include <fstream>

#include "vadbg.h"

using namespace std;

namespace vadbg
{
	void DumpString(std::string path, std::string str)
	{
		ofstream fin;
		fin.open(path.c_str(), ios_base::out);
		fin << str.c_str();
		fin.close();
	}

    void DumpBuffer(std::string path, uint8_t *pBuffer, int nBufSize)
	{
		ofstream fin;
		fin.open(path.c_str(), ios_base::out | ios_base::binary);
		fin.write((char *)pBuffer, nBufSize);
		fin.close();
	}
}

(7)vadbg.h

#ifndef VADBG_H
#define VADBG_H

#include <iostream>

namespace vadbg
{
	void DumpString(std::string path, std::string str);
    void DumpBuffer(std::string path, uint8_t *pBuffer, int nBufSize);
}


#endif // VADBG_H

(8)Videojj.cpp

#include <stdlib.h>
#include <string.h>

#include "vadbg.h"
#include "Videojj.h"

CVideojj::CVideojj()
{

}

CVideojj::~CVideojj()
{
	int i;
	for (i = 0; i < _vVjjSEI.size(); i++)
	{
		delete _vVjjSEI[i].szUD;
	}
}

// 用户可以根据自己的需要,对该函数进行修改或者扩展
// 下面这个函数的功能大致就是往视频中写入SEI信息
int CVideojj::Process(uint8_t *pNalu, int nNaluLen, int nTimeStamp)
{
    // 如果起始码后面的两个字节是0x05或者0x06,那么表示IDR图像或者SEI信息
	if (pNalu[4] != 0x06 || pNalu[5] != 0x05)
		return 0;
    uint8_t *p = pNalu + 4 + 2;
	while (*p++ == 0xff);
	const char *szVideojjUUID = "VideojjLeonUUID";
	char *pp = (char *)p;
	for (int i = 0; i < strlen(szVideojjUUID); i++)
	{
		if (pp[i] != szVideojjUUID[i])
			return 0;
	}
	
	VjjSEI sei;
	sei.nTimeStamp = nTimeStamp;
	sei.nLen = nNaluLen - (pp - (char *)pNalu) - 16 - 1;
	sei.szUD = new char[sei.nLen];
	memcpy(sei.szUD, pp + 16, sei.nLen);
	_vVjjSEI.push_back(sei);

	return 1;
}

(9)Videojj.h

#ifndef VIDEOJJ_H
#define VIDEOJJ_H

#include <vector>
#include <cstdint>
class CFlvParser;

typedef struct VjjSEI_s
{
	char *szUD;
	int nLen;
	int nTimeStamp; // ms
} VjjSEI;

class CVideojj
{
public:
	CVideojj();
	virtual ~CVideojj();

    int Process(uint8_t *pNalu, int nNaluLen, int nTimeStamp);

private:
	friend class CFlvParser;

	std::vector<VjjSEI> _vVjjSEI;
};

#endif // VIDEOJJ_H

五、音频解码

/**
* @projectName   07-05-decode_audio
* @brief         解码音频,主要的测试格式aac和mp3
* @author        Liao Qingfu
* @date          2020-01-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libavutil/frame.h>
#include <libavutil/mem.h>

#include <libavcodec/avcodec.h>

#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096

static char err_buf[128] = {0};
static char* av_get_err(int errnum)
{
    av_strerror(errnum, err_buf, 128);
    return err_buf;
}

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 *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 == AVERROR(EAGAIN))
    {
        fprintf(stderr, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
    }
    else if (ret < 0)
    {
        fprintf(stderr, "Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",
                av_get_err(ret), pkt->size);
//        exit(1);
        return;
    }

    /* read all the output frames (infile general there may be any number of them */
    while (ret >= 0)
    {
        // 对于frame, avcodec_receive_frame内部每次都先调用
        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);
        }
        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);
        }
        static int s_print_format = 0;
        if(s_print_format == 0)
        {
            s_print_format = 1;
            print_sample_format(frame);
        }
        /**
            P表示Planar(平面),其数据格式排列方式为 :
            LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)
            而不带P的数据格式(即交错排列)排列方式为:
            LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)
         播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm
          */
        for (i = 0; i < frame->nb_samples; i++)
        {
            for (ch = 0; ch < dec_ctx->channels; ch++)  // 交错的方式写入, 大部分float的格式输出
                fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
        }
    }
}
// 播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm
int main(int argc, char **argv)
{
    const char *outfilename;
    const char *filename;
    const AVCodec *codec;
    AVCodecContext *codec_ctx= NULL;
    AVCodecParserContext *parser = NULL;
    int len = 0;
    int ret = 0;
    FILE *infile = NULL;
    FILE *outfile = NULL;
    uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data = NULL;
    size_t   data_size = 0;
    AVPacket *pkt = NULL;
    AVFrame *decoded_frame = NULL;

    if (argc <= 2)
    {
        fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
        exit(0);
    }
    filename    = argv[1];
    outfilename = argv[2];

    pkt = av_packet_alloc();
    enum AVCodecID audio_codec_id = AV_CODEC_ID_AAC;
    if(strstr(filename, "aac") != NULL)
    {
        audio_codec_id = AV_CODEC_ID_AAC;
    }
    else if(strstr(filename, "mp3") != NULL)
    {
        audio_codec_id = AV_CODEC_ID_MP3;
    }
    else
    {
        printf("default codec id:%d\n", audio_codec_id);
    }

    // 查找解码器
    codec = avcodec_find_decoder(audio_codec_id);  // AV_CODEC_ID_AAC
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    // 获取裸流的解析器 AVCodecParserContext(数据)  +  AVCodecParser(方法)
    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "Parser not found\n");
        exit(1);
    }
    // 分配codec上下文
    codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }

    // 将解码器和解码器上下文进行关联
    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    // 打开输入文件
    infile = fopen(filename, "rb");
    if (!infile) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
    // 打开输出文件
    outfile = fopen(outfilename, "wb");
    if (!outfile) {
        av_free(codec_ctx);
        exit(1);
    }

    // 读取文件进行解码
    data      = inbuf;
    data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, infile);

    while (data_size > 0)
    {
        if (!decoded_frame)
        {
            if (!(decoded_frame = av_frame_alloc()))
            {
                fprintf(stderr, "Could not allocate audio frame\n");
                exit(1);
            }
        }

        ret = av_parser_parse2(parser, codec_ctx, &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(codec_ctx, pkt, decoded_frame, outfile);

        if (data_size < AUDIO_REFILL_THRESH)    // 如果数据少了则再次读取
        {
            memmove(inbuf, data, data_size);    // 把之前剩的数据拷贝到buffer的起始位置
            data = inbuf;
            // 读取数据 长度: AUDIO_INBUF_SIZE - data_size
            len = fread(data + data_size, 1, AUDIO_INBUF_SIZE - data_size, infile);
            if (len > 0)
                data_size += len;
        }
    }

    /* 冲刷解码器 */
    pkt->data = NULL;   // 让其进入drain mode
    pkt->size = 0;
    decode(codec_ctx, pkt, decoded_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;
}

六、视频解码

/**
* @projectName   07-05-decode_audio
* @brief         解码音频,主要的测试格式aac和mp3
* @author        Liao Qingfu
* @date          2020-01-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libavutil/frame.h>
#include <libavutil/mem.h>

#include <libavcodec/avcodec.h>

#define VIDEO_INBUF_SIZE 20480
#define VIDEO_REFILL_THRESH 4096

static char err_buf[128] = {0};
static char* av_get_err(int errnum)
{
    av_strerror(errnum, err_buf, 128);
    return err_buf;
}

static void print_video_format(const AVFrame *frame)
{
    printf("width: %u\n", frame->width);
    printf("height: %u\n", frame->height);
    printf("format: %u\n", frame->format);// 格式需要注意
}

static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,
                   FILE *outfile)
{
    int ret;
    /* send the packet with the compressed data to the decoder */
    ret = avcodec_send_packet(dec_ctx, pkt);
    if(ret == AVERROR(EAGAIN))
    {
        fprintf(stderr, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
    }
    else if (ret < 0)
    {
        fprintf(stderr, "Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",
                av_get_err(ret), pkt->size);
        return;
    }

    /* read all the output frames (infile general there may be any number of them */
    while (ret >= 0)
    {
        // 对于frame, avcodec_receive_frame内部每次都先调用
        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);
        }
        static int s_print_format = 0;
        if(s_print_format == 0)
        {
            s_print_format = 1;
            print_video_format(frame);
        }

        // 一般H264默认为 AV_PIX_FMT_YUV420P, 具体怎么强制转为 AV_PIX_FMT_YUV420P 在音视频合成输出的时候讲解
        // frame->linesize[1]  对齐的问题
        // 正确写法  linesize[]代表每行的字节数量,所以每行的偏移是linesize[]
        for(int j=0; j<frame->height; j++)
            fwrite(frame->data[0] + j * frame->linesize[0], 1, frame->width, outfile);
        for(int j=0; j<frame->height/2; j++)
            fwrite(frame->data[1] + j * frame->linesize[1], 1, frame->width/2, outfile);
        for(int j=0; j<frame->height/2; j++)
            fwrite(frame->data[2] + j * frame->linesize[2], 1, frame->width/2, outfile);

        // 错误写法 用source.200kbps.766x322_10s.h264测试时可以看出该种方法是错误的
        //  写入y分量
//        fwrite(frame->data[0], 1, frame->width * frame->height,  outfile);//Y
//        // 写入u分量
//        fwrite(frame->data[1], 1, (frame->width) *(frame->height)/4,outfile);//U:宽高均是Y的一半
//        //  写入v分量
//        fwrite(frame->data[2], 1, (frame->width) *(frame->height)/4,outfile);//V:宽高均是Y的一半
    }
}
// 注册测试的时候不同分辨率的问题
// 提取H264: ffmpeg -i source.200kbps.768x320_10s.flv -vcodec libx264 -an -f h264 source.200kbps.768x320_10s.h264
// 提取MPEG2: ffmpeg -i source.200kbps.768x320_10s.flv -vcodec mpeg2video -an -f mpeg2video source.200kbps.768x320_10s.mpeg2
// 播放:ffplay -pixel_format yuv420p -video_size 768x320 -framerate 25  source.200kbps.768x320_10s.yuv
int main(int argc, char **argv)
{
    const char *outfilename;
    const char *filename;
    const AVCodec *codec;
    AVCodecContext *codec_ctx= NULL;
    AVCodecParserContext *parser = NULL;
    int len = 0;
    int ret = 0;
    FILE *infile = NULL;
    FILE *outfile = NULL;
    // AV_INPUT_BUFFER_PADDING_SIZE 在输入比特流结尾的要求附加分配字节的数量上进行解码
    uint8_t inbuf[VIDEO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    uint8_t *data = NULL;
    size_t   data_size = 0;
    AVPacket *pkt = NULL;
    AVFrame *decoded_frame = NULL;

    if (argc <= 2)
    {
        fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);
        exit(0);
    }
    filename    = argv[1];
    outfilename = argv[2];

    pkt = av_packet_alloc();
    enum AVCodecID video_codec_id = AV_CODEC_ID_H264;
    if(strstr(filename, "264") != NULL)
    {
        video_codec_id = AV_CODEC_ID_H264;
    }
    else if(strstr(filename, "mpeg2") != NULL)
    {
        video_codec_id = AV_CODEC_ID_MPEG2VIDEO;
    }
    else
    {
        printf("default codec id:%d\n", video_codec_id);
    }

    // 查找解码器
    codec = avcodec_find_decoder(video_codec_id);  // AV_CODEC_ID_H264
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
    // 获取裸流的解析器 AVCodecParserContext(数据)  +  AVCodecParser(方法)
    parser = av_parser_init(codec->id);
    if (!parser) {
        fprintf(stderr, "Parser not found\n");
        exit(1);
    }
    // 分配codec上下文
    codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx) {
        fprintf(stderr, "Could not allocate audio codec context\n");
        exit(1);
    }

    // 将解码器和解码器上下文进行关联
    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec\n");
        exit(1);
    }

    // 打开输入文件
    infile = fopen(filename, "rb");
    if (!infile) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
    // 打开输出文件
    outfile = fopen(outfilename, "wb");
    if (!outfile) {
        av_free(codec_ctx);
        exit(1);
    }

    // 读取文件进行解码
    data      = inbuf;
    data_size = fread(inbuf, 1, VIDEO_INBUF_SIZE, infile);

    while (data_size > 0)
    {
        if (!decoded_frame)
        {
            if (!(decoded_frame = av_frame_alloc()))
            {
                fprintf(stderr, "Could not allocate audio frame\n");
                exit(1);
            }
        }

        ret = av_parser_parse2(parser, codec_ctx, &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(codec_ctx, pkt, decoded_frame, outfile);

        if (data_size < VIDEO_REFILL_THRESH)    // 如果数据少了则再次读取
        {
            memmove(inbuf, data, data_size);    // 把之前剩的数据拷贝到buffer的起始位置
            data = inbuf;
            // 读取数据 长度: VIDEO_INBUF_SIZE - data_size
            len = fread(data + data_size, 1, VIDEO_INBUF_SIZE - data_size, infile);
            if (len > 0)
                data_size += len;
        }
    }

    /* 冲刷解码器 */
    pkt->data = NULL;   // 让其进入drain mode
    pkt->size = 0;
    decode(codec_ctx, pkt, decoded_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;
}

七、Mp4解封装

#include <stdio.h>

#include "libavutil/log.h"
#include "libavformat/avformat.h"

#define ERROR_STRING_SIZE 1024

#define ADTS_HEADER_LEN  7;

const int sampling_frequencies[] = {
    96000,  // 0x0
    88200,  // 0x1
    64000,  // 0x2
    48000,  // 0x3
    44100,  // 0x4
    32000,  // 0x5
    24000,  // 0x6
    22050,  // 0x7
    16000,  // 0x8
    12000,  // 0x9
    11025,  // 0xa
    8000   // 0xb
    // 0xc d e f是保留的
};

int adts_header(char * const p_adts_header, const int data_length,
                const int profile, const int samplerate,
                const int channels)
{

    int sampling_frequency_index = 3; // 默认使用48000hz
    int adtsLen = data_length + 7;

    int frequencies_size = sizeof(sampling_frequencies) / sizeof(sampling_frequencies[0]);
    int i = 0;
    for(i = 0; i < frequencies_size; i++)
    {
        if(sampling_frequencies[i] == samplerate)
        {
            sampling_frequency_index = i;
            break;
        }
    }
    if(i >= frequencies_size)
    {
        printf("unsupport samplerate:%d\n", samplerate);
        return -1;
    }

    p_adts_header[0] = 0xff;         //syncword:0xfff                          高8bits
    p_adts_header[1] = 0xf0;         //syncword:0xfff                          低4bits
    p_adts_header[1] |= (0 << 3);    //MPEG Version:0 for MPEG-4,1 for MPEG-2  1bit
    p_adts_header[1] |= (0 << 1);    //Layer:0                                 2bits
    p_adts_header[1] |= 1;           //protection absent:1                     1bit

    p_adts_header[2] = (profile)<<6;            //profile:profile               2bits
    p_adts_header[2] |= (sampling_frequency_index & 0x0f)<<2; //sampling frequency index:sampling_frequency_index  4bits
    p_adts_header[2] |= (0 << 1);             //private bit:0                   1bit
    p_adts_header[2] |= (channels & 0x04)>>2; //channel configuration:channels  高1bit

    p_adts_header[3] = (channels & 0x03)<<6; //channel configuration:channels 低2bits
    p_adts_header[3] |= (0 << 5);               //original:0                1bit
    p_adts_header[3] |= (0 << 4);               //home:0                    1bit
    p_adts_header[3] |= (0 << 3);               //copyright id bit:0        1bit
    p_adts_header[3] |= (0 << 2);               //copyright id start:0      1bit
    p_adts_header[3] |= ((adtsLen & 0x1800) >> 11);           //frame length:value   高2bits

    p_adts_header[4] = (uint8_t)((adtsLen & 0x7f8) >> 3);     //frame length:value    中间8bits
    p_adts_header[5] = (uint8_t)((adtsLen & 0x7) << 5);       //frame length:value    低3bits
    p_adts_header[5] |= 0x1f;                                 //buffer fullness:0x7ff 高5bits
    p_adts_header[6] = 0xfc;      //‭11111100‬       //buffer fullness:0x7ff 低6bits
    // number_of_raw_data_blocks_in_frame:
    //    表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧。

    return 0;
}



// 程序本身 input.mp4  out.h264 out.aac
int main(int argc, char **argv)
{
    // 判断参数
    if(argc != 4) {
        printf("usage app input.mp4  out.h264 out.aac");
        return -1;
    }

    char *in_filename = argv[1];
    char *h264_filename = argv[2];
    char *aac_filename = argv[3];
    FILE *aac_fd = NULL;
    FILE *h264_fd = NULL;

    h264_fd = fopen(h264_filename, "wb");
    if(!h264_fd) {
        printf("fopen %s failed\n", h264_filename);
        return -1;
    }

    aac_fd = fopen(aac_filename, "wb");
    if(!aac_fd) {
        printf("fopen %s failed\n", aac_filename);
        return -1;
    }

    AVFormatContext *ifmt_ctx = NULL;
    int video_index = -1;
    int audio_index = -1;
    AVPacket *pkt = NULL;
    int ret = 0;
    char errors[ERROR_STRING_SIZE+1];  // 主要是用来缓存解析FFmpeg api返回值的错误string

    ifmt_ctx = avformat_alloc_context();
    if(!ifmt_ctx) {
        printf("avformat_alloc_context failed\n");
        //        fclose(aac_fd);
        return -1;
    }

    ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
    if(ret < 0) {
        av_strerror(ret, errors, ERROR_STRING_SIZE);
        printf("avformat_open_input failed:%d\n", ret);
        printf("avformat_open_input failed:%s\n", errors);
        avformat_close_input(&ifmt_ctx);
        //        go failed;
        return -1;
    }

    video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if(video_index == -1) {
        printf("av_find_best_stream video_index failed\n");
        avformat_close_input(&ifmt_ctx);
        return -1;
    }

    audio_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if(audio_index == -1) {
        printf("av_find_best_stream audio_index failed\n");
        avformat_close_input(&ifmt_ctx);
        return -1;
    }

    // h264_mp4toannexb
    const AVBitStreamFilter *bsfilter = av_bsf_get_by_name("h264_mp4toannexb");      // 对应面向对象的方法
    if(!bsfilter) {
        avformat_close_input(&ifmt_ctx);
        printf("av_bsf_get_by_name h264_mp4toannexb failed\n");
        return -1;
    }
    AVBSFContext *bsf_ctx = NULL;        // 对应面向对象的变量
    ret = av_bsf_alloc(bsfilter, &bsf_ctx);
    if(ret < 0) {
        av_strerror(ret, errors, ERROR_STRING_SIZE);
        printf("av_bsf_alloc failed:%s\n", errors);
        avformat_close_input(&ifmt_ctx);
        return -1;
    }
    ret = avcodec_parameters_copy(bsf_ctx->par_in, ifmt_ctx->streams[video_index]->codecpar);
    if(ret < 0) {
        av_strerror(ret, errors, ERROR_STRING_SIZE);
        printf("avcodec_parameters_copy failed:%s\n", errors);
        avformat_close_input(&ifmt_ctx);
        av_bsf_free(&bsf_ctx);
        return -1;
    }
    ret = av_bsf_init(bsf_ctx);
    if(ret < 0) {
        av_strerror(ret, errors, ERROR_STRING_SIZE);
        printf("av_bsf_init failed:%s\n", errors);
        avformat_close_input(&ifmt_ctx);
        av_bsf_free(&bsf_ctx);
        return -1;
    }

    pkt = av_packet_alloc();
    av_init_packet(pkt);
    while (1) {
        ret = av_read_frame(ifmt_ctx, pkt);     // 不会去释放pkt的buf,如果我们外部不去释放,就会出现内存泄露
        if(ret < 0 ) {
            av_strerror(ret, errors, ERROR_STRING_SIZE);
            printf("av_read_frame failed:%s\n", errors);
            break;
        }
        // av_read_frame 成功读取到packet,则外部需要进行buf释放
        if(pkt->stream_index == video_index) {
            // 处理视频
            ret = av_bsf_send_packet(bsf_ctx, pkt); // 内部把我们传入的buf转移到自己bsf内部
            if(ret < 0) {       // 基本不会进入该逻辑
                av_strerror(ret, errors, ERROR_STRING_SIZE);
                printf("av_bsf_send_packet failed:%s\n", errors);
                av_packet_unref(pkt);
                continue;
            }
//            av_packet_unref(pkt); // 这里不需要去释放内存
            while (1) {
                ret = av_bsf_receive_packet(bsf_ctx, pkt);
                if(ret != 0) {
                    break;
                }
                size_t size = fwrite(pkt->data, 1, pkt->size, h264_fd);
                if(size != pkt->size)
                {
                    av_log(NULL, AV_LOG_DEBUG, "h264 warning, length of writed data isn't equal pkt->size(%d, %d)\n",
                           size,
                           pkt->size);
                }
                av_packet_unref(pkt);
            }
        } else if(pkt->stream_index == audio_index) {
            // 处理音频
            char adts_header_buf[7] = {0};
            adts_header(adts_header_buf, pkt->size,
                        ifmt_ctx->streams[audio_index]->codecpar->profile,
                        ifmt_ctx->streams[audio_index]->codecpar->sample_rate,
                        ifmt_ctx->streams[audio_index]->codecpar->channels);
            fwrite(adts_header_buf, 1, 7, aac_fd);  // 写adts header , ts流不适用,ts流分离出来的packet带了adts header
            size_t size = fwrite( pkt->data, 1, pkt->size, aac_fd);   // 写adts data
            if(size != pkt->size)
            {
                av_log(NULL, AV_LOG_DEBUG, "aac warning, length of writed data isn't equal pkt->size(%d, %d)\n",
                       size,
                       pkt->size);
            }
            av_packet_unref(pkt);
        } else {
            av_packet_unref(pkt);       // 释放buffer
        }
    }

    printf("while finish\n");
failed:
    if(h264_fd) {
        fclose(h264_fd);
    }
    if(aac_fd) {
        fclose(aac_fd);
    }
    if(pkt)
        av_packet_free(&pkt);
    if(ifmt_ctx)
        avformat_close_input(&ifmt_ctx);


    printf("Hello World!\n");
    return 0;
}

八、AVIO

/**
* @projectName   07-05-decode_audio
* @brief         解码音频,主要的测试格式aac和mp3
* @author        Liao Qingfu
* @date          2020-01-16
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <libavutil/frame.h>
#include <libavutil/mem.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

#define BUF_SIZE 20480


static char* av_get_err(int errnum)
{
    static char err_buf[128] = {0};
    av_strerror(errnum, err_buf, 128);
    return err_buf;
}

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 int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
    FILE *in_file = (FILE *)opaque;
    int read_size = fread(buf, 1, buf_size, in_file);
    printf("read_packet read_size:%d, buf_size:%d\n", read_size, buf_size);
    if(read_size <=0) {
        return AVERROR_EOF;     // 数据读取完毕
    }
    return read_size;
}

static void decode(AVCodecContext *dec_ctx, AVPacket *packet, AVFrame *frame,
                   FILE *outfile)
{
    int ret = 0;
    ret = avcodec_send_packet(dec_ctx, packet);
    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:%s\n",
               av_get_err(ret));
        return;
    }

    while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return;
        } else if (ret < 0)  {
            printf("Error during decoding\n");
            exit(1);
        }
        if(!packet) {
            printf("get flush frame\n");
        }
        int data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
        //        print_sample_format(frame);
        /**
           P表示Planar(平面),其数据格式排列方式为 :
           LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)
           而不带P的数据格式(即交错排列)排列方式为:
           LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)
        播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm
            并不是每一种都是这样的格式
        */
        // 这里的写法不是通用,通用要调用重采样的函数去实现
        // 这里只是针对解码出来是planar格式的转换
        for(int i = 0; i < frame->nb_samples; i++) {
            for(int ch = 0; ch < dec_ctx->channels; ch++) {
                fwrite(frame->data[ch] + data_size *i, 1, data_size, outfile);
            }
        }
    }
}

int main(int argc, char **argv)
{
    if(argc != 3) {
        printf("usage: %s <intput file> <out file>\n", argv[0]);
        return -1;
    }
    const char *in_file_name = argv[1];
    const char *out_file_name = argv[2];
    FILE *in_file = NULL;
    FILE *out_file = NULL;

    // 1. 打开参数文件
    in_file = fopen(in_file_name, "rb");
    if(!in_file) {
        printf("open file %s failed\n", in_file_name);
        return  -1;
    }
    out_file = fopen(out_file_name, "wb");
    if(!out_file) {
        printf("open file %s failed\n", out_file_name);
        return  -1;
    }

    // 2自定义 io
    uint8_t *io_buffer = av_malloc(BUF_SIZE);
    AVIOContext *avio_ctx = avio_alloc_context(io_buffer, BUF_SIZE, 0, (void *)in_file,    \
                                               read_packet, NULL, NULL);
    AVFormatContext *format_ctx = avformat_alloc_context();
    format_ctx->pb = avio_ctx;
    int ret = avformat_open_input(&format_ctx, NULL, NULL, NULL);
    if(ret < 0) {
        printf("avformat_open_input failed:%s\n", av_err2str(ret));
        return -1;
    }

    // 编码器查找
    AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_AAC);
    if(!codec) {
        printf("avcodec_find_decoder failed\n");
        return -1;
    }

    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
    if(!codec_ctx) {
        printf("avcodec_alloc_context3 failed\n");
        return -1;
    }
    ret = avcodec_open2(codec_ctx, codec, NULL);
    if(ret < 0) {
        printf("avcodec_open2 failed:%s\n", av_err2str(ret));
        return -1;
    }

    AVPacket *packet = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();

    while (1) {
        ret = av_read_frame(format_ctx, packet);
        if(ret < 0) {
            printf("av_read_frame failed:%s\n", av_err2str(ret));
            break;
        }
        decode(codec_ctx, packet, frame, out_file);
    }

    printf("read file finish\n");
    decode(codec_ctx, NULL, frame, out_file);

    fclose(in_file);
    fclose(out_file);

    av_free(io_buffer);
    av_frame_free(frame);
    av_packet_free(packet);

    avformat_close_input(&format_ctx);
    avcodec_free_context(&codec_ctx);

    printf("main finish\n");

    return 0;
}













评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值