09 - AVIO - 最简单的自定义IO - [aac提取pcm]

在自定义协议的时候,提取连续的媒体数据

#define BUFSIZE 1024 * 4 // 经典大小
// int (*read_packet)(void *opaque, uint8_t *buf, int buf_size)
int read_packet(void *opaque, uint8_t *buf, int bufSize)
{
    FILE *inFile = (FILE *)opaque;
    int readSize = fread(buf, 1, bufSize, inFile);
    av_log(NULL, AV_LOG_DEBUG, "[%s] readSize:%d, buf_size:%d\n -- line:%d", __FUNCTION__, readSize, bufSize, __LINE__);
    if (readSize <= 0) // 数据读取完毕
        return AVERROR_EOF;
    return readSize;
}

int AVIO_decodeVideoInterface(AVCodecContext *decoderCtx, AVPacket *packet, AVFrame *frame, FILE *dest_fp)
{
    int ret = avcodec_send_packet(decoderCtx, packet);
    if (ret == AVERROR(EAGAIN))
    {
        av_log(NULL, AV_LOG_WARNING, "[%s] Receive_frame and send_packet both returned EAGAIN,which is an API violation. -- line:%d\n", __FUNCTION__, __LINE__);
    }
    else if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avcodec_send_packet failed! -- line:%d\n", __FUNCTION__, __LINE__);
        return -1;
    }
    if (!packet)
    {
        av_log(NULL, AV_LOG_INFO, "[%s] get flush frame -- line:%d \n", __FUNCTION__, __LINE__);
    }

    while (avcodec_receive_frame(decoderCtx, frame) >= 0)
    {
        size_t dataSize = av_get_bytes_per_sample(decoderCtx->sample_fmt);
        if (dataSize < 0)
        {
            av_log(NULL, AV_LOG_ERROR, "[%s] get bytes failed! -- line:%d\n", __FUNCTION__, __LINE__);
            return -1;
        }
        av_log(NULL, AV_LOG_DEBUG, "[%s] sample rate: %d HZ, channels: %d, pixformat:%u -- line:%d\n", __FUNCTION__, frame->sample_rate, frame->channels, frame->format, __LINE__);

        // 这里只是针对是planar格式的转换,并不通用
        for (int i = 0; i < frame->nb_samples; i++)
        {
            for (int channel = 0; channel < decoderCtx->channels; channel++)
            {
                size_t writeSize = fwrite(frame->data[channel] + dataSize * i, 1, dataSize, dest_fp);
                if (writeSize <= 0 || writeSize != dataSize)
                {
                    av_log(NULL, AV_LOG_ERROR, "[%s] write file failed! -- line:%d\n", __FUNCTION__, __LINE__);
                    return -1;
                }
            }
        }
    }
}

int AVIO_decodeVideo(const char *inFileName, const char *outFileName)
{
    FILE *inFile = fopen(inFileName, "rb");
    FILE *outFile = fopen(outFileName, "wb");
    if (!inFile || !outFile)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] open outfile:%s or %s failed! -- line:%d\n", __FUNCTION__, outFileName, inFileName, __LINE__);
        goto _end;
    }
    // 自定义 IO
    uint8_t *ioBuffer = av_malloc(BUFSIZE);
    if (!ioBuffer)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] malloc ioBuffer failed! -- line:%d\n", __FUNCTION__, __LINE__);
        goto _end;
    }
    /**
     * ioBuffer -- Memory block for input/output operations via AVIOContext.
     * The buffer must be allocated with av_malloc() and friends
     * It may be freed and replaced with a new buffer by libavformat.
     * AVIOContext.buffer holds the buffer currently in use,
     * which must be later freed with av_free().
     */
    /**
     * BUFSIZE -- The buffer size is very important for performance.
     * For protocols with fixed blocksize it should be set to this blocksize.
     * For others a typical size is a cache page, e.g. 4kb.
     */
    /**
     * (void *)inFile -- An opaque pointer to user-specific data
     */
    /**
     * read_packet -- A function for refilling the buffer, may be NULL.
     * For stream protocols, must never return 0 but rather a proper AVERROR code.
     */
    AVIOContext *avioCtx = avio_alloc_context(ioBuffer, BUFSIZE, 0/*读数据是0 写数据是1*/, (void *)inFile, read_packet, NULL, NULL);
    if (!avioCtx)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avio alloc context failed! -- line:%d\n", __FUNCTION__, __LINE__);
        goto _end;
    }
    AVFormatContext *fmtCtx = avformat_alloc_context();
    if (!fmtCtx)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avformat alloc context failed! -- line:%d\n", __FUNCTION__, __LINE__);
        goto _end;
    }
    fmtCtx->pb = avioCtx;
    int ret = avformat_open_input(&fmtCtx, NULL, NULL, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avformat open input failed%s -- line:%d\n", __FUNCTION__, av_err2str(ret), __LINE__);
        goto _end;
    }
    // 编码器查找
    AVCodec *decoder = avcodec_find_decoder(AV_CODEC_ID_AAC);
    if (!decoder)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avcodec find decoder failed! -- line:%d\n", __FUNCTION__, __LINE__);
        goto _end;
    }
    AVCodecContext *decoderCtx = avcodec_alloc_context3(decoder);
    if (!decoderCtx)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avcodec alloc context3 failed! -- line:%d\n", __FUNCTION__, __LINE__);
        goto _end;
    }
    ret = avcodec_open2(decoderCtx, decoder, NULL);
    if (ret < 0)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] avcodec open2 failed:%s! -- line:%d\n", __FUNCTION__, av_err2str(ret), __LINE__);
        goto _end;
    }
    AVPacket *packet = av_packet_alloc();
    AVFrame *frame = av_frame_alloc();
    if (!packet || !frame)
    {
        av_log(NULL, AV_LOG_ERROR, "[%s] av packet or frame alloc failed! -- line:%d\n", __FUNCTION__, __LINE__);
        goto _end;
    }
    while (av_read_frame(fmtCtx, packet) >= 0)
    {
        AVIO_decodeVideoInterface(decoderCtx, packet, frame, outFile);
    }

    av_log(NULL, AV_LOG_INFO, "[%s] read file finish-- line:%d\n", __FUNCTION__, __LINE__);
    AVIO_decodeVideoInterface(decoderCtx, NULL, frame, outFile);

_end:

    if (inFile)
        fclose(inFile);
    if (outFile)
        fclose(outFile);
    if (packet)
        av_packet_free(&packet);
    if (fmtCtx)
        avformat_close_input(&fmtCtx);
    if (decoderCtx)
        avcodec_free_context(&decoderCtx);
    if (frame)
        av_frame_free(&frame);
    if (avioCtx)
        avio_context_free(&avioCtx);

    return ret;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值