日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/10 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mp4v2 写mp4 java_使用mp4v2将H264+AAC合成mp4文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

錄制程序要添加新功能:錄制CMMB電視節目,我們的板卡發送出來的是RTP流(H264視頻和AAC音頻),錄制程序要做的工作是:

(1)接收并解析RTP包,分離出H264和AAC數據流;

(2)將H264視頻和AAC音頻以某種格式封裝,最后存成文件,供用戶查看。

第一步已經有部分代碼可供參考,因此很快就完成了。

第二步,我們決定封裝成mp4,查找了一些資料后,決定使用開源庫mp4v2來合成mp4文件。

技術路線已確定,就開工干活。

(一)mp4格式的基礎知識。

關于mp4格式,網上介紹的不少,有以下內容可供參考:

(1)兩個ISO標準:

[ISO/IEC 14496-12]:ISO base media file format --”is a general format forming the basis for a number of other more specific file formats. This format contains the timing, structure, and media information for timed sequences of media data, such as audio-visual presentations ”

[ISO/IEC 14496-14]:MP4 file format --”This specification defines MP4 as an instance of the ISO Media File format [ISO/IEC 14496-12 and ISO/IEC

15444-12]. ”

定義了mp4文件格式標準。

是上面兩個標準的解釋,建議先看這個,了解大概,具體細節再看ISO標準文件。

(二)技術驗證。主要就是寫驗證代碼,驗證技術可行性。

去官網下載mp4v2源碼、編譯、安裝過程略過不提。所有資料可以在http://code.google.com/p/mp4v2/找到。

先寫部分驗證代碼,很快完成了,但封裝出來的文件有問題,無法播放。

合成部分,代碼如下:

static?void*?writeThread(void*?arg)

{

rtp_s*?p_rtp?=?(rtp_s*)?arg;

if?(p_rtp?==?NULL)

{

printf("ERROR!\n");

return;

}

MP4FileHandle?file?=?MP4CreateEx("test.mp4",?MP4_DETAILS_ALL,?0,?1,?1,?0,?0,?0,?0);

if?(file?==?MP4_INVALID_FILE_HANDLE)

{

printf("open?file?fialed.\n");

return;

}

MP4SetTimeScale(file,?90000);

//添加h264?track

MP4TrackId?video?=?MP4AddH264VideoTrack(file,?90000,?90000?/?25,?320,?240,

0x64,?//sps[1]?AVCProfileIndication

0x00,?//sps[2]?profile_compat

0x1f,?//sps[3]?AVCLevelIndication

3);?//?4?bytes?length?before?each?NAL?unit

if?(video?==?MP4_INVALID_TRACK_ID)

{

printf("add?video?track?failed.\n");

return;

}

MP4SetVideoProfileLevel(file,?0x7F);

//添加aac音頻

MP4TrackId?audio?=?MP4AddAudioTrack(file,?48000,?1024,?MP4_MPEG4_AUDIO_TYPE);

if?(video?==?MP4_INVALID_TRACK_ID)

{

printf("add?audio?track?failed.\n");

return;

}

MP4SetAudioProfileLevel(file,?0x2);

int?ncount?=?0;

while?(1)

{

frame_t*?pf?=?NULL;?//frame

pthread_mutex_lock(&p_rtp->mutex);

pf?=?p_rtp->p_frame_header;

if?(pf?!=?NULL)

{

if?(pf->i_type?==?1)//video

{

MP4WriteSample(file,?video,?pf->p_frame,?pf->i_frame_size,?MP4_INVALID_DURATION,?0,?1);

}

else?if?(pf->i_type?==?2)//audio

{

MP4WriteSample(file,?audio,?pf->p_frame,?pf->i_frame_size?,?MP4_INVALID_DURATION,?0,?1);

}

ncount++;

//clear?frame.

p_rtp->i_buf_num--;

p_rtp->p_frame_header?=?pf->p_next;

if?(p_rtp->i_buf_num?<=?0)

{

p_rtp->p_frame_buf?=?p_rtp->p_frame_header;

}

free_frame(&pf);

pf?=?NULL;

if?(ncount?>=?1000)

{

break;

}

}

else

{

//printf("BUFF?EMPTY,?p_rtp->i_buf_num:%d\n",?p_rtp->i_buf_num);

}

pthread_mutex_unlock(&p_rtp->mutex);

usleep(10000);

}

MP4Close(file);

}

