最近公司快不行了,又有空閑的時間了??,對之前做的一些功能做一些總結(jié),微信小視頻,曾經(jīng)熱火朝天的功能,許多社交類app都想著模仿。下面就來解析下小視頻功能特點。
1.小視頻沒有聲音
2.小視頻在cell中無限循環(huán)播放
3.性能要求,滑動需要順暢(線程設計)。
最早接觸iOS,我只知道播放視頻通常有兩種方式,一種是 AVPlayer,另一種是MPMoviePlayerController 。現(xiàn)在 ios9之后蘋果已推薦播放視頻使用 AVPlayer。
下面介紹下簡單的說下 AVPlayer 視頻播放器使用時的相關知識,具體的參見官方文檔
//創(chuàng)建一個AVPlayer播放類
@property(nonatomic,strong) AVPlayer *player;
//AVPlayerItem(多媒體資源)
@property(nonatomic,strong) AVPlayerItem *playerItem;
//AVAsset(音視頻當中單個資源,例如聲道,影片)
@property (nonatomic, strong) AVAsset
AVPlayer功能很強大,音視頻都可以播放,但像小視頻功能我們只需要他的視頻功能, 音頻功能不需要,如果小視頻功能用AVPlayer來實現(xiàn),系統(tǒng)開銷可能就有點浪費了,因為AVPlayer實現(xiàn)了Video+Audio ,而且AVPlayer的音頻對AudioSession也有影響,如果app其他地方有使用到聲音的,需要注意它的使用了。
后來追求性能,發(fā)現(xiàn)了AVAssetReader類
AVAssetReader用于從AVAsset資源中讀取媒體樣本,可以讀取出視頻和音頻
//從媒體中得到聲音軌道 AVMediaTypeVideo 視頻
AVAssetTrack *track = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
AVAssetReaderOutput輸出資源并通過copyNextSampleBuffer方法將我們需要的數(shù)據(jù)給弄一份出來。注意讀取的過程是分幀(音頻是分塊)讀取,并不是一次性將將所有數(shù)據(jù)讀取去完畢。結(jié)束時,AVAssetReader的status屬性 會變成AVAssetReaderStatusCompleted,通過此屬性來判斷是否讀取完畢。
//從媒體中得到視頻軌道 AVMediaTypeAudio 音頻
AVAssetTrack *track = [[self.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
//讀取配置
NSDictionary *setting = @{
(id)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey:@(self.size.width),
(id)kCVPixelBufferHeightKey:@(self.size.height),
};
//讀取輸出,在相應的軌道和輸出對應格式的數(shù)據(jù)
self.assetReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:track outputSettings:setting];
//賦給讀取并開啟讀取
if ([self.assetReader canAddOutput:self.assetReaderOutput]) {
[self.assetReader addOutput:self.assetReaderOutput];
}
[self.assetReader startReading];//開始讀取
小視頻功能可以通過AVPlayer和AVAssetReader 兩種途徑實現(xiàn),
我寫了個簡單的demo,但里面還是有很多學問的。下面簡述下demo中的3中實現(xiàn)方式:
實現(xiàn)方式1:用過AVPlayer的 可能都遇到過這么一個現(xiàn)象:黑屏。黑屏的原因是app最多支持16個AVPlayerLayer同時存在,等到17個的時候就會出現(xiàn)黑屏,解決這個問題需要及時釋放AVPlayer相關資源。 如果一屏需要展示的播放個數(shù)大于16,那么AVPlayer就不適合用來實現(xiàn)小視頻,當然實際過程中1屏幕就最多6,7個人視頻同時播放了。
實現(xiàn)方式2:利用AVAssetReader 中的獲取到的CGImageRef
將每一幀都放到layer.contents中展示
self.videoImageView.layer.contents = (__bridge id)(imageRef);
實現(xiàn)方式3:實際開發(fā)中 gif圖 你可以是服務器下載下來的,也可以是通過AVAssetReader 獲取到所需要的UIImages,制作成gif,然后保存展示。
以上3中方式,1的性能較差。2,3各有優(yōu)點。
這3種方式都有一個最大的特點,cell滑動過程中不做視頻的加載,這樣做為了滑動更流暢,結(jié)束的時候再加載當前屏幕中的cell視頻。對于gif和customPlayerLayer如果想要修改成滑動時播放,需要修改timer的runloop的model。
//cell 不在可視區(qū)域內(nèi) 可以暫停播放 釋放資源
- (BOOL)currentCellIsNotShowedInScreen
{
UITableView *table;
for (UIView* next = [self superview]; next; next = next.superview) {
UIResponder* nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UITableView class]]) {
table = (UITableView *)nextResponder;
break;
}
}
if (table) {
CGRect cellR = [table rectForRowAtIndexPath:self.currentIndexPath];
if ((table.contentOffset.y + table.frame.size.height) < CGRectGetMinY(cellR) || table.contentOffset.y > CGRectGetMaxY(cellR)) {
return true;
}
}
return false;
}
demo
總結(jié):demo中cell元素比較簡單,但也能比較不同方式的性能差異,視頻播放其實并不難實現(xiàn),主要是注意性能,利用線程的特性,處理好視頻播放。視頻播放肯定還有其他實現(xiàn)方式。