问题:

通过rtsp交互,解析RTP流(h264视频+alaw(pcma)音频), 将其保存为MP4格式。

解决方案:

使用MP4V2 ,版本2.0.0, VC2010可编译。(还有一种gpac库,听说支持h265,未实验)

Mp4V2下载地址: https://code.google.com/archive/p/mp4v2/downloads  ,  文件名mp4v2-2.0.0.tar.bz2

参考文章: 使用mp4v2将H264+AAC合成mp4文件

参考文章: 将RTSP流录制为mp4文件

参考文章:  linux下利用mp4v2库将h264和aac文件封装成MP4

参考文章: Android音视频系列:视频容器操作篇 -- mp4容器打包实现

参考文章: Mp4V2调试经验记录   (视频或者音画同步的主要参数是duration)

参考文章:mp4v2 接口函数  : 其中有一些注意的地方,我似乎没有理会。

PS: 考虑过FFmpeg,但是太大,Windows下编译麻烦,不会裁剪。Mp4V2比较轻量级,适合做一个SDK。

前期:

建议先对MP4格式有一点基本了解, 看看一些MP4格式的文档,有一个基本概念。

参考文章: mp4文件格式解析 , 介绍详细,其中推荐mp4v2 和 gpac,和一个在线MP4格式解析器,

参考文章(代码): 逐个Box解析MP4文件的代码,挺好的。 MP4 file analyzer  (This is a console application, written by MSVC 2008.It will display mp4 file content in human readable format.)

套路:


