H264/AAC实时流 录制成MP4格式的本地视频

GITHUB:
https://github.com/chezi008/mp4muxer

建议使用场景

一般视频流有如下两种途径获取:

  1. Android摄像头采集
  2. 服务端传输过来的视频流

如果数据由本机摄像头直接采集,建议使用MediaMuxer类去实现mp4的合成。如果是服务端传输过来的视频流可以使用mp4v2的方法实现mp4的合成。我在项目里面也简单的利用MediaMuxer编写了一个Demo。可能写的不是很详细,功能也不是很完善。所以有什么问题还是多多希望指出,一起改进。

mp4v2的So文件

  1. 我demo里面只导入了armeabi 指令集的so包,需要其他类型的可以自己去官网下载进行打包。
  2. 将mp4v2的头文件放在libs里面的include文件夹下面,所以再cmake文件里面需要增加include_directories(libs/include)命令。

使用c++将mp4v2的使用封装了一层

封装了如下几个方法:

// open or creat a mp4 file.
MP4FileHandle CreateMP4File(const char *fileName,int width,int height,int timeScale = 90000,int frameRate = 25);
// wirte 264 metadata in mp4 file.
bool Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata);
// wirte 264 data, data can contain  multiple frame.
int WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size);
// close mp4 file.
void CloseMP4File(MP4FileHandle hMp4File);
// convert H264 file to mp4 file.
// no need to call CreateMP4File and CloseMP4File,it will create/close mp4 file automaticly.
bool WriteH264File(const char* pFile264,const char* pFileMp4);

方法说明:

  1. CreateMP4File:创建需要写入的mp4文件,可以设置视频的分辨率。默认的时间刻度是90000,帧率是25帧
  2. WriteH264Data:写入h264数据,可以写入byte[]类型的数据流,也可是LPMP4ENC_Metadata(sps pps)类型数据。
  3. CloseMP4File:转换完成后记得要释放内存,调用次方法
  4. WriteH264File:直接将本地h264文件转换成mp4。实现方法是一样的,只是在C++代码里面实现了,将h264数据分割成一帧一帧,再写入至输入文件中。

一、mp4v2 方法的使用

这个库太久没有维护了,还是建议使用官方API,即第二种方法。
同样,我在代码中封装了三个本地方法:

public static native void init(String mp4FilePath, int widht, int height);public static native int writeH264Data(byte[] data, int size);public static native void close();

从方法名,可以看得出怎么使用,所以在这里就不多赘述了。项目里面提供了标准的h264测试文件mtv.h264。

二、MediaMuxer合成Mp4

####官方文档介绍:http://www.loverobots.cn/android-api/reference/android/media/MediaMuxer.html

 MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4);// More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat()// or MediaExtractor.getTrackFormat().MediaFormat audioFormat = new MediaFormat(...);MediaFormat videoFormat = new MediaFormat(...);int audioTrackIndex = muxer.addTrack(audioFormat);int videoTrackIndex = muxer.addTrack(videoFormat);ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize);boolean finished = false;BufferInfo bufferInfo = new BufferInfo();muxer.start();while(!finished) {// getInputBuffer() will fill the inputBuffer with one frame of encoded// sample from either MediaCodec or MediaExtractor, set isAudioSample to// true when the sample is audio data, set up all the fields of bufferInfo,// and return true if there are no more samples.finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo);if (!finished) {int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex;muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo);}};muxer.stop();muxer.release();

MediaMuxer的使用

  1. 初始化:
