ffmpeg编程示例-提取aac

提供一个可以运行的ffmpeg工程:https://gitee.com/qiuguolu1108/ffmpeg-study

#include "spdlog/spdlog.h"

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
}

#define ADTS_HEADER_LEN 7

char error[1024];

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 sample_rate, 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;
  for (i = 0; i < frequencies_size; i++) {
    if (sampling_frequencies[i] == sample_rate) {
      sampling_frequency_index = i;
      break;
    }
  }

  if (i >= frequencies_size) {
    SPDLOG_ERROR("unsupport samplerate:{}", sample_rate);
  }

  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() {
  const char file_name[] = "Jasmine.flv";

  FILE* fp = fopen("Jasmine.aac", "wb");
  if (!fp) {
    SPDLOG_ERROR("Could not open file: Jasmine.aac");
  }

  AVFormatContext* fmt_ctx = NULL;
  int ret = avformat_open_input(&fmt_ctx, file_name, NULL, NULL);
  if (ret < 0) {
    av_strerror(ret, error, 1024);
    SPDLOG_ERROR("avformat_open_input: {}", error);
  }

  // 获取解码器信息
  ret = avformat_find_stream_info(fmt_ctx, NULL);
  if (ret < 0) {
    av_strerror(ret, error, 1024);
    SPDLOG_ERROR("avformat_find_stream_info: {}", error);
  }

  // dump媒体信息
  av_dump_format(fmt_ctx, 0, file_name, 0);

  // 查找audio对应的steam index
  int audio_index =
      av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
  if (audio_index < 0) {
    SPDLOG_ERROR("Could not find {} stream in input file {}",
                 av_get_media_type_string(AVMEDIA_TYPE_AUDIO), file_name);
  }

  // 打印AAC级别
  SPDLOG_INFO("audio profile: {}, FF_PROFILE_AAC_LOW: {}",
              fmt_ctx->streams[audio_index]->codecpar->profile,
              FF_PROFILE_AAC_LOW);

  if (fmt_ctx->streams[audio_index]->codecpar->codec_id != AV_CODEC_ID_AAC) {
    SPDLOG_ERROR("the media file no contain AAC stream, it's codec_id is {}\n",
                 fmt_ctx->streams[audio_index]->codecpar->codec_id);
  }

  AVPacket pkt;

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

  // 读取媒体文件,并把aac数据帧写入到本地文件。
  while (av_read_frame(fmt_ctx, &pkt) >= 0) {
    if (pkt.stream_index == audio_index) {
      char adts_header_buf[7] = {0};
      adts_header(adts_header_buf, pkt.size,
                  fmt_ctx->streams[audio_index]->codecpar->profile,
                  fmt_ctx->streams[audio_index]->codecpar->sample_rate,
                  fmt_ctx->streams[audio_index]->codecpar->channels);

      // 写adts header , ts流不适用,ts流分离出来的packet带了adts header。
      fwrite(adts_header_buf, 1, 7, fp);

      int len = fwrite(pkt.data, 1, pkt.size, fp);  // 写adts data
      if (len != pkt.size) {
        SPDLOG_WARN(
            "warning, length of writed data isn't equal pkt.size({}, {})\n",
            len, pkt.size);
      }
    }
    av_packet_unref(&pkt);
  }

  avformat_close_input(&fmt_ctx);
  fclose(fp);

  SPDLOG_INFO("extract successful");

  return 0;
}
Input #0, flv, from 'Jasmine.flv':
  Metadata:
    description     : Bilibili VXCode Swarm Transcoder v0.3.63
    metadatacreator : Version 1.9
    hasKeyframes    : true
    hasVideo        : true
    hasAudio        : true
    hasMetadata     : true
    canSeekToEnd    : true
    datasize        : 47142454
    videosize       : 39013116
    audiosize       : 8071726
    lasttimestamp   : 200
    lastkeyframetimestamp: 200
    lastkeyframelocation: 47143855
  Duration: 00:03:20.46, start: 0.120000, bitrate: 1881 kb/s
    Stream #0:0: Video: h264 (High), yuv420p(tv, bt709, progressive), 1280x720 [SAR 1:1 DAR 16:9], 1556 kb/s, 25.03 fps, 25 tbr, 1k tbn, 50 tbc
    Stream #0:1: Audio: aac (LC), 48000 Hz, stereo, fltp, 318 kb/s
[2021-09-14 21:23:06.425] [info] [main.cpp:121] audio profile: 1, FF_PROFILE_AAC_LOW: 1
[2021-09-14 21:23:06.493] [info] [main.cpp:158] extract successful

使用如下命令播放生成的aac文件:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值