利用AVAssetReader制作简易视频播放器

需求:已经存在一个音频播放器的的情况下,需要同时播放一个视频(没有声音),做成替换视频背景音的假象.

方案-:使用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.音频不支持流方式,必须是已经下载完成的文件

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);

根据帧间隔做一个定时器来替换绘制的图片,每次计时获取一张图片


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];

});

获取图片函数


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问题还有很多,如播放快慢的时间校准,前后台切换问题等

你可能感兴趣的:(利用AVAssetReader制作简易视频播放器)