FFmpeg封装ADTS为MP4

背景案例:
从海思芯片采集到的AAC数据为一帧帧ADTS数据,直接存为文件就是AAC了,可以直接播放,有时候需要与视频一起封装到MP4,
FFmpeg就可以做到这一点。

注:
修复上一次博客的如下代码可以实现ADTS封装为MP4,经过验证ffplay,vlc可以播放,但是H5 video标签以及某些播放器无法播放。
根本原因是需要在封装的时候,类似视频,需要在extradata中添加asc数据,asc数据可以通过adts转化而来.

下面直接贴参考代码如下:

/** @Description: * @Author: xujin* @Email: xujin_wuhan@163.com* @Date: 2022-10-25 16:49:21* @LastEditTime: 2022-11-03 23:42:28* @LastEditors: xujin* @Reference: */
/** @Description: * @Author: xujin* @Email: xujin_wuhan@163.com* @Date: 2022-10-25 16:49:21* @LastEditTime: 2022-11-11 13:04:58* @LastEditors: xujin* @Reference: */
#include <cstdio>
#include <string>
#include <fstream>
#include <memory>
#include <map>
extern "C" {
#include "libavformat/avformat.h"
}#define     SAMPLE_RATE             48000
#define     SAMPLE_SIZE             1024
#define     CHANNEL_SIZE            2
#define     SAMPLE_BITS             32 // AV_SAMPLE_FMT_FLTP
#define     AUDIO_FRAME_SIZE        (SAMPLE_SIZE * CHANNEL_SIZE * SAMPLE_BITS)
#define     DBG(fmt, args...)       printf("|DBG|%s:%s:%d|" fmt, __FILE__, __FUNCTION__, __LINE__, ##args);printf("\n");
#define     ERR(fmt, args...)       printf("|ERR|%s:%s:%d|" fmt, __FILE__, __FUNCTION__, __LINE__, ##args);printf("\n");#pragma pack(push, 1)
typedef struct AdtsHeader {unsigned int syncword:12;//同步字0xfff,说明一个ADTS帧的开始unsigned char ID:1;//ID比较奇怪,标准文档中是这么说的”MPEG identifier, set to ‘1’. See ISO/IEC 11172-3″,但我写0了,也可以unsigned char layer:2;//一般设置为0unsigned char protection_absent:1;//是否误码校验unsigned char profile:2;//表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLCunsigned char sampling_frequency_index:4;//表示使用的采样率下标0x3 48k ,0x4 44.1k, 0x5 32kunsigned char private_bit:1;//一般设置为0unsigned char channel_configuration:3;// 表示声道数unsigned char original_copy:1;//一般设置为0unsigned char home:1;//一般设置为0unsigned char copyright_identification_bit:1;//一般设置为0unsigned char copyright_identification_start:1;//一般设置为0unsigned int frame_length:13;// 一个ADTS帧的长度包括ADTS头和raw data blockunsigned int adts_buffer_fullness:11;// 0x7FF 说明是码率可变的码流unsigned char number_of_raw_data_blocks_in_frame:2;//表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧.
} AdtsHeader;
#pragma pack(pop)int ParseAdtsHeaderFromData(AdtsHeader &header, const char *data, int size) {if (!data || size < sizeof(AdtsHeader)) {ERR("Failed to parse adts header");return -1;}header.syncword = data[0] << 4 | ((data[1] & 0xf0) >> 4);header.ID = (data[1] & 0x8) >> 3;header.layer = (data[1] & 0x6) >> 1;header.protection_absent = data[1] & 0x1;header.profile = (data[2] & 0xc0) >> 6;header.sampling_frequency_index = (data[2] & 0x3c) >> 2;header.private_bit = (data[2] & 0x2)>> 1;header.channel_configuration = ((data[2] & 0x01) << 2) | ((data[3] & 0xc0) >> 6);header.original_copy = (data[3] & 0x20) >> 5;header.home = (data[3] & 0x10) >> 4;header.copyright_identification_bit = (data[3] & 0x08) >> 3;header.copyright_identification_start = (data[3] & 0x04) >> 2;header.frame_length = ((data[3] & 0x3) << 11) | (data[4] << 3) | ((data[5] &0xe0) >> 5); // 注意:对于移位操作,正确可以直接移,负数右移会左边补1导致bug,最好的做法是先&操作获取结果,然后再移位header.adts_buffer_fullness = ((data[5] & 0x1f) << 6) | ((data[6] & 0xfc) >> 2);header.number_of_raw_data_blocks_in_frame = data[7] & 0x3;return 0;
}// 先测试将ADTS封装为mp4 (将ADTS数据写成文件就是AAC数据了)
// ./a.out test.aac test.mp4
int main(int argc, char **argv) {if (argc < 2) {ERR("Usage, <%s> <aac_file> <output_mp4>", argv[0]);return -1;}DBG("sizeof(AdtsHeader) = %lu", sizeof(AdtsHeader));const std::string aac_filename(argv[1]);const std::string mp4_filename(argv[2]);std::ifstream aac_file(aac_filename);if (!aac_file.is_open()) {ERR("Failed to open %s", aac_filename.c_str());return -1;}std::ofstream mp4_file(mp4_filename);if (!mp4_file.is_open()) {ERR("Failed open %s", mp4_filename.c_str());return -1;}AVFormatContext *out_fmt_ctx = nullptr;int ret = avformat_alloc_output_context2(&out_fmt_ctx, nullptr, "mp4", mp4_filename.c_str());if (ret < 0 || !out_fmt_ctx) {ERR("Failed to avformat_alloc_output_context2, ret:%d", ret);return -1;}AVStream *audio_stream = avformat_new_stream(out_fmt_ctx, nullptr);if (!audio_stream) {ERR("Failed to avformat_new_stream");return -1;}audio_stream->codecpar->sample_rate = SAMPLE_RATE;audio_stream->codecpar->channels = CHANNEL_SIZE;audio_stream->codecpar->channel_layout = av_get_default_channel_layout(audio_stream->codecpar->channels);audio_stream->codecpar->format = AV_SAMPLE_FMT_FLTP;// audio_stream->codecpar->bit_rate = 128 * 1024; // 对已知的数据数据做封装,设置与否都不起作用,该值在编码的时候设置audio_stream->codecpar->bits_per_raw_sample = SAMPLE_BITS;audio_stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;audio_stream->codecpar->codec_id = AV_CODEC_ID_AAC;audio_stream->codecpar->frame_size = AUDIO_FRAME_SIZE;audio_stream->codecpar->profile = 0x01; // 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AAC-LC// 根据AdtsHeader获取ASC(MPEG-4 Audio Specific Configuration) [添加asc数据]unsigned char asc[2] = {0};std::map<int, int> sample_frequency_index_map;sample_frequency_index_map.insert(std::pair<int,int>(96000, 0));sample_frequency_index_map.insert(std::pair<int,int>(88200, 0x1));sample_frequency_index_map.insert(std::pair<int,int>(64000, 0x2));sample_frequency_index_map.insert(std::pair<int,int>(48000, 0x3));sample_frequency_index_map.insert(std::pair<int,int>(44100, 0x4));sample_frequency_index_map.insert(std::pair<int,int>(32000, 0x5));sample_frequency_index_map.insert(std::pair<int,int>(24000, 0x6));sample_frequency_index_map.insert(std::pair<int,int>(22050, 0x7));sample_frequency_index_map.insert(std::pair<int,int>(16000, 0x8));sample_frequency_index_map.insert(std::pair<int,int>(12000, 0x9));sample_frequency_index_map.insert(std::pair<int,int>(11025, 0xa));sample_frequency_index_map.insert(std::pair<int,int>(8000, 0xb));sample_frequency_index_map.insert(std::pair<int,int>(7350, 0xc));unsigned int sample_frequency_index = sample_frequency_index_map.find(48000)->second;unsigned int object_type = 2; // AAC LC by defaultunsigned int channel = 2;asc[0] = (object_type << 3) | (sample_frequency_index >> 1);asc[1] = ((sample_frequency_index & 0x1) << 7) | (channel << 3);audio_stream->codecpar->extradata = (uint8_t *)av_malloc(sizeof(asc) + AV_INPUT_BUFFER_PADDING_SIZE);memcpy(audio_stream->codecpar->extradata , asc, sizeof(asc));audio_stream->codecpar->extradata_size = sizeof(asc);av_dump_format(out_fmt_ctx, 0, nullptr, 1);std::unique_ptr<char[]> audio_frame_buf(new char[AUDIO_FRAME_SIZE]{0});if (!audio_frame_buf) {ERR("Failed to get memory");return -1;}int frameCount = 0;unsigned int pts = 0;while (!aac_file.eof()) {memset(audio_frame_buf.get(), 0, AUDIO_FRAME_SIZE);aac_file.read(audio_frame_buf.get(), sizeof(AdtsHeader));if (aac_file.gcount() != sizeof(AdtsHeader)) {continue;}AdtsHeader header;ParseAdtsHeaderFromData(header, audio_frame_buf.get(), aac_file.gcount());// DBG("frame_size:%d", header.frame_length);aac_file.read(audio_frame_buf.get() + sizeof(AdtsHeader), header.frame_length - sizeof(AdtsHeader));// 打开输出文件if (!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE) && !out_fmt_ctx->pb) {DBG("avio_open");avio_open(&out_fmt_ctx->pb, mp4_filename.c_str(), AVIO_FLAG_READ_WRITE);if (avformat_write_header(out_fmt_ctx, nullptr) < 0) {ERR("Failed to avformat_write_header");return -1;}}AVPacket pkt;av_init_packet(&pkt);pkt.stream_index = 0;pkt.size = aac_file.gcount();pkt.data = (uint8_t*)audio_frame_buf.get() + sizeof(AdtsHeader); // 将ADTS打包为mp4需要去掉ADTS头部(头部为7个字节)pkt.pts = pts;pkt.dts = pts;pkt.duration = 1024;pts += pkt.duration;av_interleaved_write_frame(out_fmt_ctx, &pkt);}av_write_trailer(out_fmt_ctx);avformat_close_input(&out_fmt_ctx);aac_file.close();    return 0;
}