現象:沒有圖像,也沒有聲音,根本無法播放。

于是,艱苦的工作開始了:跟蹤查找原因。

(1)使用 vlc播放合成的mp4文件,查看詳細輸出:

vlc?-vvv?test.mp4

[0x8e9357c]?mp4?stream?debug:?found?Box:?ftyp?size?24

[0x8e9357c]?mp4?stream?debug:?found?Box:?free?size?136

[0x8e9357c]?mp4?stream?debug:?skip?box:?"free"

[0x8e9357c]?mp4?stream?debug:?found?Box:?mdat?size?985725

[0x8e9357c]?mp4?stream?debug:?skip?box:?"mdat"

[0x8e9357c]?mp4?stream?debug:?found?Box:?moov?size?5187

[0x8e9357c]?mp4?stream?debug:?found?Box:?mvhd?size?108

[0x8e9357c]?mp4?stream?debug:?read?box:?"mvhd"?creation

734515d-06h:22m:03s?modification?734515d-06h:22m:23s

time?scale?90000?duration?694977d-48h:00m:29s

rate?1.000000?volume?1.000000?next?track?id?3

可以看到vlc(實際上是調用libmp4庫)解析box都正確的,mdat的大小也是正確的。

但接下來一行:

skip box: "mdat"

就比較奇怪了,明明解析正確了,為什么要將mdat忽略掉呢?要知道,mdat里存放的可是真正的音視頻數據阿?如果skip掉了,后面解碼時沒有數據,當然播放不了了?

(2)既然找到疑點,繼續跟蹤。

查看vlc的源代碼,在文件modules/demux/mp4/libmp4.c中發現:skip信息是由MP4_ReadBoxSkip()函數打印的,而調用的地方在libmp4.c中2641行:

/*?Nothing?to?do?with?this?box?*/

{?FOURCC_mdat,??MP4_ReadBoxSkip,????????MP4_FreeBox_Common?},

{?FOURCC_skip,??MP4_ReadBoxSkip,????????MP4_FreeBox_Common?},

{?FOURCC_free,??MP4_ReadBoxSkip,????????MP4_FreeBox_Common?},

{?FOURCC_wide,??MP4_ReadBoxSkip,????????MP4_FreeBox_Common?},

而在libmp4.h中:

#define?FOURCC_mdat?VLC_FOURCC(?'m',?'d',?'a',?'t'?)

#define?FOURCC_skip?VLC_FOURCC(?'s',?'k',?'i',?'p'?)

#define?FOURCC_free?VLC_FOURCC(?'f',?'r',?'e',?'e'?)

#define?FOURCC_wide?VLC_FOURCC(?'w',?'i',?'d',?'e'?)

從代碼看,vlc調用libmp4解析文件時,主動忽略了mdat,skip,free,wide這四種類型的box。

為什么呢?

(3)繼續查看modules/demux/mp4/mp4.c中的Open()函數(解析模塊的入口函數),可以看到本模塊的主要工作是初始化一個demux_sys_t結構體,該結構體定義如下:

struct?demux_sys_t

{

MP4_Box_t????*p_root; /*?container?for?the?whole?file?*/

mtime_t??????i_pcr;

uint64_t?????i_time; /*time?position?of?the?presentation?*?in?movie?timescale*/

uint64_t?????i_timescale;????/*?movie?time?scale?*/

uint64_t?????i_duration;?????/*?movie?duration?*/

unsigned?int?i_tracks;???????/*?number?of?tracks?*/

mp4_track_t??*track;/*?array?of?track?*/

float????????f_fps; /*?number?of?frame?per?seconds?*/

/*?*/

MP4_Box_t????*p_tref_chap;

/*?*/

input_title_t?*p_title;

};

似乎只是為了獲取mp4的tracks,moov,duration, timescale等基本信息,實際上并不解碼數據,因此就不需要關注mdat這個box了。

