MOV格式。
資陽網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,資陽網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為資陽上1000家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢,請找那個售后服務(wù)好的資陽做網(wǎng)站的公司定做!
MOV是由Apple公司開發(fā)的音頻、視頻文件格式,同時也是QuickTime影片格式。常用于存儲常用數(shù)字媒體類型,如音頻和視頻。MOV格式文件是以軌道的形式組織起來的,一個MOV格式文件結(jié)構(gòu)中可以包含很多軌道。在某些方面它比WMV和RM更優(yōu)秀,并能被眾多的多媒體編輯及視頻處理軟件所支持。
QuickTime視頻文件播放程序,除了播放MP3外,QuickTime還支持MIDI播放。并且可以收聽/收網(wǎng)絡(luò)播放,支持HTTP、RTP和RTSP標(biāo)準(zhǔn)。該軟件還支持主要的圖像格式,比如:JPEG、BMP、PICT、PNG和GIF。該軟件的其他特性還有:支持?jǐn)?shù)字視頻文件,包括:MiniDV、DVCPro、DVCam、AVI、AVR、MPEG-1、OpenDML以及MacromediaFlash等。QuickTime用于保存音頻和視頻信息,現(xiàn)在它被包括AppleMacOS,MicrosoftWindows95/98/NT/2003/XP/VISTA在內(nèi)的所有主流電腦平臺支持。
QuickTime文件格式支持25位彩色,支持領(lǐng)先的集成壓縮技術(shù),提供150多種視頻效果,并配有提供了200多種MIDI兼容音響和設(shè)備的聲音裝置。它無論是在本地播放還是作為視頻流格式在網(wǎng)上傳播,都是一種優(yōu)良的視頻編碼格式。
基本流程
1.初始化輸入設(shè)備
2.初始化輸出設(shè)備
3.創(chuàng)建AVCaptureSession,用來管理視頻與數(shù)據(jù)的捕獲
4.創(chuàng)建預(yù)覽視圖
前面介紹了如何通過相機(jī)實(shí)時獲取音視頻數(shù)據(jù),我們接下來就需要了解獲取到的數(shù)據(jù)到底是什么樣的,使用系統(tǒng)提供的接口獲取到的音視頻數(shù)據(jù)都保存在CMSampleBufferRef中,這個結(jié)構(gòu)在iOS中表示一幀音頻/視頻數(shù)據(jù),它里面包含了這一幀數(shù)據(jù)的內(nèi)容和格式,我們可以把它的內(nèi)容取出來,提取出/轉(zhuǎn)換成我們想要的數(shù)據(jù)。
代表視頻的CMSampleBufferRef中保存的數(shù)據(jù)是yuv420格式的視頻幀(因?yàn)槲覀冊谝曨l輸出設(shè)置中將輸出格式設(shè)為:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)。
在下面的回調(diào)中,可以拿到最終的CMSampleBufferRef數(shù)據(jù)
視頻是由一幀一幀的數(shù)據(jù)連接而成,而一幀視頻數(shù)據(jù)其實(shí)就是一張圖片。
yuv是一種圖片儲存格式,跟RGB格式類似。
RGB格式的圖片很好理解,計(jì)算機(jī)中的大多數(shù)圖片,都是以RGB格式存儲的。
yuv中,y表示亮度,單獨(dú)只有y數(shù)據(jù)就可以形成一張圖片,只不過這張圖片是灰色的。u和v表示色差(u和v也被稱為:Cb-藍(lán)色差,Cr-紅色差),
為什么要yuv?
有一定歷史原因,最早的電視信號,為了兼容黑白電視,采用的就是yuv格式。
一張yuv的圖像,去掉uv,只保留y,這張圖片就是黑白的。
而且yuv可以通過拋棄色差來進(jìn)行帶寬優(yōu)化。
比如yuv420格式圖像相比RGB來說,要節(jié)省一半的字節(jié)大小,拋棄相鄰的色差對于人眼來說,差別不大。
一張yuv格式的圖像,占用字節(jié)數(shù)為 (width * height + (width * height) / 4 + (width * height) / 4) = (width * height) * 3 / 2
一張RGB格式的圖像,占用字節(jié)數(shù)為(width * height) * 3
在傳輸上,yuv格式的視頻也更靈活(yuv3種數(shù)據(jù)可分別傳輸)。
很多視頻編碼器最初是不支持rgb格式的。但是所有的視頻編碼器都支持yuv格式。
我們這里使用的就是yuv420格式的視頻。
yuv420也包含不同的數(shù)據(jù)排列格式:I420,NV12,NV21.
其格式分別如下,
I420格式:y,u,v 3個部分分別存儲:Y0,Y1...Yn,U0,U1...Un/2,V0,V1...Vn/2
NV12格式:y和uv 2個部分分別存儲:Y0,Y1...Yn,U0,V0,U1,V1...Un/2,Vn/2
NV21格式:同NV12,只是U和V的順序相反。
綜合來說,除了存儲順序不同之外,上述格式對于顯示來說沒有任何區(qū)別。
使用哪種視頻的格式,取決于初始化相機(jī)時設(shè)置的視頻輸出格式。
設(shè)置為kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange時,表示輸出的視頻格式為NV12;
設(shè)置為kCVPixelFormatType_420YpCbCr8Planar時,表示使用I420。
設(shè)置為kCVPixelFormatType_32RGBA時,表示使用BGRA。
GPUImage設(shè)置相機(jī)輸出數(shù)據(jù)時,使用的就是NV12.
為了一致,我們這里也選擇NV12格式輸出視頻。
libyuv是Google開源的實(shí)現(xiàn)各種YUV與RGB之間相互轉(zhuǎn)換、旋轉(zhuǎn)、縮放的庫。它是跨平臺的,可在Windows、Linux、Mac、Android等操作系統(tǒng),x86、x64、arm架構(gòu)上進(jìn)行編譯運(yùn)行,支持SSE、AVX、NEON等SIMD指令加速.
導(dǎo)入libyuv庫,并設(shè)置頭文件搜索路徑,不然會報錯
文章只介紹了使用AVFoundation進(jìn)行視頻采集和使用libyuv進(jìn)行格式轉(zhuǎn)換,音視頻相關(guān)的知識還有很多,這里不再做詳細(xì)介紹了。
視頻采集會得到格式為CMSampleBufferRef的視頻包,視頻推流一般把視頻流轉(zhuǎn)換成flv格式
(1)首先將視頻流轉(zhuǎn)換成YUV的數(shù)據(jù)格式
//獲取yuv數(shù)據(jù)
- (NSData*)convertVideoSmapleBufferToYuvData:(CMSampleBufferRef)videoSample {
//通過CMSampleBufferGetImageBuffer方法,獲得CVImageBufferRef,里面就包含了YUV420數(shù)據(jù)的指針
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(videoSample);
//鎖住格式轉(zhuǎn)換線程,開始轉(zhuǎn)換格式
CVPixelBufferLockBaseAddress(pixelBuffer,0);
//獲取圖像寬度(像素)
size_t pixelWidth = CVPixelBufferGetWidth(pixelBuffer);
//獲取圖像高度(像素)
size_t pixelHeight = CVPixelBufferGetHeight(pixelBuffer);
//計(jì)算YUV中的Y所占字節(jié)數(shù)
size_t y_size = pixelWidth * pixelHeight;
//計(jì)算YUV中的U和V分別所占的字節(jié)數(shù)
size_t uv_size = y_size /4;
uint8_t * yuv_frame = aw_alloc(uv_size *2+ y_size);
//獲取pixelBuffer中的Y數(shù)據(jù)
uint8_t * y_frame = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0);
memcpy(yuv_frame, y_frame, y_size);
//獲取pixelBuffer中的UV數(shù)據(jù)
uint8_t * uv_frame = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1);
memcpy(yuv_frame + y_size, uv_frame, uv_size *2);
//獲取到想要的數(shù)據(jù),解鎖格式轉(zhuǎn)換線程
CVPixelBufferUnlockBaseAddress(pixelBuffer,0);
//yuv格式----nv12格式
NSData * yuvData = [NSData dataWithBytesNoCopy:yuv_frame length:y_size + uv_size *2];
//由于相機(jī)偏轉(zhuǎn),我們需要對的到的視頻頁面進(jìn)行旋轉(zhuǎn)
return [self rotateNV12Data:nv12Data];
}
(2)由于相機(jī)偏轉(zhuǎn),我們需要對的到的視頻頁面進(jìn)行旋轉(zhuǎn)
- (NSData*)rotateNV12Data:(NSData*)nv12Data {
int degree = 0;
switch(self.videoConfig.orientation) {
case UIInterfaceOrientationLandscapeLeft:
degree = 90;
break;
case UIInterfaceOrientationLandscapeRight:
degree = 270;
break;
default:
//do nothing
break;
}
if(degree !=0) {
uint8_ t * src_nv12_bytes = (uint8_t*)nv12Data.bytes;
uint32_t width = (uint32_t)self.videoConfig.width;
uint32_t height = (uint32_t)self.videoConfig.height;
uint32_t w_x_h = (uint32_t)(self.videoConfig.width*self.videoConfig.height);
uint8_t * rotatedI420Bytes =aw_alloc(nv12Data.length);
NV12ToI420Rotate(src_nv12_bytes, width,
src_nv12_bytes + w_x_h, width,
rotatedI420Bytes, height,
rotatedI420Bytes + w_x_h, height /2,
rotatedI420Bytes + w_x_h + w_x_h /4, height /2,
width, height, (RotationModeEnum)degree);
I420ToNV12(rotatedI420Bytes, height,
rotatedI420Bytes + w_x_h, height /2,
rotatedI420Bytes + w_x_h + w_x_h /4, height /2,
src_nv12_bytes, height, src_nv12_bytes + w_x_h, height,
height, width);
aw_free(rotatedI420Bytes);
}
return nv12Data;
}
(3)將nv12格式的數(shù)據(jù)合成為flv格式
- (aw_flv_video_tag*)encodeYUVDataToFlvTag:(NSData*)yuvData{
if(!_vEnSession) {
return NULL;
}
OSStatus status = noErr;
//獲取視頻寬度
size_t pixelWidth = self.videoConfig.pushStreamWidth;
//獲取視頻高度
size_t pixelHeight = self.videoConfig.pushStreamHeight;
//NV12數(shù)據(jù)-CVPixelBufferRef中
//硬編碼主要調(diào)用VTCompressionSessionEncodeFrame函數(shù),此函數(shù)處理的是CVPixelBufferRef類型。
CVPixelBufferRef pixelBuf =NULL;
//初始化pixelBuf
CVPixelBufferCreate(NULL, pixelWidth, pixelHeight,kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,NULL, pixelBuf);
if(CVPixelBufferLockBaseAddress(pixelBuf,0) != kCVReturnSuccess){
NSLog(@"encode video lock base address failed");?
return NULL;
}
//將YUV數(shù)據(jù)填充到CVPixelBufferRef中
size_t y_size = aw_stride(pixelWidth) * pixelHeight;
size_t uv_size = y_size /4;
uint8_t * yuv_frame = (uint8_t*)yuvData.bytes;
//獲取y frame
uint8_t * y_frame = CVPixelBufferGetBaseAddressOfPlane(pixelBuf,0);
memcpy(y_frame, yuv_frame, y_size);
//獲取uv frame
uint8_t * uv_frame = CVPixelBufferGetBaseAddressOfPlane(pixelBuf,1);
memcpy(uv_frame, yuv_frame + y_size, uv_size *2);
//編碼CMSampleBufRef
//獲取時間戳,由于視頻幀是有順序的,所以timestamp可以自定義一個累加字段
uint32_t ptsMs = timestamp+1;//self.vFrameCount++ * 1000.f / self.videoConfig.fps;
CMTime pts =CMTimeMake(ptsMs,1000);
//將NV12數(shù)據(jù)的PixelBuf送到硬編碼器中,進(jìn)行編碼。
status = VTCompressionSessionEncodeFrame(_vEnSession, pixelBuf, pts,kCMTimeInvalid,NULL, pixelBuf,NULL);
if(status == noErr) {
dispatch_semaphore_wait(self.vSemaphore,DISPATCH_TIME_FOREVER);
if(_naluData) {
//硬編碼成功,_naluData內(nèi)的數(shù)據(jù)即為h264視頻幀。
//因?yàn)槭峭屏?,所以需要獲取幀長度,轉(zhuǎn)成大端字節(jié)序,放到數(shù)據(jù)的最前面
uint32_t naluLen = (uint32_t)_naluData.length;
//小端轉(zhuǎn)大端。計(jì)算機(jī)內(nèi)一般都是小端,而網(wǎng)絡(luò)和文件中一般都是大端。大端轉(zhuǎn)小端和小端轉(zhuǎn)大端算法一樣,就是字節(jié)序反轉(zhuǎn)就行了。
uint8_t naluLenArr[4] = {naluLen 240xff, naluLen 160xff, naluLen 80xff, naluLen 0xff};
//數(shù)據(jù)拼接
NSMutableData * mutableData = [NSMutableData dataWithBytes:naluLenArr length:4];
[mutableData appendData:_naluData];
//h264 - flv tag,合成flvtag之后就可以直接發(fā)送到服務(wù)端了。
aw_flv_video_tag * video_tag = aw_encoder_create_video_tag((int8_t*)mutableData.bytes, mutableData.length, ptsMs,0,self.isKeyFrame);
//編碼完成,釋放數(shù)據(jù)。
_naluData = nil;
_isKeyFrame = NO;
CVPixelBufferUnlockBaseAddress(pixelBuf,0);
CFRelease(pixelBuf);
return video_tag;
}
}else {
NSLog(@"encode video frame error");
}
CVPixelBufferUnlockBaseAddress(pixelBuf,0);
CFRelease(pixelBuf);
return NULL;
}
快進(jìn):AVPlayer .rate 1
慢放: 0 AVPlayer .rate 1
快退: AVPlayer .rate 0 (m3u8 不能快退)
MP4:如果快進(jìn)到了緩存不夠的地方:KVC監(jiān)聽 AVPlayerItem .isPlaybackBufferEmpty = yes
快退到頭會走通知AVPlayerItemDidPlayToEndTimeNotification
m3u8(靜態(tài)):快進(jìn)與MP4格式視頻一致,可以10倍 20倍播放。
m3u8(動態(tài)):快進(jìn)到了緩存不夠的地方,會走通知AVPlayerItemDidPlayToEndTimeNotification。
備注:使用[AVPlayerItem stepByCount:-24];
將AVPlayerItem 向前或向后移動指定的步數(shù),正數(shù)前進(jìn),負(fù)數(shù)后退。 每個步數(shù)的大小取決于AVPlayerItem啟用的AVPlayerItemTracks對象;
注冊監(jiān)聽和通知
比較蘋果的ProRes格式是在十多年前開發(fā)的,現(xiàn)在支持高達(dá)8K的分辨率,對于需要視頻編輯的人來說非常實(shí)用。 Final Cut Pro經(jīng)過編碼,可高效處理和編輯ProRes內(nèi)容,并廣泛用于專業(yè)視頻行業(yè)中。
iPhone 13 Pro 機(jī)型還將首次能夠以 ProRes 視頻格式進(jìn)行錄制,為專業(yè)人士提供更高質(zhì)量的輸出。
蘋果手機(jī)優(yōu)點(diǎn)如下:
1、iOS系統(tǒng)
我們知道蘋果手機(jī)的iOS系統(tǒng)是獨(dú)有的,只有在蘋果手機(jī)中才能夠體驗(yàn)到這種系統(tǒng),而這個手機(jī)系統(tǒng)是非常流暢的,安卓手機(jī)的系統(tǒng)在使用一段時間之后都會有不同程度的卡頓,但是蘋果手機(jī)的iOS系統(tǒng)依舊非常流暢。
2、應(yīng)用生態(tài)優(yōu)異
蘋果手機(jī)對于開發(fā)者上傳在應(yīng)用商店的軟件有著很嚴(yán)格的審核,像是一些不安全的軟件或者是沒有質(zhì)量的軟件是不會在應(yīng)用商店里存在的,對于軟件有一個質(zhì)量上的保障,但是很多的安卓手機(jī)并不是這樣。
3、iOS系統(tǒng)安全性好
在使用安卓手機(jī)的時候,很多軟件在打開的時候都會開放很多的運(yùn)用權(quán)限,因?yàn)椴婚_的話完全沒有辦法正常使用,這一點(diǎn)是很容易造成用戶信息的泄露,而且在很多的游戲上還有一不小心點(diǎn)到就被扣費(fèi)。