ffmpeg裸流解析

使用FFmpeg API解码MP3&AAC音频文件 - 简书

// 打开裸流解析上下文
    AVCodecParserContext* pCodecParserCTX = av_parser_init(pCodec->id);
    if (pCodecParserCTX == NULL)
    {
        printf("init parser context failed! \n");
        return -1;
    }

// 尽量喂给,但是一次最大只解析出一个包
            int nPacketSize = av_parser_parse2(pCodecParserCTX, pCodecCTX, &(pPacket->data), &(pPacket->size), 
                    (uint8_t*)sDataBuffer + nOffset, nBytesRead - nOffset, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
 

av_parser_close(pCodecParserCTX);

代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
}

const char* getSampleFormatName(enum AVSampleFormat emSampleFormat);
bool decode(AVCodecContext* pCodecCTX, const AVPacket* pPacket, AVFrame* pFrame, FILE* pFile);

int main(int argc, char* argv[])
{
    const char* pCodecName = avcodec_get_name(AV_CODEC_ID_MP3);
    printf("codec: %d -> %s \n", AV_CODEC_ID_MP3, pCodecName);

    const AVCodec* pCodec = avcodec_find_decoder_by_name(pCodecName);
    if (pCodec == NULL)
    {
        printf("can't find decoder! \n");
        return -1;
    }

    // 根据指定解码器初使化对应的解码上下文
    AVCodecContext* pCodecCTX = avcodec_alloc_context3(pCodec);
    if (pCodecCTX == NULL)
    {
        printf("can't alloc decoder context! \n");
        return -1;
    }

    // 打开解码器上下文
    int rc = avcodec_open2(pCodecCTX, pCodec, NULL);
    if (rc < 0)
    {
        char sError[128] = {0};
        av_strerror(rc, sError, sizeof(sError));
        printf("avcodec_open2() ret:[%d:%s] \n", rc, sError);
        return -1;
    }

    // 打开裸流解析上下文
    AVCodecParserContext* pCodecParserCTX = av_parser_init(pCodec->id);
    if (pCodecParserCTX == NULL)
    {
        printf("init parser context failed! \n");
        return -1;
    }

    AVPacket* pPacket = av_packet_alloc();
    AVFrame* pFrame = av_frame_alloc();

    FILE* pFileInput = fopen("test.mp3", "rb");
    FILE* pFileOutput = fopen("test.pcm", "wb");

    while (true)
    {
        const int BUFF_SIZE = 100; //20480;
        char sDataBuffer[BUFF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE] = {0};

        int nBytesRead = fread(sDataBuffer, 1, BUFF_SIZE, pFileInput);
        if (nBytesRead <= 0)
            break;

        printf("read bytes: %d \n", nBytesRead);

        // 一次读取,全部喂给
        int nOffset = 0;
        while (nOffset < nBytesRead)
        {
            // 尽量喂给,但是一次最大只解析出一个包
            int nPacketSize = av_parser_parse2(pCodecParserCTX, pCodecCTX, &(pPacket->data), &(pPacket->size), 
                    (uint8_t*)sDataBuffer + nOffset, nBytesRead - nOffset, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);

            printf("\t offset:%d max feed size:%d eat size:%d \n", nOffset, nBytesRead - nOffset, nPacketSize);

            // 输出了报文,需要解码
            if (pPacket->size > 0)
            {
                printf("\t\t out packet size:%d \n", pPacket->size);

                // 解码,这里AVPacket结构只有data和size成员有效
                if (!decode(pCodecCTX, pPacket, pFrame, pFileOutput))
                {
                    printf("decode fatal! \n");
                    exit (-1);
                }
            }

            nOffset += nPacketSize;
        }
    }

    // 解码尾包
    decode(pCodecCTX, NULL, pFrame, pFileOutput);

    AVSampleFormat emSampleFormat = pCodecCTX->sample_fmt;
    if (av_sample_fmt_is_planar(emSampleFormat))
    {
        const char* pPacked = av_get_sample_fmt_name(emSampleFormat);
        printf("Warning: the sample format the decoder produced is planar(%s). This example will output the first channel only.\n",
                pPacked ? pPacked : "?");
        emSampleFormat = av_get_packed_sample_fmt(emSampleFormat);
    }

    printf("Play the output audio file with command: \n");
    printf("\t ffplay -f %s -ac %d -ar %d test.pcm \n", getSampleFormatName(emSampleFormat), pCodecCTX->channels, pCodecCTX->sample_rate);

    fclose(pFileOutput);
    fclose(pFileInput);

    av_packet_free(&pPacket);
    av_frame_free(&pFrame);

    av_parser_close(pCodecParserCTX);
    avcodec_free_context(&pCodecCTX);

    return 0;
}

const char* getSampleFormatName(enum AVSampleFormat emSampleFormat)
{
    switch (emSampleFormat)
    {
    case AV_SAMPLE_FMT_U8:
        return "u8";
    case AV_SAMPLE_FMT_S16:
        return AV_NE("s16be", "s16le");
    case AV_SAMPLE_FMT_S32:
        return AV_NE("s32be", "s32le");
    case AV_SAMPLE_FMT_FLT:
        return AV_NE("f32be", "f32le");
    case AV_SAMPLE_FMT_DBL:
        return AV_NE("f64be", "f64le");
    }
    return "unkown";
}

bool decode(AVCodecContext* pCodecCTX, const AVPacket* pPacket, AVFrame* pFrame, FILE* pFile)
{
    // 发送数据
    int rc = avcodec_send_packet(pCodecCTX, pPacket);
    if (rc < 0)
    {
        char sError[128] = {0};
        av_strerror(rc, sError, sizeof(sError));
        printf("avcodec_send_packet() ret:[%d:%s] \n", rc, sError);

        return false;
    }

    // 接收解码结果
    while (true)
    {
        rc = avcodec_receive_frame(pCodecCTX, pFrame);

        if (rc < 0)
        {
            // 无解码输出
            if (rc == AVERROR(EAGAIN) || rc == AVERROR_EOF)
                return true;

            // 解码出错
            char sError[128] = {0};
            av_strerror(rc, sError, sizeof(sError));
            printf("avcodec_receive_frame() ret:[%d:%s] \n", rc, sError);

            return false;
        }

        printf("\t\t => frame format:[%d:%s] channels:[%d] sample_rate:[%d] nb_samples:[%d] pkt_size:[%d] linesize:[%d] \n", 
                pFrame->format, av_get_sample_fmt_name((AVSampleFormat)pFrame->format), pFrame->channels, pFrame->sample_rate, pFrame->nb_samples, pFrame->pkt_size, pFrame->linesize[0]);
                    
        int nSampleSize = av_get_bytes_per_sample(pCodecCTX->sample_fmt);

        // 多通道按交错排列写入
        for (int i = 0; i < pFrame->nb_samples; ++i)
        {
            for (int c = 0; c < pCodecCTX->channels; ++c)
            {
                fwrite(pFrame->data[c] + nSampleSize * i, 1, nSampleSize, pFile);
            }
        }
    }

    return true;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值