綜上:vlc的輸出是正常的,libmp4忽略了mdat這個box也不是造成mp4文件無法播放的原因,只是因為libmp4這個模塊并不真正解碼數據,所以不需要關注這個box。

既然問題不在這,那在哪里呢?

(4)繼續看vlc的輸出:

AVC: nal size -1710483062

no frame!

[0x8e93eb4] avcodec decoder warning: cannot decode one frame (3595 bytes)

可以看到,vlc實際上是調用avcodec(ffmpeg)來解碼數據的,我們的視頻是AVC(H264)格式的。

從錯誤信息可以確定,是H264的NAL大小錯誤,似乎跟mp4文件本身關系不大。

不管那么多,先看看代碼再說。

vlc是以lib的形式使用ffmpeg的,所以我們必須看ffmpeg的代碼:

libavcodec/h264.c:

static?int?decode_nal_units(H264Context?*h,?const?uint8_t?*buf,?int?buf_size){

….

for(;;){

if(buf_index?>=?next_avc)?{

if(buf_index?>=?buf_size)?break;

nalsize?=?0;

for(i?=?0;?i?nal_length_size;?i++)

nalsize?=?(nalsize?<

if(nalsize?<=?0?||?nalsize?>?buf_size?-?buf_index){

av_log(h->s.avctx,?AV_LOG_ERROR,?"AVC:?nal?size?%d\n",?nalsize);

break;

}

next_avc=?buf_index?+?nalsize;

}

}

可以看到,正是這里報錯的。

但是,為什么報錯呢?根據ffmpeg的信息,知道取出來的 nalsize為負數。

懷疑是h264流本身有問題,于是用Elecard查看了生成的mp4文件,視頻播放又非常正常。似乎h264流是正常的?

愁呀愁。。。。

內容如下:

Ottavio Campana

“question about MP4AddH264VideoTrack。

What's the meaning of the profile_compat and

sampleLenFieldSizeMinusOne fields?”

Jeremy Noring

"Usually an NALU is prefixed by the start code 0x00000001. To write it

as a sample in MP4 file format, just replace the start code with size

of the NALU(without 4-byte start code) in big endian. You also need to

specify how many bytes of the size value requires. Take libmp4v2 for

example, the last parameter in MP4AddH264VideoTrack(.., uint8_t

sampleLenFieldSizeMinusOne) indicate the number of byes minus one."

...so each sample you and to mp4v2 should be prefixed with a size code

(in big-endian, of course). I use a 4 byte size code, so

sampleLenFieldSizeMinusOne gets set to 3. This seems to work; my

files playback on just about everything. Perhaps one of the project

maintainers can clarify this, and it'd also be good to update the

documentation of that call to make this clear.”

Ottavio Campana

that's the code I used as reference to write my program :-(

but my doubt is that there must be something wrong somewhere, because

boxes seem to be correctly written, but when I try to decode them I

get errors like

[h264 @ 0xb40fa0]AVC: nal size -502662121

have you ever seen an error like this?

Ottavio Campana

> Not sure, but it looks you're not converting it to big-endian before

> prefixing it to your sample.

well, eventually using ffmpeg to dump the read frames, I discovered

that I had to strip che NALU start code, i.e. the 0x00000001, and to

put the NALU size at its place.

It works perfectly now, but I still wonder why I had to put the size

at the begin of the data, since it is a parameter which is passed to

MP4WriteSample, so I expected the function to add it.

從中得到如下關鍵信息:

(1)h264流中的NAL,頭四個字節是0x00000001;

(2)mp4中的h264track,頭四個字節要求是NAL的長度,并且是大端順序;

(3)mp4v2很可能針對此種情況并沒有做處理,所以寫到mp4文件中的每個NAL頭四個字節還是0x00000001.

那好說,我將每個sample(也就是NAL)的頭四個字節內容改成NAL的長度,且試試看:

if(pf->i_frame_size?>=?4)

{

uint32_t*?p?=?(&pf->p_frame[0]);

*p?=?htonl(pf->i_frame_size?-4);//大端,去掉頭部四個字節

}

MP4WriteSample(file,?video,?pf->p_frame,?pf->i_frame_size,?????MP4_INVALID_DURATION,?0,?1);

測試下來,果然OK了!

(6)視頻已經解決了,音頻還有問題:播放的聲音太快。

嘗試調整參數:

MP4TrackId audio = MP4AddAudioTrack(file, 48000, 1024, MP4_MPEG4_AUDIO_TYPE);

第三個參數sampleDuration,表示每個sample持續多少個duration,網上看到的都是1024。

我嘗試了幾個不同的值:128,256,512,4096都不行,最后發現設為2048就正常了。

(為什么是2048??????我不清楚,也許是因為我們的音頻是雙聲道?有時間再研究。。。)

正確代碼如下:

MP4TrackId?audio?=?MP4AddAudioTrack(file,?48000,?2048,?MP4_MPEG4_AUDIO_TYPE);

至此,已經成功的將rtp流合成了mp4文件,證明了技術上是可行的。

注意:很多參數都是針對我們的具體應用寫死的,僅供參考。

(三)將功能合并到錄制程序中。

略。

from:http://www.rosoo.net/a/201305/16631.html

總結

以上是生活随笔為你收集整理的mp4v2 写mp4 java_使用mp4v2将H264+AAC合成mp4文件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲熟女乱色一区二区三区久久久 | 欧美日韩三级在线 | 天堂网中文在线观看 | 加勒比伊人 | 中文在线字幕免费观 | 天堂а√在线中文在线新版 | 视频在线观看一区二区 | 领导揉我胸亲奶揉下面 | 不卡的av在线免费观看 | 激情六月色 | 天天干夜夜干 | 婷婷激情五月网 | 四虎国产精品永久免费观看视频 | 精品一区二区人妻 | 黄色a免费| 怡红院成人在线 | 成人中文字幕在线观看 | 绯色av一区| 免费看的黄色 | 欧美xxxxx自由摘花 | 日本欧美中文字幕 | 欧美大片一区二区 | 成人欧美一区二区三区 | 无码任你躁久久久久久老妇 | 粗大黑人巨茎大战欧美成人免费看 | 国产伦精品免费视频 | 国产超碰91 | 麻豆网站在线 | bangbros性欧美18 | 国产视频一区二区三区在线播放 | 九九热这里只有精品6 | 亚洲 国产 日韩 欧美 | 亚洲热影院| 日韩极品视频在线观看 | 中文字幕av第一页 | 黄色一级生活片 | 中文字幕第十二页 | 日日夜夜爱 | 女久久| 亚洲福利社 | 亚洲国产婷婷 | 精品视频在线一区 | 天堂网av中文字幕 | 制服诱惑一区二区三区 | 亚洲国产一二 | 久久久久久久久久久久久久久久久久久久 | 激情综合网站 | 香蕉视频成人在线观看 | 国产精品国产一区二区三区四区 | 羞羞的视频在线观看 | 久久aⅴ国产欧美74aaa | 玖玖爱资源站 | 一道本在线播放 | 日韩h在线| 午夜视频a | 午夜香蕉网 | www.69av.com| 国产精品精品视频 | 精品香蕉99久久久久网站 | 欧美成人一区二免费视频软件 | 国产在线观看成人 | 成人啪啪18免费游戏链接 | 高级毛片 | 中文字幕日产av | 成人免费一区二区 | 男生草女生的视频 | 免费a网站| 美女久久久久久久久 | 永久免费汤不热视频 | 欧美浓毛大泬视频 | 中文字幕永久在线观看 | 欧洲精品码一区二区三区免费看 | 麻豆专区| 日韩一二三区视频 | 91视频最新地址 | 免费久草视频 | 中文资源在线播放 | 中国少妇乱子伦视频播放 | 国产亚洲欧美在线视频 | 另类一区二区 | 国产又粗又猛又爽免费视频 | 老湿机69福利区午夜x片 | xxxwww黄色| 中文字幕.com | 久久久久久综合网 | 伊人久久一区 | 伊人97| 青青草婷婷 | 国产在线视频一区 | 葵司有码中文字幕二三区 | 性天堂网| 人人cao| 一本色道久久88综合无码 | 国产成人欧美一区二区三区91 | 无码国产伦一区二区三区视频 | 男女黄网站 | 懂色一区二区 | 少妇人妻偷人精品视频蜜桃 | www.色悠悠 |