RV1126_对H265编码数据进行mp4存储
一、基于FFMPEG的封装
把H264或H265封装成mp4,有很多第三方库可供选择,使用最方便最多的就是FFMPEG。
封装mp4,需要了解各个box结构所包含的信息及作用。所有的数据全部存放在一个个叫box的结构中。
了解各个box的功能,是进行封装的第一步。具体关于mp4各个box功能的介绍,网上可以参考的文章都特别详细。
但是在用FFMPEG进行mp4封装过程中,程序如果中途程序退出,或设备断电,将会导致mp4文件不能正常播放。
原因是mp4封装的与帧相关的长度,偏移位置都记录在最后一个moov Box中,如果该tag记录没有或不完整,播放器就没法读取到视频帧。
通过mp4info程序可以看到一个标准ffmpeg封装的mp4格式各个标签:
其中的moov是封装mp4最关键的Box。
二、避免文件损坏
解决程序中途退出,或设备断电引起的mp4文件不完整,只需要将moov Box和记录视频帧纯数据的mdat Box交换顺序。确保mdat中的数据都是以追加方式写入文件。moov box在固定空间大小内进行修改,每当有视频帧写入,就实时更新该box。不管什么时候断电,都不会对之前的数据造成影响,最多只会影响当前帧的完整性。如下图结构:
三、RV1126编码数据与海思系列的区别
海思是把各个nalu数据分别打包到一个指针数组中,处理起来比较灵活。
RV和海思系列的编码数据不同之处在于RK通过RK_MPI_MB_GetPtr获取到I帧指针后,需要根据nalu_type : 0x20 , 0x21 ,0x22依次去遍历vps,sps,pps。提取出来并放到mp4的moov Box中。为了实现mp4封装代码库的统一,先把RK获取到的编码数据经过函数转换到和海思对应的结构体中,保证底层使用同一套代码。
在调用mp4封装代码前通过以下函数转换:检测各个vps,sps,pps ,返回其对应的地址指针,和对应的长度
```c
static char * find_nalu_tag(char *pu8Buf,int size, int *nalu_len){
int i = 0;
hi_bool bNewPic = HI_FALSE;
hi_bool bFindStart = HI_FALSE;
hi_bool bFindEnd = HI_FALSE;
for(i=0; i < size-4; i++) {
bNewPic = (pu8Buf[i+0] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 0 && pu8Buf[i+3] == 1);
if (bNewPic) {
DK_PRT("find 0 0 0 1: type: %02x ", pu8Buf[i+4]);
bFindStart = HI_TRUE;
i += 4;
break;
}
}
for( ; i < size-4; i++) {
bNewPic = (pu8Buf[i+0] == 0 && pu8Buf[i+1] == 0 && pu8Buf[i+2] == 0 && pu8Buf[i+3] == 1);
if (bNewPic) {
DK_PRT("find 0 0 0 1: type: %02x ", pu8Buf[i+4]);
bFindEnd = HI_TRUE;
break;
}
}
if(bFindStart && bFindEnd){
*nalu_len = i;
return (pu8Buf+i);
}else{
DK_PRT("find return null ");
return NULL;
}
}
对其返回值和长度分别对pack进行赋值:
```c
static int deal_frame_stream(venc_stream *pStream,char *ptr,int size,es_VENC_NALU_TYPE_E nalu_type){
int pack_cnt = 0;
int i =0;
int offset = 0;
int nalu_len = 0;
char *addr = NULL;
if(nalu_type ==es_VENC_NALU_IDRSLICE){
addr = find_nalu_tag(ptr + offset,size-offset,&nalu_len);
while(addr){
pStream->pack[pack_cnt].addr = addr;
pStream->pack[pack_cnt].offset = offset;
pStream->pack[pack_cnt].len = nalu_len;
pack_cnt++;
if(pack_cnt >4){
DK_PRT("Line:%d find nalu_tag error", __LINE__);
return -1;
}
offset += nalu_len;
addr = find_nalu_tag(ptr+offset,size-offset,&nalu_len);
}
}else if(nalu_type ==es_VENC_NALU_PSLICE){
pStream->pack[pack_cnt].addr = ptr;
pStream->pack[pack_cnt].offset = offset;
pStream->pack[pack_cnt].len = size;
pack_cnt++;
}
pStream->pack_cnt = pack_cnt;
return 0;
}
四、代码实现
考虑到直接修改ffmpeg源代码工作量比较大,先把封装mp4的功能提取出来,单独实现动态改变moov标签。
感谢ffmpeg开源项目,才使得封装mp4的工作量变小了不少。
把封装mp4的文件做成 动态链接库。
在rkmedia目录下新建目录:third_lib/es2mp4,并把源代码编译成so库文件:libes2mp4.so
在link.txt 中 添加 -L../third_lib/es2mp4/ -les2mp4
在rkmedia/include 新建目录es2mp4
cp rkmedia/third_lib/es2mp4/es2mp4.h ./es2mp4/
在开发板系统中:将编译的libes2mp4.so 拷贝到 /oem/usr/lib下
以下是对封装Box的总体调用过程:
最后封装的mp4文件能正常被系统解析,并播放。
可以看到1000帧的视频,时间总长在33秒,也说明封装信息是正确的。
这里只实现了对视频帧的封装,后续再考虑把音频也封装进mp4,需要考虑到音视频同步的问题。
- 分享
- 举报
-
毛巾卷 2024-02-28 14:59:31回复 举报大佬,详细的过程代码方便发一下学习一下不
-
Daiker 回复 毛巾卷 2024-02-29 17:40:14回复 举报该方法有其局限性,只能针对特殊场景使用。录像时长受mdat的空间限制。思路其实很简单,mdat和moov互换位置即可,只是动态更新mdat比较麻烦
-
-
毛巾卷 2024-02-27 15:08:59回复 举报学习一下,这个问题我也困扰过
-
2024-01-22 16:35:19
-
浏览量:1186次2023-06-12 14:34:40
-
浏览量:670次2024-01-13 15:17:32
-
浏览量:1612次2023-12-29 17:51:55
-
浏览量:1422次2024-02-27 17:03:43
-
浏览量:2707次2020-08-04 20:12:26
-
浏览量:5243次2021-04-27 16:32:49
-
浏览量:6360次2019-12-07 15:40:17
-
浏览量:3140次2022-04-12 09:57:34
-
浏览量:5460次2021-04-27 16:31:59
-
浏览量:986次2024-01-17 11:25:11
-
浏览量:4051次2020-08-26 17:39:57
-
浏览量:3103次2019-06-27 08:53:15
-
浏览量:1524次2023-06-12 14:35:30
-
浏览量:7895次2024-01-04 14:35:55
-
浏览量:880次2024-01-06 16:49:28
-
浏览量:1216次2024-01-09 17:35:19
-
浏览量:5472次2021-04-27 16:33:54
-
浏览量:7600次2023-12-27 20:28:48
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
Daiker
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明