提供一个可以运行的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