开发环境
C/C++ 导入Ffmpeg(Version 6.1.1),在程序中调用相关API,非命令行形式调用
Cue 存储格式分析
在音频中添加标线,并不是所有音频中的一个标准规定, 同样, 不同的音频编辑软件生成的信息存储格式也都是不尽相同。以 Adobe audition(以下简称AU) 生成 wav 文件为例。在AU中添加Cue后,保存音频文件为 wav 格式。用十六进制工具(例如 HxD Hex Editor)打开该文件。
/*
在 wav 格式的文件中,前44字节(通常情况下, 也有46字节)为头部信息
0x00000000-0x00000003: RIFF 0x52 0x49 0x46 0x46, 此为标准, wav文件均相同;
0x04-0x07: 从 0x00000008 开始到 wav文件 结束字节总数, 注意, 实际的wav字节数会大于此数,
这就是因为某些信息并不是wav文件的标准,所以某些音频编辑软件会在 wav 标准信息存储结束后,再追加某些信息;
0x08-0x0B: WAVE 0x57 0x41 0x56 0x45, 此为标准, wav文件均相同;
0x0C-0x0F: fmt 0x66 0x6D 0x74 0x20, 此为标准, wav文件均相同;
0x10-0x13:
0x14-0x15: 格式种类;
0x16-0x17: 声道个数, 1-单声道; 2-双声道;
0x18-0x1B: 采样率;
0x1C-0x1F: 波形数据传输速率, 即每秒传输的字节数;
0x20-0x21: 暂不知
0x22-0x23: 采样点数据的位数,即8位PCM或16位PCM等;
0x24-0x25: 暂不知
0x26-0x29: data 0x64 0x61 0x74 0x61, 此为标准, wav文件均相同;
0x2A-0x2D: PCM数据总长度
*/
在AU中添加Cue后,信息会存储在上述所有数据之后,以 0x63 0x75 0x65 0x20 做标识,此后为Cue信息,格式如下:
1. 4个字节: 0x63 0x75 0x65 0x20 表示 cue 标线信息开始,如下图红框;
2. 4个字节: cue 标线信息共占据了多少个字节, 每一个 cue标线 占据 0x18 个字节,如下图绿框;
3. 4个字节: 共有几个 cue 标线,如下图蓝框0000000A,共十个cue;
4. 每个 cue 标线信息,如下图棕框:
1. 4个字节: 标线序号(顺序同AU中一致);
2. 4个字节: 标线位置(采样点序号);
3. 4个字节: 0x64 0x61 0x74 0x61 固定格式, 表示数据信息开始;
4. 12个字节, 其他信息数据。
源代码
extern "C" {
#include
#include
#include
#include
#include
#include
#include
#include
}
int AudioEditor::audio_load()
{
int res = 1;
int ret = -1;
const AVInputFormat* format = NULL;
AVFormatContext* format_context = NULL;
std::string src_filepath = "";
std::vector src_cue_ls(0);
char* error_buffer = NULL;
// 申请 错误日志 空间
error_buffer = (char*)malloc(1024 * sizeof(char));
if (!error_buffer)
{
goto err;
}
// 分配 音频解析环境 内存
format_context = avformat_alloc_context();
if (format_context == NULL)
{
goto err;
}
// 读取音频
ret = avformat_open_input(&audio->format_context, src_filepath.c_str(), format, NULL);
if (ret != 0)
{
av_strerror(ret, error_buffer, 1024);
goto err;
}
// 解析 音频信息, 包含 声道数|采样率|比特率 等等
ret = avformat_find_stream_info(audio->format_context, NULL);
if (ret < 0)
{
goto err;
}
// 获取标记信息
// format_context->nb_chapters 是 cue 的总个数
for (int i = 0; i < format_context->nb_chapters; i++)
{
AVChapter* tmp_chapter = format_context->chapters[i];
src_cue_ls.push_back(tmp_chapter->start);
}
// 将音频帧定位到 0秒 处
av_seek_frame(format_context, -1, 0, AVSEEK_FLAG_BYTE);
// 适当时刻,释放申请的 ctx 内存,非则会内存溢出
avformat_free_context(format_context);
goto end;
err:
res = (res == 1) ? 0 : res;
end:
if (error_buffer) free(error_buffer);
return res;
}