利用AVAssetReader制作簡易視頻播放器

需求:已經(jīng)存在一個音頻播放器的的情況下,需要同時播放一個視頻(沒有聲音),做成替換視頻背景音的假象.

方案-:使用AVPlayer播放AVMutableComposition合成的資源

主要代碼如下:


AVAsset *videoAsset = [AVAsset assetWithURL:baseVideoUrl];

AVAssetTrack *videoTrack = [videoAsset tracksWithMediaType:AVMediaTypeVideo].firstObject;

AVMutableComposition *mix_composition = [AVMutableComposition composition];

AVMutableCompositionTrack *mul_v_track = [mix_composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

BOOL success = [mul_v_track insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil];

if (!success) {

NSLog(@"視頻插入失敗");

return;

}

AVAsset *a_asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:audioPath]];

NSArray *a_tracks = [a_asset tracksWithMediaType:AVMediaTypeAudio];

for (AVAssetTrack *a_track in a_tracks) {

AVMutableCompositionTrack *mul_audio_track = [mix_composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

BOOL success = [mul_audio_track insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:a_track atTime:kCMTimeZero error:nil];

if (!success) {

NSLog(@"插入音頻失敗");

return;

}

}

AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:mix_composition];

self.player = [AVPlayer playerWithPlayerItem:item];

該方案缺點:

1.音頻不支持流方式,必須是已經(jīng)下載完成的文件

2.切換音頻時會有一定的延遲

方案二:使用AVAssetReader讀取一幀一幀的圖片并渲染,音頻使用單獨的播放器去播放


AVURLAsset *vas = [AVURLAsset URLAssetWithURL:self.fileUrl options:@{AVURLAssetPreferPreciseDurationAndTimingKey:@(YES)}];

AVAssetReader *videoReader = [AVAssetReader assetReaderWithAsset:vas error:nil];

AVAssetTrack *videoTrack = [vas tracksWithMediaType:AVMediaTypeVideo].firstObject;

NSMutableDictionary *outPutSetting = [NSMutableDictionary dictionary];

[outPutSetting setObject:@(kCVPixelFormatType_32BGRA) forKey:(id)kCVPixelBufferPixelFormatTypeKey];

AVAssetReaderTrackOutput *outPut = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:outPutSetting];

[videoReader addOutput:outPut];

outPut.supportsRandomAccess = YES;

CGFloat frameDuration = CMTimeGetSeconds(videoTrack.minFrameDuration);

self.duration = self.asset.duration;

CMTimeShow(self.asset.duration);

根據(jù)幀間隔做一個定時器來替換繪制的圖片,每次計時獲取一張圖片


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

NSTimer *timer = [NSTimer timerWithTimeInterval:frameDuration target:self selector:@selector(readNextFrame) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

[[NSRunLoop currentRunLoop] run];

self.decodeTimer = timer;

[timer fire];

});

獲取圖片函數(shù)


CMSampleBufferRef sampleBuffer = [self.output copyNextSampleBuffer];

_currentPlayTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer);

CMTimeShow(_currentPlayTime);

CVImageBufferRef cvimagebuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

CVPixelBufferLockBaseAddress(cvimagebuffer, 0);

void *baseAddre = CVPixelBufferGetBaseAddress(cvimagebuffer);

size_t cvwidth = CVPixelBufferGetWidth(cvimagebuffer);

size_t cvheight = CVPixelBufferGetHeight(cvimagebuffer);

size_t bytesPerRow = CVPixelBufferGetBytesPerRow(cvimagebuffer);

CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();

CGContextRef context = CGBitmapContextCreate(baseAddre, cvwidth, cvheight, 8, bytesPerRow, space, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);

CGImageRef resImage = CGBitmapContextCreateImage(context);

CVPixelBufferUnlockBaseAddress(cvimagebuffer, 0);

CGContextRelease(context);

CGColorSpaceRelease(space);

_cgimage = resImage;

封裝播放器,播放,暫停,滑動指定時間播放


//暫停就是將計時器停止就可以了

- (void)pause {

self.status = KGPreviewPlayerStatusPause;

//停止計時器,即停止了解碼操作

[self.decodeTimer setFireDate:[NSDate distantFuture]];

}


//seekTime需要重新指定reader的timerange

- (void)seekTime:(CMTime)time {

if (self.status == KGPreviewPlayerStatusEnd) {

CMTimeRange range = CMTimeRangeMake(time, CMTimeSubtract(self.asset.duration, time));

NSValue *value = [NSValue valueWithCMTimeRange:range];

[self.output resetForReadingTimeRanges:@[value]];

} else {

[self.decodeTimer setFireDate:[NSDate distantPast]];

[self.reader cancelReading];

if (self.status != KGPreviewPlayerStatusPause) {

[self.clock lockWhenCondition:100];

}

_currentPlayTime = time;

[self reConfigReader];

[self startDecode];

}

}

方案二demo地址:https://github.com/taozaizai/AVAssertReaderDemo.git

demo問題還有很多,如播放快慢的時間校準(zhǔn),前后臺切換問題等

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容