FFmpeg封装ADTS为MP4相关推荐

  1. 转: FFMpeg 封装MP4 文件

    FFmpeg 封装MP4文件的一个例子    项目中用到FFmpeg 将IP摄像头的视频流的保存MP4文件的.之前的大哥将它先存成了H264文件,然后又调用FFMpeg的命令行去实现转码为MP4.感觉 ...

  2. php封装多段mp4,解决ffmpeg将多段视频裁剪拼接后卡顿现象

    合并 将下载的ts流名称按照顺序记录在一个txt文件中,然后使用命令 ffmpeg.exe -f concat -i ./your.txt -c copy ./output.mkv ffmpeg.ex ...

  3. ffmpeg综合项目:mp4播放器(项目代码已上传到码云)

    0.系列文章: ffmpeg音视频编码入门:视频解码 ffmpeg音视频编码入门:音频解码(acc/mp3 转 pcm) ffmpeg -- SDL2播放yuv文件(使用事件驱动和多线程,支持按键暂停 ...

  4. FFmpeg封装格式处理2-解复用例程

    本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10506642.html FFmpeg封装格式处理相关内容分为如下几篇文章: [1]. F ...

  5. Java 利用ffmpeg工具实现视频MP4转m3u8

    Java 利用ffmpeg工具实现视频MP4转m3u8(一) 前言 (一)ffmpeg工具转码 1.如何安装ffmpeg工具 2.如何使用ffmpeg工具进行视频转码 (二)播放m3u8文件 1.vi ...

  6. 封装格式分析-MP4

    声明:MP4 的封装格式是比较复杂的,本文不会把 MP4 的各个字段,各种场景用法都罗列出来,那样会形成一个手册. 文本主要讲解 MP4 这种封装格式在音视频开发中比较常用的字段,还有一些关键的知识点 ...

  7. (三)利用ffmpeg实现简单的MP4推流

    首先的话,我想写感谢一个带我进入音视频处理领域的人,虽然从未谋面,但是是他的博客指引了我学习音视频开发的道路,启蒙了一个曾经迷茫的程序员.但是很可惜,他已经在2016年不幸离开了我们,他是雷霄骅,一位 ...

  8. 使用 ffmpeg 转换视频格式 mp4 webm

    ffmpeg 是 *nix 系统下最流行的音视频处理库,功能强大,并且提供了丰富的终端命令,实是日常视频处理的一大利器! 实例 flac 格式转 mp3 音频格式转换非常简单:. ffmpeg -i ...

  9. Ubuntu 使用 ffmpeg 将 webm 转换为 mp4 (批量)

    Ubuntu 使用 ffmpeg 将 webm 转换为 mp4: ffmpeg -i input.webm -vf 'scale=trunc(iw/2)*2:trunc(ih/2)*2' output ...

最新文章

  1. Python 哪种方式循环最快,或许颠覆你的认知!
  2. 威刚(A-DATA)DDR3-1600震撼评测
  3. 工作以后如何有效学习
  4. HDU 5253 最小生成树(kruskal)+ 并查集
  5. debian安装什么mysql_安装多个mysql实例(debian版本)
  6. pymol安装教程linux,PyMOL | Pymol绘图教程(一)
  7. sqlserver200864位下载_Microsoft SQL Server 2008 R2 官方简体中文正式版下载
  8. 硅谷女孩火了,更该让他们开始学编程
  9. Java整数的所有质因数,用JAVA将一个正整数分解成质因数,例如输入90,打印出90=2*3*3*5...
  10. 计算机管理里边设置访问权限,电脑访问权限设置_电脑软件访问权限设置
  11. 计算机语言写信祝福语,写信祝福语
  12. 拜年神器php,Biu神器安卓版
  13. 2020NISP一级(模拟题一)
  14. oracle ORA-00001: 违反唯一约束条件
  15. vi编辑器基础命令合集与yim初级使用
  16. 用Python做一个游戏辅助脚本(含完整编程思路)
  17. MFRC522使用、设计心得及详细教程资料
  18. 客户端数字证书申请指南、安装指南和备份指南
  19. 测量学10_建筑工程测量及道路工程测量
  20. (C++)编写一个判别素数的函数,在主函数输入一个整数,输出是否为素数的信息

热门文章

  1. windows更改pip源_Windows下更改pip镜像源
  2. java 数字转大写_Java把数字转换成中文大写数字
  3. 基于oracle的餐馆评价系统,美团大众点评优化评价系统 助力餐饮商家做好口碑营销...
  4. 一箭穿心——两颗心紧紧相连(Python代码实现)
  5. 移动电源哪个牌子的好,移动电源品牌排行
  6. 傅里叶实现高精度乘法
  7. 宝塔Linux面板安装教程
  8. android 后台键盘按键监听,android键盘的监听
  9. 机械革命z3pro笔记本U盘重装win10系统教学
  10. 带你理解交换机基本原理和配置