美文网首页
利用AVAssetReader制作简易视频播放器

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

作者: 逃之不桃 | 来源:发表于2019-05-31 15:43 被阅读0次

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

    方案-:使用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制作简易视频播放器

          本文链接:https://www.haomeiwen.com/subject/lfcgtctx.html