• T31开发笔记:MP4录制


    若该文为原创文章,转载请注明原文出处

    使用MP4V2把H264和AAC文件封装成MP4文件。

    一、硬件和开发环境

    1、硬件:T31X+SC5235 

    2、开发环境: ubuntu16.04-64bit

    3、编译器:mips-gcc540-glibc222-32bit-r3.3.0.tar.gz

    注:板子和和WIFI模块是某淘上淘的,使用的是RTL8188,使用的是USB接口,uboot和内核是自己裁剪移植的,内核默认自带WIFI驱动,所以不用移植可以直接使用。

    二、MP4V2下载及编译

    1、MP4V2下载

    mp4v2官网连接: https://launchpad.net/ubuntu/artful/+source/mp4v2

     

     2、MP4V2编译

    1. 1、解压:
    2. tar xvf mp4v2_2.0.0_dfsg0.orig.tar.bz2
    3. 2、进入文件:
    4. cd mp4v2-2.0.0
    5. 3、创建install文件
    6. mkdir install
    7. 3、配置
    8. ./configure --host=mips-linux CC=ips-linux-gnu-gcc CXX=mips-linux-gnu-g++ --prefix=/home/yifeng/mp4v2-2.0.0/install --disable-option-checking --disable-debug --disable-optimize --disable-fvisibility --disable-gch --disable-largefile --disable-util --disable-dependency-tracking --disable-libtool-lock --enable-shared --enable-static
    9. 4、编译
    10. make
    11. 5、安装
    12. make install

    编译完成后,会在install生成lib文件,扡生成的动态库拷贝到开发板的/usr/lib下。

    至此:编译完成。

    三、测试

    说明:把h264和aac文件封装成MP4文件,不是实时采集h264和aac。

    完整代码:

    1. #include <stdio.h>
    2. #include <string.h>
    3. #include <stdlib.h>
    4. #include <mp4v2/mp4v2.h>
    5. struct AdtsHeader
    6. {
    7. unsigned int syncword; //12 bit 同步字 '1111 1111 1111',说明一个ADTS帧的开始
    8. unsigned int id; //1 bit MPEG 标示符, 0 for MPEG-41 for MPEG-2
    9. unsigned int layer; //2 bit 总是'00'
    10. unsigned int protectionAbsent; //1 bit 1表示没有crc,0表示有crc
    11. unsigned int profile; //1 bit 表示使用哪个级别的AAC
    12. unsigned int samplingFreqIndex; //4 bit 表示使用的采样频率
    13. unsigned int privateBit; //1 bit
    14. unsigned int channelCfg; //3 bit 表示声道数
    15. unsigned int originalCopy; //1 bit
    16. unsigned int home; //1 bit
    17. /*下面的为改变的参数即每一帧都不同*/
    18. unsigned int copyrightIdentificationBit; //1 bit
    19. unsigned int copyrightIdentificationStart; //1 bit
    20. unsigned int aacFrameLength; //13 bit 一个ADTS帧的长度包括ADTS头和AAC原始流
    21. unsigned int adtsBufferFullness; //11 bit 0x7FF 说明是码率可变的码流
    22. /* number_of_raw_data_blocks_in_frame
    23. * 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧
    24. * 所以说number_of_raw_data_blocks_in_frame == 0
    25. * 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
    26. */
    27. unsigned int numberOfRawDataBlockInFrame; //2 bit
    28. };
    29. static int parseAdtsHeader(uint8_t* in, struct AdtsHeader* res)
    30. {
    31. static int frame_number = 0;
    32. memset(res,0,sizeof(*res));
    33. if ((in[0] == 0xFF)&&((in[1] & 0xF0) == 0xF0))
    34. {
    35. res->id = ((unsigned int) in[1] & 0x08) >> 3;
    36. res->layer = ((unsigned int) in[1] & 0x06) >> 1;
    37. res->protectionAbsent = (unsigned int) in[1] & 0x01;
    38. res->profile = ((unsigned int) in[2] & 0xc0) >> 6;
    39. res->samplingFreqIndex = ((unsigned int) in[2] & 0x3c) >> 2;
    40. res->privateBit = ((unsigned int) in[2] & 0x02) >> 1;
    41. res->channelCfg = ((((unsigned int) in[2] & 0x01) << 2) | (((unsigned int) in[3] & 0xc0) >> 6));
    42. res->originalCopy = ((unsigned int) in[3] & 0x20) >> 5;
    43. res->home = ((unsigned int) in[3] & 0x10) >> 4;
    44. res->copyrightIdentificationBit = ((unsigned int) in[3] & 0x08) >> 3;
    45. res->copyrightIdentificationStart = (unsigned int) in[3] & 0x04 >> 2;
    46. res->aacFrameLength = (((((unsigned int) in[3]) & 0x03) << 11) |
    47. (((unsigned int)in[4] & 0xFF) << 3) |
    48. ((unsigned int)in[5] & 0xE0) >> 5) ;
    49. res->adtsBufferFullness = (((unsigned int) in[5] & 0x1f) << 6 |
    50. ((unsigned int) in[6] & 0xfc) >> 2);
    51. res->numberOfRawDataBlockInFrame = ((unsigned int) in[6] & 0x03);
    52. return 0;
    53. }
    54. else
    55. {
    56. printf("failed to parse adts header\n");
    57. return -1;
    58. }
    59. }
    60. int getNalu(FILE *pFile, unsigned char *pNalu)
    61. {
    62. unsigned char c;
    63. int pos = 0;
    64. int len;
    65. if(!pFile)
    66. return -1;
    67. if((len = fread(pNalu, 1, 4, pFile)) <= 0)
    68. return -1;
    69. if(pNalu[0] != 0 || pNalu[1] != 0 || pNalu[2] != 0 || pNalu[3] != 1)
    70. return -1;
    71. pos = 4;
    72. while(1)
    73. {
    74. if(feof(pFile))
    75. break;
    76. pNalu[pos] = fgetc(pFile);
    77. if(pNalu[pos-3] == 0 && pNalu[pos-2] == 0 && pNalu[pos-1] == 0 && pNalu[pos] == 1)
    78. {
    79. fseek(pFile, -4, SEEK_CUR);
    80. pos -= 4;
    81. break;
    82. }
    83. pos++;
    84. }
    85. len = pos+1;
    86. return len;
    87. }
    88. static int dump_frame(uint8_t* p_frame, uint32_t size)
    89. {
    90. printf("*********************************************************:%u\n", size);
    91. if(p_frame != NULL && size >0)
    92. {
    93. uint32_t i=0;
    94. for(; i<100; i++)
    95. {
    96. printf("%x ", p_frame[i]);
    97. if((i+1)%16 == 0)
    98. {
    99. printf("\n");
    100. }
    101. }
    102. }
    103. printf("\n");
    104. }
    105. int packet2Mp4(const char *inputh264File, const char *inputaacFile, const char *outputFiles)
    106. {
    107. FILE *pInh264 = NULL;
    108. FILE *pInAac = NULL;
    109. unsigned char *pBuf = malloc(1024*1024);
    110. unsigned char *frame = malloc(5000);
    111. unsigned char *pNalu = NULL;
    112. unsigned char naluType;
    113. int len;
    114. int num = 0;
    115. MP4FileHandle pHandle = NULL;
    116. MP4TrackId videoId;
    117. MP4TrackId audioId;
    118. int width = 480;
    119. int height = 288;
    120. int frameRate = 25;
    121. int timeScale = 90000;
    122. int addStream = 1;
    123. int ret = 0;
    124. struct AdtsHeader adtsHeader;
    125. int videoCnt = 0;
    126. int audioCnt = 0;
    127. pInh264 = fopen(inputh264File, "rb");
    128. if(!pInh264)
    129. return -1;
    130. pInAac = fopen(inputaacFile, "rb");
    131. if(!pInAac)
    132. return -1;
    133. pHandle = MP4Create(outputFiles, 0);
    134. if(pHandle == MP4_INVALID_FILE_HANDLE)
    135. {
    136. printf("ERROR:Create mp4 handle fialed.\n");
    137. return -1;
    138. }
    139. MP4SetTimeScale(pHandle, timeScale);
    140. // MP4_MPEG4_AAC_MAIN_AUDIO_TYPE
    141. audioId = MP4AddAudioTrack(pHandle, 48000, 1024, MP4_MPEG4_AUDIO_TYPE);
    142. if (audioId == MP4_INVALID_TRACK_ID)
    143. {
    144. printf("add audio track fialed.\n");
    145. return -1;
    146. }
    147. MP4SetAudioProfileLevel(pHandle, 0x2);
    148. while(1)
    149. {
    150. videoCnt++;
    151. audioCnt++;
    152. if(videoCnt >= 40)
    153. {
    154. videoCnt = 0;
    155. /* 视频处理 */
    156. len = getNalu(pInh264, pBuf);
    157. if (len <= 0)
    158. break;
    159. if (pBuf[0] != 0 || pBuf[1] != 0 || pBuf[2] != 0 || pBuf[3] != 1)
    160. continue;
    161. len -= 4;
    162. pNalu = pBuf+4;
    163. naluType = pNalu[0]&0x1F;
    164. switch (naluType)
    165. {
    166. case 0x07: // SPS
    167. printf("------------------------------------\n");
    168. printf("sps(%d)\n", len);
    169. if (addStream)
    170. {
    171. videoId = MP4AddH264VideoTrack
    172. (pHandle,
    173. timeScale, // 涓€绉掗挓澶氬皯timescale
    174. timeScale/frameRate, // 姣忎釜甯ф湁澶氬皯涓猼imescale
    175. width, // width
    176. height, // height
    177. pNalu[1], // sps[1] AVCProfileIndication
    178. pNalu[2], // sps[2] profile_compat
    179. pNalu[3], // sps[3] AVCLevelIndication
    180. 3); // 4 bytes length before each NAL unit
    181. if (videoId == MP4_INVALID_TRACK_ID)
    182. {
    183. printf("Error:Can't add track.\n");
    184. return -1;
    185. }
    186. printf("pNalu[1] = %x, pNalu[2] = %x, pNalu[3] = %x\n", pNalu[1], pNalu[2], pNalu[3]);
    187. MP4SetVideoProfileLevel(pHandle, 0x7F);
    188. addStream = 0;
    189. }
    190. dump_frame(pNalu, len);
    191. MP4AddH264SequenceParameterSet(pHandle, videoId, pNalu, len);
    192. break;
    193. case 0x08: // PPS
    194. printf("pps(%d)\n", len);
    195. dump_frame(pNalu, len);
    196. MP4AddH264PictureParameterSet(pHandle, videoId, pNalu, len);
    197. break;
    198. default:
    199. printf("slice(%d)\n", len);
    200. pBuf[0] = (len>>24)&0xFF;
    201. pBuf[1] = (len>>16)&0xFF;
    202. pBuf[2] = (len>>8)&0xFF;
    203. pBuf[3] = (len>>0)&0xFF;
    204. dump_frame(pBuf, len);
    205. MP4WriteSample(pHandle, videoId, pBuf, len+4, MP4_INVALID_DURATION, 0, 1);
    206. break;
    207. }
    208. }
    209. if(audioCnt >= 21)
    210. {
    211. audioCnt = 0;
    212. /* 音频处理 */
    213. ret = fread(frame, 1, 7, pInAac);
    214. if(ret <= 0)
    215. {
    216. break;
    217. }
    218. if(parseAdtsHeader(frame, &adtsHeader) < 0)
    219. {
    220. printf("parse err\n");
    221. break;
    222. }
    223. ret = fread(frame, 1, adtsHeader.aacFrameLength-7, pInAac);
    224. if(ret < 0)
    225. {
    226. printf("read err\n");
    227. break;
    228. }
    229. printf("audio frame:\n");
    230. dump_frame(frame, len);
    231. MP4WriteSample(pHandle, audioId, frame, adtsHeader.aacFrameLength-7 , MP4_INVALID_DURATION, 0, 1);
    232. }
    233. }
    234. free(pBuf);
    235. free(frame);
    236. fclose(pInh264);
    237. fclose(pInAac);
    238. MP4Close(pHandle, 0);
    239. return 0;
    240. }
    241. int main(int argc, char *argv[])
    242. {
    243. if (packet2Mp4("output.h264", "output.aac", "test.mp4"))
    244. {
    245. printf("Error:Packet to Mp4 fail.\n");
    246. return -1;
    247. }
    248. return 0;
    249. }

    编译:

    mips-linux-gnu-gcc h264_aac_to_mp4.c -o h264_aac_to_mp4 -lpmv4p2

    把可执行文件和h264,aac文件拷贝到开发板下运行,生成的mp4可以在vlc上播放。

     

    四、总结

    1、mpv2库交叉编译。

    2、使用mpv2静态库,执行文件太大,建议使用动态库。

    3、MP4格式学习。

    如有侵权,请及时联系博主删除,VX:18750903063

  • 相关阅读:
    python学习记录
    金仓数据库KingbaseES服务器应用参考手册--2. initdb
    如何去占用windows端口
    这么多年你还在怕正则吗?
    电脑监控软件解读
    leetcodehot 100 ----滑动窗口
    1404. 将二进制表示减到1的步骤数
    【echarts】解决ECharts鼠标悬停(mouseover)事件触发范围问题
    发挥项目“临时性”威力,让项目顺利实施
    httprunner实战接口测试笔记,拿走不谢
  • 原文地址:https://blog.csdn.net/weixin_38807927/article/details/126830171