mMuxer = new MediaMuxer(outPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
  1. 根据自己情况添加track,不要方法添加,此操作必须在mMuxer start方法之前调用:
mVideoTrackIndex = mMuxer.addTrack(mediaFormat);mAudioTrackIndex = mMuxer.addTrack(mediaFormat);
  1. 写入数据
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
mMuxer.writeSampleData(track, outputBuffer, bufferInfo);
  1. 结束
 mMuxer.stop();mMuxer.release();

在结束的时候你可能会遇到几种异常:

  • E/MPEG4Writer: Missing codec specific data:
    这是因为在写入数据的时候没有写入编码参数,h264的编码参数包含SPS和PPS。所以当你视频流遇到这些参数帧的时候,请设置好对应的参数。
 //设置sps和pps 如果设置不正确会导致合成的mp4视频作为文件预览的时候,预览图片是黑色的//视频进度条拖拽画面会出现绿色,以及块状现象mediaformat.setByteBuffer("csd-0", mCSD0);mediaformat.setByteBuffer("csd-1", mCSD1);

AAC参数:PCM在用编码器编码成AAC格式音频的时候,编码器会在自动设置参数。


当outIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED
我们可以获取到,MediaFormat format = mEnc.getOutputFormat(),
format就包含了CODEC_CONFIG。此时的format可直接作为addTrack()的参数使用。
  • There are no sync frames for video track
    这是因为没有设置关键帧的flag。
switch (nalType & 0x1f) {case H264Decoder.NAL_TYPE_I:bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;break;
}
  • 音视频同步
    方法里面传入的时间参数PTS的单位时:microseconds,微妙。
 long pts = System.nanoTime() / 1000;

直接使用当前时间戳会有问题,录制成的MP4总时间会很大。我使用一个相对时间,当前时间相对于开始的时间差。

bufferInfo.presentationTimeUs = System.nanoTime()/1000-startPts;

使用中一些需要注意的地方

1. MediaFormat 可以在初始化编码器的时候获取
mediaformat = MediaFormat.createVideoFormat("video/avc", VIDEO_WIDTH, VIDEO_HEIGHT);
2. 写入数据时候的inputBuffer 和 bufferInfo 需要自己构造

三、注意

  1. 不熟悉cmake编译的同学,可以去查查资料,反正我在项目里面使用so文件遇到过比较大的坑,因为一直不熟悉cmake的语法,链接库都不会。
  2. 还有就是对视频流数据结构的理解,这玩意是一帧一帧组成的。不同类型的帧需要不同的处理。比如第一帧需要传入I帧,你得先去将视频流分割成一帧一帧的,再去判断帧的类型。不太熟悉的同学可以传送门一下。切记传入数据以帧为单位

四、Q&S

  1. 录制的视频或快或慢。
    在 MP4Encoder::CreateMP4File本地方法中有一个m_nFrameRate变量,控制帧率的,也就是每分钟多少帧,这里可以自己去控制,和录制视频的帧率一致就行了,这里默认的是25帧。
  2. 录制的本地mp4视频预览画面是黑色或者是绿色的
    造成的原因是录制视频流的时候第一帧不是关键帧(I帧),所以在使用writeH264Data方法的时候,记得第一帧传入关键帧。
  3. download下载的项目无法运行
    。。。这个,你就自己去配置编译环境了,代码都在这了。
  4. 录制成的MP4第一帧模糊
    这是因为写数据的时候没有进行关键帧的判断,第一帧写入关键帧就不会有这个问题了。

五、引用

Android在MediaMuxer和MediaCodec用例:https://www.cnblogs.com/hrhguanli/p/5043610.html
Grafika: https://github.com/google/grafika
HWEncoderExperiments:https://github.com/OnlyInAmerica/HWEncoderExperiments/tree/audioonly/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments

Android使用Mp4v2用h264流和aac流合成mp4相关推荐

  1. Mp4v2实现h264+aac打包成Mp4视频文件

    使用mp4v2实现录制mp4视频,需要准备如下信息: 1.获取mp4v2源码并编译成库文件,对于mp4v2的编译可以看前面的文章android 编译mp4v2 2.0.0生成动态库 : 2.获取h26 ...

  2. mp4v2 写mp4 java_使用mp4v2将H264+AAC合成mp4文件

    录制程序要添加新功能:录制CMMB电视节目,我们的板卡发送出来的是RTP流(H264视频和AAC音频),录制程序要做的工作是: (1)接收并解析RTP包,分离出H264和AAC数据流: (2)将H26 ...

  3. 使用mp4v2将H264+AAC合成mp4文件

    录制程序要添加新功能:录制CMMB电视节目,我们的板卡发送出来的是RTP流(H264视频和AAC音频),录制程序要做的工作是: (1)接收并解析RTP包,分离出H264和AAC数据流: (2)将H26 ...

  4. 嵌入式 使用mp4v2将H264+AAC合成mp4文件

    录制程序要添加新功能:录制CMMB电视节目,我们的板卡发送出来的是RTP流(H264视频和AAC音频),录制程序要做的工作是: (1)接收并解析RTP包,分离出H264和AAC数据流: (2)将H26 ...

  5. Android直播开发之旅(17):使用FFmpeg提取MP4中的H264和AAC

    最近在开发中遇到了一个问题,即无法提取到MP4中H264流的关键帧进行处理,且保存到本地的AAC音频也无法正常播放.经过调试分析发现,这是由于解封装MP4得到的H264和AAC是ES流,它们缺失解码时 ...

  6. <整理总结>H264/265码流数据包格式分析(带mp4v2封装H264/265为MP4的源码示例)

    H264/265码流数据包格式分析 前言: 一.H.264码流解析 I帧P帧B帧说明: 二.H.265码流解析 三.主要源码 前言: 最近在学习使用MP4v2将H264/H265码流以及AAC音频封装 ...

  7. java h264 aac合成_使用mp4v2将H264+AAC合成mp4文件

    录制程序要添加新功能:录制CMMB电视节目,我们的板卡发送出来的是RTP流(H264视频和AAC音频),录制程序要做的工作是: (1)接收并解析RTP包,分离出H264和AAC数据流: (2)将H26 ...

  8. hls直播实现源码V2(h264,aac流输入,ffmpeg实现,MFC,VC环境,ts,m3u8)

    周星驰来也! 像很多80,90后年轻人(老男人吧)一样,周星驰一直就是哥喜爱的电影演员之一,不仅喜欢星爷的人物形象,更是喜欢这种乐观向上的精神!(哥16年的QQ一直就是周星驰这个昵称). 转入主题吧: ...

  9. FFmpeg将H264和AAC合成为MP4

    前言 之前的内容提到使用Mp4V2合成Mp4文件失败,只能换方案使用FFmpeg去尝试了. 一.下载编译FFmpeg 在网上下载到了FFmpeg源码库,一开始使用的是 ffmpeg-4.1.3 版本, ...

最新文章

  1. linux学习笔记一
  2. dataframe转换成dict和列表的技巧
  3. 企业为什么要建设数据分析平台?应该怎么建?
  4. -mmin find shell 报错_[shell]find用法小结
  5. 文件读、写、追加的区别 Python
  6. Tuple和 ValueTuple
  7. 利用IP标准访问列表进行网络流量的控制
  8. 如何用计算机计算一元三次方程,计算器如何解一元三次方程?
  9. 微信小程序-weUI组件库
  10. win10中修改mac地址
  11. 基于asp.net338医院体检信息管理系统
  12. 十字隧道工程盾构机穿过马骝洲水道
  13. C#学习笔记 正则表达式
  14. PADS-电阻、电容、电感PCB封装
  15. 通过Guest账号共享XP上的打印机
  16. pd.read_excel出现ValueError: Unknown engine: openpyxl解决方案
  17. Windows7下固态硬盘安装Linux Mint 18双系统
  18. 启动win7系统开机黑屏提示0xc000000f错误代码怎么办
  19. 印刷机是怎么工作的?
  20. thinkpad x230评测_联想thinkpadx230怎么样 联想thinkpadx230详细评测

热门文章

  1. 团队和做的直观图_直观,可靠的日期和时间处理,终于出现在Java中
  2. AtCoder - 2365 Camel and Oases
  3. 充电枪cp信号控制板_通过充电枪CP信号控制常电输入的电路的制作方法
  4. 双栈路由Linux,IPV6 IPV4双栈互通与静态路由
  5. 不看会后悔系列之好看又好用的样机模板推荐!
  6. 硬件中常说的EMC是啥?
  7. AltiumDesigner 绘制PCB常见问题
  8. java字体怎么带下划线_链接的字体及下划线等
  9. 全球与中国干式涡旋泵市场深度研究分析报告
  10. 方文室随记12柳永13秦朝