//创建mp4文件
MP4FileHandle file = MP4CreateEx("d:\\test.mp4", MP4_CREATE_64BIT_DATA | MP4_CREATE_64BIT_TIME);MP4SetTimeScale(file, 90000);MP4TrackId video = MP4AddH264VideoTrack(file, 90000, 90000/_fps, _width, _height,0x64, //sps[1] AVCProfileIndication0x00, //sps[2] profile_compat0x1f, //sps[3] AVCLevelIndication3); // 4 bytes length before each NAL unit
MP4SetVideoProfileLevel(file, 0x7F);//源码重新编译,新加函数MP4AddALawAudioTrack2
MP4TrackId audio = MP4AddALawAudioTrack2(file,8000,   //timescale8000*40/1000);   //sampleDuration.  40msMP4SetTrackIntegerProperty(file,audio, "mdia.minf.stbl.stsd.alaw.channels",1);
MP4SetAudioProfileLevel(file, 0x2);while(1)
{//更新SPSMP4AddH264SequenceParameterSet(file, video, (const uint8_t*)(pH264+4), len);//更新PPSMP4AddH264PictureParameterSet(file, video, (const uint8_t*)(pH264+4), len);//更新H264 (前四个字节需要特殊处理)if(key_frame){MP4WriteSample(file, video, (const uint8_t* )pH264, len, MP4_INVALID_DURATION, 0, 1);}else{MP4WriteSample(file, video, (const uint8_t* )pH264, len, MP4_INVALID_DURATION, 0, 0);}//更新pcmaMP4WriteSample(file, audio, (const uint8_t* )pCMA, len , MP4_INVALID_DURATION, 0, 1);}MP4Close(file);

PS:      注意Write H264 Sample时,h264流中的NAL,头四个字节是0x00000001,  而mp4中的h264track,头四个字节要求是NAL的长度,并且是大端顺序,所以,写入前,需要进行如下更改:

uint32_t* pSize = (uint32_t*)pH264 ;
*pSize = htonl(len - 4);

PS:   源码中函数 AddALawAudioTrack里面计算

uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample

然而实际的1秒25帧, 间隔40ms, 而不是20ms。故而需要手动添加一个新的接口。

PS:   音频和视频同步没有特别处理,没考虑。

PS:   SPS,PPS 不知道是否需要每次变更都需要进行  MP4AddH264SequenceParameterSet  ,  MP4AddH264PictureParameterSet操作。 不确定这个操作的影响。

简单实验结果:

测试步骤1  :  VLC可播放, 有声音。

测试步骤2: 手机端微信可播放。

测试步骤3: 桌面端微信不可播放。 (猜测可能是音频格式需要AAC格式)

测试步骤4: Html5 可播放,有声音。

测试步骤5: Windows Media Player可播放, 但是没声音。

困扰: 发现MP4文件中的mdat 数据特别长, 不知道是否有影响, 这份生成的MP4文件不支持格式工厂转换。

其他:

感觉MP4似乎主要用来打包H264和AAC的,而我用的音频是PCMA,不知道有没有兼容性问题。

mp4格式好复杂,结构可变,又多,  里面有好多Box结构体,我曾经企图一个字节一个字节的研究,后来放弃了,复杂。

附图(图片来自网络):

参考源码:  (来源: https://github.com/Thinkerfans/lib-mp4v2/tree/master/mp4v2)

lib-mp4v2/mp4v2/mp4record.h

//
//  mp4record.h
//  RTSP_Player
//
//  Created by apple on 15/4/7.
//  Copyright (c) 2015年 thinker. All rights reserved.
//#ifndef __RTSP_Player__mp4record__
#define __RTSP_Player__mp4record__#include "mp4v2.h"#define  _NALU_SPS_  0
#define  _NALU_PPS_  1
#define  _NALU_I_    2
#define  _NALU_P_    3int initMp4Encoder(const char * filename,int width,int height);
int mp4VEncode(uint8_t * data ,int len);
int mp4AEncode(uint8_t * data ,int len);
void closeMp4Encoder();#endif /* defined(__RTSP_Player__mp4record__) */

lib-mp4v2/mp4v2/mp4record.c

//
//  mp4record.c
//  RTSP_Player
//
//  Created by apple on 15/4/7.
//  Copyright (c) 2015年 thinker. All rights reserved.
//#include "mp4record.h"
#include <stdlib.h>typedef struct MP4V2_CONTEXT{int m_vWidth,m_vHeight,m_vFrateR,m_vTimeScale;MP4FileHandle m_mp4FHandle;MP4TrackId m_vTrackId,m_aTrackId;double m_vFrameDur;} MP4V2_CONTEXT;struct MP4V2_CONTEXT * recordCtx = NULL;int initMp4Encoder(const char * filename,int width,int height){int ret = -1;recordCtx = malloc(sizeof(struct MP4V2_CONTEXT));if (!recordCtx) {printf("error : malloc context \n");return ret;}recordCtx->m_vWidth = width;recordCtx->m_vHeight = height;recordCtx->m_vFrateR = 25;recordCtx->m_vTimeScale = 90000;recordCtx->m_vFrameDur = 300;recordCtx->m_vTrackId = 0;recordCtx->m_aTrackId = 0;recordCtx->m_mp4FHandle = MP4Create(filename,0);if (recordCtx->m_mp4FHandle == MP4_INVALID_FILE_HANDLE) {printf("error : MP4Create  \n");return ret;}MP4SetTimeScale(recordCtx->m_mp4FHandle, recordCtx->m_vTimeScale);//------------------------------------------------------------------------------------- audio track
//    recordCtx->m_aTrackId = MP4AddAudioTrack(recordCtx->m_mp4FHandle, 44100, 1024, MP4_MPEG4_AUDIO_TYPE);
//    if (recordCtx->m_aTrackId == MP4_INVALID_TRACK_ID){
//        printf("error : MP4AddAudioTrack  \n");
//        return ret;
//    }
//
//    MP4SetAudioProfileLevel(recordCtx->m_mp4FHandle, 0x2);
//    uint8_t aacConfig[2] = {18,16};
//    MP4SetTrackESConfiguration(recordCtx->m_mp4FHandle,recordCtx->m_aTrackId,aacConfig,2);
//    printf("ok  : initMp4Encoder file=%s  \n",filename);return 0;
}
int mp4VEncode(uint8_t * _naluData ,int _naluSize){int index = -1;if(_naluData[0]==0 && _naluData[1]==0 && _naluData[2]==0 && _naluData[3]==1 && _naluData[4]==0x67){index = _NALU_SPS_;}if(index!=_NALU_SPS_ && recordCtx->m_vTrackId == MP4_INVALID_TRACK_ID){return index;}if(_naluData[0]==0 && _naluData[1]==0 && _naluData[2]==0 && _naluData[3]==1 && _naluData[4]==0x68){index = _NALU_PPS_;}if(_naluData[0]==0 && _naluData[1]==0 && _naluData[2]==0 && _naluData[3]==1 && _naluData[4]==0x65){index = _NALU_I_;}if(_naluData[0]==0 && _naluData[1]==0 && _naluData[2]==0 && _naluData[3]==1 && _naluData[4]==0x41){index = _NALU_P_;}//switch(index){case _NALU_SPS_:if(recordCtx->m_vTrackId == MP4_INVALID_TRACK_ID){recordCtx->m_vTrackId = MP4AddH264VideoTrack(recordCtx->m_mp4FHandle,recordCtx->m_vTimeScale,recordCtx->m_vTimeScale / recordCtx->m_vFrateR,recordCtx->m_vWidth,     // widthrecordCtx->m_vHeight,    // height_naluData[5], // sps[1] AVCProfileIndication_naluData[6], // sps[2] profile_compat_naluData[7], // sps[3] AVCLevelIndication3);           // 4 bytes length before each NAL unitif (recordCtx->m_vTrackId == MP4_INVALID_TRACK_ID)  {return -1;}MP4SetVideoProfileLevel(recordCtx->m_mp4FHandle, 0x7F); //  Simple Profile @ Level 3}MP4AddH264SequenceParameterSet(recordCtx->m_mp4FHandle,recordCtx->m_vTrackId,_naluData+4,_naluSize-4);//break;case _NALU_PPS_:MP4AddH264PictureParameterSet(recordCtx->m_mp4FHandle,recordCtx->m_vTrackId,_naluData+4,_naluSize-4);break;case _NALU_I_:{uint8_t * IFrameData = malloc(_naluSize+1);//IFrameData[0] = (_naluSize-3) >>24;IFrameData[1] = (_naluSize-3) >>16;IFrameData[2] = (_naluSize-3) >>8;IFrameData[3] = (_naluSize-3) &0xff;memcpy(IFrameData+4,_naluData+3,_naluSize-3);
//            if(!MP4WriteSample(recordCtx->m_mp4FHandle, recordCtx->m_vTrackId, IFrameData, _naluSize+1, recordCtx->m_vFrameDur/44100*90000, 0, 1)){
//                return -1;
//            }
//            recordCtx->m_vFrameDur = 0;if(!MP4WriteSample(recordCtx->m_mp4FHandle, recordCtx->m_vTrackId, IFrameData, _naluSize+1, MP4_INVALID_DURATION, 0, 1)){return -1;}free(IFrameData);//break;}case _NALU_P_:{_naluData[0] = (_naluSize-4) >>24;  _naluData[1] = (_naluSize-4) >>16;  _naluData[2] = (_naluSize-4) >>8;  _naluData[3] = (_naluSize-4) &0xff;//            if(!MP4WriteSample(recordCtx->m_mp4FHandle, recordCtx->m_vTrackId, _naluData, _naluSize, recordCtx->m_vFrameDur/44100*90000, 0, 1)){
//                return -1;
//            }
//            recordCtx->m_vFrameDur = 0;if(!MP4WriteSample(recordCtx->m_mp4FHandle, recordCtx->m_vTrackId, _naluData, _naluSize, MP4_INVALID_DURATION, 0, 1)){return -1;}break;}}return 0;
}int mp4AEncode(uint8_t * data ,int len){if(recordCtx->m_vTrackId == MP4_INVALID_TRACK_ID){return -1;}MP4WriteSample(recordCtx->m_mp4FHandle, recordCtx->m_aTrackId, data, len , MP4_INVALID_DURATION, 0, 1);recordCtx->m_vFrameDur += 1024;return 0;
}void closeMp4Encoder(){if(recordCtx){if (recordCtx->m_mp4FHandle != MP4_INVALID_FILE_HANDLE) {MP4Close(recordCtx->m_mp4FHandle,0);recordCtx->m_mp4FHandle = NULL;}free(recordCtx);recordCtx = NULL;}printf("ok  : closeMp4Encoder  \n");}

将 H264 + PCMA 转为MP4格式(RTSP协议)相关推荐

  1. 如何快速把mkv转为mp4格式,并带(保留)字幕!可在iPhone上播放

    mkv文件其实属于封装格式,包括视频.音频.字幕等文件,像一些PR等剪辑软件就不能直接调用,而且很多播放器在识别这些文件时也可能因为兼容性问题导致不能播放.解决办法很简单,把mkv文件转为mp4格式就 ...

  2. 安卓手机m3u8转为mp4格式100%有效的方法

    本文以图片形式手把手演示,请读者耐心 首先推荐两款安卓手机上全网视频下载神器 X浏览器和QQ浏览器 虽然我平时用安卓手机的X浏览器(这款浏览器强烈推荐使用)的嗅探资源媒体功能就已经能下载90%的网络视 ...

  3. qlv格式的视频如何转为mp4格式(爬虫小技巧)

    qlv格式的视频是腾讯专属的加密视频,非要在腾讯视频中才能打开,万一身边没有个腾讯视频播放器,打不开岂不是很尴尬...还是转为mp4格式吧 全网搜方法之后,除了命令行转,就是转换器转. 命令行转:新版 ...

  4. 腾讯视频下载视频QLV格式转为MP4格式

    腾讯视频下载视频QLV格式转为MP4格式(最新) 腾讯视频跟新之后缓存路径 现在里面就没有vodcache文件夹了,所以也没有tdl文件了,那么之前大部分教程里在cmd命令中用copy/E 0*.td ...

  5. ffmpeg将视频flv格式转为mp4格式

    前言 最近使用bilibili哔哩哔哩下载助手下载B站的视频时,发现下载的视频的格式都是.flv,因此想利用网络上的在线转换工具将flv转为mp4格式,无奈转换后的mp4视频,下载速度感人,于是使用f ...

  6. 如何快速把mkv转为mp4格式,并保留字幕!

    mkv文件其实属于封装格式,包括视频.音频.字幕等文件,像一些PR等剪辑软件就不能直接调用,而且很多播放器在识别这些文件时也可能因为兼容性问题导致不能播放.解决办法很简单,把mkv文件转为mp4格式就 ...

  7. 将爱奇艺视频QSV格式转为MP4格式(亲测可用)

    编写不易,如有转载,请声明出处:http://blog.csdn.net/zxc514257857/article/details/71189451 1,通过爱奇艺播放器下载的视频文件格式均为QSV格 ...

  8. 使用ffmpeg开源库将h264封装为mp4格式

    最近一直在做使用ffmpeg关于读取标准h264格式内存如何封装为mp4格式文件,在经过一周的持续奋战之后在网上找了一些代码,特别的雷神的博客让我获益匪浅,开始不知道如何持续读取发送来的内存块,如何边 ...

  9. 亲测可行!!!将下载好的优酷文件kux格式转为MP4格式

    近来,在做一个PPT时,需要插入一些视频. 大家懂的,各大视频网址上面下载出来的视频格式,千奇百怪,各种加密. 通过网页搜寻各种视频格式转换方法无果后,皇天不负有心人让我找到了一款软件可以有效的实现视 ...

最新文章

  1. Python Django 参数解包及代码示例
  2. 面向站长和网站管理员的Web缓存加速指南
  3. web处理高并发措施
  4. 大家一起做训练 第一场 A Next Test
  5. 嵌入式Linux系统编程学习之二十无名管道(PIPE)
  6. OpenCV-python学习笔记(二)——image processing图像基本处理
  7. div水平居中和垂直居中
  8. MapInfo MIF/MID文件格式描述
  9. iPhonexr安兔兔html5测试,iPhone XR安兔兔跑分数据出炉,跟XS对比相差大吗?
  10. 关于对ffmpeg中SAR/DAR/PAR的理解
  11. android vivo oppo 真机调试 apk、安装失败,系统老是报“解析包时出现问题”
  12. 3Dmax制作锁模型教程
  13. 中文拼音转换成CMU的音素工具
  14. Typora超级纯净免费记笔记软件分享给大家
  15. HTML网页设计与制作:电影网站设计——电影泰坦尼克号(4页) HTML+CSS+JavaScript
  16. 十六款优秀任务管理软件,哪款适合您呢?
  17. 《计算机网络技术》第一章测试(题目及答案)
  18. 《电子元器件的可靠性》——2.4节电子元器件的失效规律
  19. 一.图像处理系统MATLAB实现(GUI界面)
  20. 第1章 Linux系统介绍与环境搭建准备

热门文章

  1. 世界上最健康的生活方式
  2. 【C++】哈希与哈希冲突
  3. 《University Calculus》-chaper13-向量场中的积分-线积分
  4. 基于matlab人体行为识别和检测的研究
  5. 使用JWT和Spring Security保护REST API
  6. 如何在PDF文档中插入直线
  7. 软件测试面试题:1.怎样看待加班问题?
  8. overflow溢出
  9. handlebar的helper应用
  10. Coursera机器学习吴恩达-Week1