• ffmpeg-- audio decoder


    测试代码来源于:http://ffmpeg.org/doxygen/trunk/decode_audio_8c-example.html

    /*
    * Copyright (c) 2001 Fabrice Bellard
    *
    * Permission is hereby granted, free of charge, to any person obtaining a copy
    * of this software and associated documentation files (the "Software"), to deal
    * in the Software without restriction, including without limitation the rights
    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    * copies of the Software, and to permit persons to whom the Software is
    * furnished to do so, subject to the following conditions:
    *
    * The above copyright notice and this permission notice shall be included in
    * all copies or substantial portions of the Software.
    *
    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    * THE SOFTWARE.
    */
     
    /**
    * @file
    * audio decoding with libavcodec API example
    *
    * @example decode_audio.c
    */
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    #include <libavutil/mem.h>
     
     
    #define AUDIO_INBUF_SIZE 20480
    #define AUDIO_REFILL_THRESH 4096
     
    {
      int i, ch;
      int ret, data_size;
     
      /* send the packet with the compressed data to the decoder */
      ret = avcodec_send_packet(dec_ctx, pkt);
      if (ret < 0) {
      fprintf(stderr, "Error submitting the packet to the decoder ");
      exit(1);
      }
     
      /* read all the output frames (in general there may be any number of them */
      while (ret >= 0) {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
          return;
        else if (ret < 0) {
          fprintf(stderr, "Error during decoding ");
          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 ");
          exit(1);
        }
        for (i = 0; i < frame->nb_samples; i++)
          for (ch = 0; ch < dec_ctx->channels; ch++)
            fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
      }
    }
     
    int main(int argc, char **argv)
    {
      const char *outfilename, *filename;
      const AVCodec *codec;
      AVCodecParserContext *parser = NULL;
      int len, ret;
      FILE *f, *outfile;
      size_t data_size;
      AVFrame *decoded_frame = NULL;
     
      if (argc <= 2) {
        fprintf(stderr, "Usage: %s <input file> <output file> ", argv[0]);
        exit(0);
      }  
      filename = argv[1];
      outfilename = argv[2];
     
      pkt = av_packet_alloc();
     
      /* find the MPEG audio decoder */
      if (!codec) {
        fprintf(stderr, "Codec not found ");
        exit(1);
      }
     
      parser = av_parser_init(codec->id);
      if (!parser) {
        fprintf(stderr, "Parser not found ");
        exit(1);
      }
     
      c = avcodec_alloc_context3(codec);
      if (!c) {
        fprintf(stderr, "Could not allocate audio codec context ");
        exit(1);
      }
     
      /* open it */
      if (avcodec_open2(c, codec, NULL) < 0) {
        fprintf(stderr, "Could not open codec ");
        exit(1);
      }
     
      f = fopen(filename, "rb");
      if (!f) {
        fprintf(stderr, "Could not open %s ", filename);
        exit(1);
      }
      outfile = fopen(outfilename, "wb");
      if (!outfile) {
        av_free(c);
        exit(1);
      }
     
      /* decode until eof */
      data = inbuf;
      data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f);
     
      while (data_size > 0) {
        if (!decoded_frame) {
          if (!(decoded_frame = av_frame_alloc())) {
            fprintf(stderr, "Could not allocate audio frame ");
            exit(1);
          }
        }
     
        ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,data, data_size,
            AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
        if (ret < 0) {
          fprintf(stderr, "Error while parsing ");
          exit(1);
        }
        data += ret;
        data_size -= ret;
     
        if (pkt->size)
        decode(c, pkt, decoded_frame, outfile);
     
        if (data_size < AUDIO_REFILL_THRESH) {
          memmove(inbuf, data, data_size);
          data = inbuf;
          len = fread(data + data_size, 1,AUDIO_INBUF_SIZE - data_size, f);
          if (len > 0)
            data_size += len;
        }
      }
     
      /* flush the decoder */
      pkt->data = NULL;
      pkt->size = 0;
      decode(c, pkt, decoded_frame, outfile);
     
      fclose(outfile);
      fclose(f);
     
      av_parser_close(parser);
      av_frame_free(&decoded_frame);
      av_packet_free(&pkt);
     
      return 0;
    }

    需要在sample code加一行avcodec_register_all()

    Makefile如下:

    export CC=gcc
    FFMPEGPATH=/mnt/hgfs/share/ffmpeg-3.3.3/ffmpeg-3.3.3/output
    SOURCE=decode_audio.c
    INCLUDE=-I$(FFMPEGPATH)/include
    LINK=-L$(FFMPEGPATH)/lib/ -lavcodec -lavformat -lavutil -lswresample
    LINK+=-lpthread -lm -ldl
    TARGET=adecoder
    all:
      $(CC) $(SOURCE) $(INCLUDE) $(LINK) -o $(TARGET)

     主要函数介绍:

    AVPacket *av_packet_alloc(void)

    分配并初始化AVPacket结构体,AVPacket是存储压缩编码数据相关信息的结构体。free AVPacket的函数为av_packet_free().

    av_packet_alloc只allocate AVPacket本身,并不分配内部data buffer. data buffer 使用其他方式allocate,比如av_new_packet.

    AVCodec * avcodec_find_decoder(enum AVCodecID  id)

    使用codec ID在已经注册的decoders中查找相应的decoder,如果ffmpeg有注册了相应的decoder,则返回AVCodec结构体,否则返回NULL.AVCodec是存储编解码器信息的结构体.

    AVCodecParserContex *av_parser_init(int codec_id)

    根据codec id,在已经注册的parser中查找,是否有相关codec的parser,如果存在该codec的parser,则分配AVCodecParserContex结构体。parser用于从raw data中parse出packet.AVCodecParserContex存储parser contex相关信息的结构体,包含AVCodecParser结构体和frame_offset,next_frame_offset等,AVCodecParser存储parser相关信息。

    AVCodecContext *avcodec_alloc_context3(const AVCodec *codec)

    allocate AVCodecContext 结构体,对AVCodecContext 设置一些default值。AVCodecContext 存储codec contex信息,包含AVCodec结构。

    int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)

    使用AVCodec对AVCodecContex进行初始化。

     AVFrame *av_frame_alloc(void)

    allocate AVFrame 结构体,并对AVFrame 的Filed设置default值。AVFrame存储解码后的数据。av_frame_alloc只allocate AVFrame本身,并不分配内部data buffer. data buffer 使用其他方式allocate,比如av_frame_get_buffer().

     int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx, uint8_t **poutbuf, int *poutbuf_size,const uint8_t *buf, int buf_size, int64_t pts, int64_t dts, int64_t pos)

    调用codec的parser的parser_parse函数从输入的数据buf中 parse一个packet出来。packet的data放到poutbuf, size为poutbuf_size.

    int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)

    将parser parse出的raw packet作为输入数据给到decoder解码。如果decoder有send_packet函数则调用该函数,如果没有,则调用avcodec_decode_audio4()进行解码,将解码出来的AVFrame结构保存在avctx->internal->buffer_frame。

    如果返回0表示该函数成功返回。如果返回负数AVERROR(EAGAIN),decoder当前状态不接收input数据,用户必须调用avcodec_recieve_frame来读走output数据,一旦output数据被读走,resent packet后就不会返回这样的错误。

    int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)

    如果decoder有recieve_frame函数则调用该函数,如果没有该函数,则check avctx->internal->buffer_frame是否有数据,如果有则返回avctx->internal->buffer_frame,没有数据这调用avcodec_decode_audio4()进行解码,将解码出来的AVFrame结构保存在avctx->internal->buffer_frame,并返回AVFrame。

    对于video,一个avpkt对应一个video frame,但对于某些audio codec,一个avpkt有多个audio frame.如果有多个audio frame,需要在调用avcodec_send_packet后调用多次avcodec_recieve_frame直到packet完全被消耗,才能send new packet.

     int attribute_align_arg avcodec_decode_audio4(AVCodecContext *avctx,AVFrame *frame,int *got_frame_ptr,const AVPacket *avpkt)

    从输入的avpcket解码出audio frame.返回值为负值表示decoder出现error,否则返回消耗avpkt的byte数。

    对于一个AVPacket包含多个audio frame的情况,第一调用avcodec_decode_audio4只解码出第一个frame,返回值小于avpkt->size.再call一次avcodec_decode_audio4解码出第二个frame.....即使函数不返回frame,也要将packet送到decoder直至消耗完或返回error.

    某些decoder在input 和output有delay,这表示一些packet并不是立即经由decoder解码输出,而需要decoding结束时flush,从而获得所有的解码数据。对于没有delay的decoder,flush也是安全的。flush是通过调用该函数,并将avpkt->data=NULL, avpkt->size=0.

  • 相关阅读:
    python 发送邮件 email
    python 日志 logging
    python 判断当前环境是什么系统 platform 模块
    pandas to_excel 添加颜色
    Pandas excel 双标题 多级索引 层次化索引 MultiIndex
    windows10 安装 pyltp python3.6
    Python的内存管理
    pandas 中 DataFramt 改变 列的顺序
    元组
    基本语法
  • 原文地址:https://www.cnblogs.com/fellow1988/p/9131250.html
Copyright © 2020-2023  润新知