iOS视频倒放

iOS视频倒放

视频的倒放就是视频从后往前播放,这个只适应于视频图像,对声音来说倒放只是噪音,没什么意义,所以倒放的时候声音都是去除的。


倒放实现

一般对H264编码的视频进行解码,都是从头至尾进行的,因为视频存在I帧、P帧、B帧,解码P帧的时候需要依赖前面最近的I帧或者前一个P帧,解码B帧的时候,不仅要依赖前面的缓存数据还要依赖后面的数据,这就导致了我们没法真正让解码器从后往前解码,只能把视频分成很多足够小的片段,对每一个片段单独进行处理。具体思路如下:我们需要先seek到最后第n个GOP的第一帧-I帧,然后把当前这个点到视频最后的图像都解码出来,存储在一个数组里面。这个n是根据解码数据大小定的,因为如果解码出来的数据太大,内存占用过多,会导致程序被杀掉,我是把视频分成一秒一个小片段,对这些片段,倒过来进行解码,然后把每一段解出来的图像,倒过来编码。使用AVFoundation可以很方便的实现github

//  SJReverseUtility.h
//  playback
//
//  Created by Lightning on 2018/7/12.

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

typedef void(^ReverseCallBack)(AVAssetWriterStatus status, float progress, NSError *error);

@interface SJReverseUtility : NSObject

- (instancetype)initWithAsset:(AVAsset *)asset outputPath:(NSString *)path;

- (void)startProcessing;

- (void)cancelProcessing;

@property (nonatomic, copy) ReverseCallBack callBack;

@property (nonatomic, assign) CMTimeRange timeRange;

@end
//
//  SJReverseUtility.m
//  playback
//
//  Created by Lightning on 2018/7/12.

#import "SJReverseUtility.h"

@interface SJReverseUtility()

@property (nonatomic, strong) NSMutableArray *samples;

@property (nonatomic, strong) AVAsset *asset;

@property (nonatomic, strong) NSMutableArray *tracks;

@property (nonatomic, strong) AVMutableComposition *composition;

@property (nonatomic, strong) AVAssetWriter *writer;

@property (nonatomic, strong) AVAssetWriterInput *writerInput;

@property (nonatomic, strong) AVAssetWriterInputPixelBufferAdaptor *writerAdaptor;

@property (nonatomic, assign) uint frame_count;

@property (nonatomic, strong) AVMutableCompositionTrack *compositionTrack;

@property (nonatomic, assign) CMTime offsetTime;

@property (nonatomic, assign) CMTime intervalTime;

@property (nonatomic, assign) CMTime segDuration;

@property (nonatomic, assign) BOOL shouldStop;

@property (nonatomic, copy) NSString *path;

@end

@implementation SJReverseUtility

- (instancetype)initWithAsset:(AVAsset *)asset outputPath:(NSString *)path
{
    self = [super init];
    if (self) {
        _asset = asset;

        _composition = [AVMutableComposition composition];
        AVMutableCompositionTrack *ctrack = [_composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
        _compositionTrack = ctrack;

        _timeRange = kCMTimeRangeInvalid;
        _frame_count = 0;
        _offsetTime = kCMTimeZero;
        _intervalTime = kCMTimeZero;
        [self setupWriterWithPath:path];

    }
    return self;
}

- (void)cancelProcessing
{
    self.shouldStop = YES;
}

- (void)startProcessing
{
    if (CMTIMERANGE_IS_INVALID(_timeRange)) {
        _timeRange = CMTimeRangeMake(kCMTimeZero, _asset.duration);
    }
    CMTime duration = _asset.duration;
    CMTime segDuration = CMTimeMake(1, 1);
    self.segDuration = segDuration;
    NSArray *videoTracks = [_asset tracksWithMediaType:AVMediaTypeVideo];
    AVAssetTrack *track = videoTracks[0];
    //should set before starting
    self.writerInput.transform = track.preferredTransform;//fix video orientation

    [self.writer startWriting];
    [self.writer startSessionAtSourceTime:kCMTimeZero]; //start processing

    //divide video into n segmentation
    int n = (int)(CMTimeGetSeconds(duration)/CMTimeGetSeconds(segDuration)) + 1;
    if (CMTIMERANGE_IS_VALID(_timeRange)) {
        n = (int)(CMTimeGetSeconds(_timeRange.duration)/CMTimeGetSeconds(segDuration)) + 1;
        duration = CMTimeAdd(_timeRange.start, _timeRange.duration);

    }

    __weak typeof(self) weakSelf = self;
    for (int i = 1; i < n; i++) {
        CMTime offset = CMTimeMultiply(segDuration, i);
        if (CMTimeCompare(offset, duration) > 0) {
            break;
        }
        CMTime start = CMTimeSubtract(duration, offset);
        if (CMTimeCompare(start, _timeRange.start) < 0) {
            start = kCMTimeZero;
            segDuration = CMTimeSubtract(duration, CMTimeMultiply(segDuration, i-1));
        }
        self.compositionTrack = [_composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
        [self.compositionTrack insertTimeRange:CMTimeRangeMake(start, segDuration) ofTrack:track atTime:kCMTimeZero error:nil];

        [self generateSamplesWithTrack:_composition];

        [self encodeSampleBuffer];

        if (self.shouldStop) {
            [self.writer cancelWriting];
            if ([[NSFileManager defaultManager] fileExistsAtPath:_path]) {
                [[NSFileManager defaultManager] removeItemAtPath:_path error:nil];
            }
            !weakSelf.callBack? :weakSelf.callBack(weakSelf.writer.status, -1, weakSelf.writer.error);

            return;
        }

        [self.compositionTrack removeTimeRange:CMTimeRangeMake(start, segDuration)];

        !weakSelf.callBack? :weakSelf.callBack(weakSelf.writer.status, (float)i/n, weakSelf.writer.error);
    }
    [self.writer finishWritingWithCompletionHandler:^{
        !weakSelf.callBack? :weakSelf.callBack(weakSelf.writer.status, 1.0f, weakSelf.writer.error);
    }];

}

- (void)setupWriterWithPath:(NSString *)path
{
    NSURL *outputURL = [NSURL fileURLWithPath:path];
    AVAssetTrack *videoTrack = [[_asset tracksWithMediaType:AVMediaTypeVideo] lastObject];

    // Initialize the writer
    self.writer = [[AVAssetWriter alloc] initWithURL:outputURL
                                            fileType:AVFileTypeMPEG4
                                               error:nil];
    NSDictionary *videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
                                           @(videoTrack.estimatedDataRate), AVVideoAverageBitRateKey,
                                           nil];
    int width = videoTrack.naturalSize.width;
    int height = videoTrack.naturalSize.height;
    NSDictionary *writerOutputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                          AVVideoCodecH264, AVVideoCodecKey,
                                          [NSNumber numberWithInt:videoTrack.naturalSize.width], AVVideoWidthKey,
                                          [NSNumber numberWithInt:videoTrack.naturalSize.height], AVVideoHeightKey,
                                          videoCompressionProps, AVVideoCompressionPropertiesKey,
                                          nil];
    AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
                                                                     outputSettings:writerOutputSettings
                                                                   sourceFormatHint:(__bridge CMFormatDescriptionRef)[videoTrack.formatDescriptions lastObject]];
    [writerInput setExpectsMediaDataInRealTime:NO];
    self.writerInput = writerInput;
    self.writerAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil];
    [self.writer addInput:self.writerInput];

}

- (void)generateSamplesWithTrack:(AVAsset *)asset
{
    // Initialize the reader
    AVAssetReader *reader = [[AVAssetReader alloc] initWithAsset:asset error:nil];
    AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] lastObject];

    NSDictionary *readerOutputSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange], kCVPixelBufferPixelFormatTypeKey, nil];
    AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack
                                                                                        outputSettings:readerOutputSettings];
    [reader addOutput:readerOutput];
    [reader startReading];

    // read in the samples
    _samples = [[NSMutableArray alloc] init];

    CMSampleBufferRef sample;
    while(sample = [readerOutput copyNextSampleBuffer]) {
        [_samples addObject:(__bridge id)sample];
        NSLog(@"count = %d",_samples.count);
        CFRelease(sample);
    }
    if (_samples.count > 0 ) {
        self.intervalTime = CMTimeMakeWithSeconds(CMTimeGetSeconds(self.segDuration)/(float)(_samples.count), _asset.duration.timescale);
    }

}

- (void)encodeSampleBuffer
{
    for(NSInteger i = 0; i < _samples.count; i++) {
        // Get the presentation time for the frame

        CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp((__bridge CMSampleBufferRef)_samples[i]);

        presentationTime = CMTimeAdd(_offsetTime, self.intervalTime);

        size_t index = _samples.count - i - 1;

        if (0 == _frame_count) {
            presentationTime = kCMTimeZero;
            index = _samples.count - i - 2; //倒过来的第一帧是黑的丢弃
        }
        CMTimeShow(presentationTime);

        CVPixelBufferRef imageBufferRef = CMSampleBufferGetImageBuffer((__bridge CMSampleBufferRef)_samples[index]);

        while (!_writerInput.readyForMoreMediaData) {
            [NSThread sleepForTimeInterval:0.1];
        }
        _offsetTime = presentationTime;

        BOOL success = [self.writerAdaptor appendPixelBuffer:imageBufferRef withPresentationTime:presentationTime];
        _frame_count++;
        if (!success) {
            NSLog(@"status = %ld",(long)self.writer.status);
            NSLog(@"status = %@",self.writer.error);
        }

    }

}

@end

在iOS里面,这段代码可以倒放任意时长的视频。但是在每一帧的时间戳上,还有待改进。



iOS视频倒放

原文地址:https://www.cnblogs.com/song-jw/p/9356542.html

时间: 2024-08-19 03:50:19

iOS视频倒放的相关文章

怎么实现EDIUS中视频倒放的

我们在播放视频素材的时候,可能会需要视频的快速.慢速播放或者倒放,这些在EDIUS中该怎么实现呢?今天的EDIUS教程就来为大家解决这个问题,一起跟着小编来调整视频播放速度和实现EDIUS倒放吧! 若有疑问可直接访问:http://www.ediuschina.com/jiqiao/sudu-shezhi.html 首先我们先导入一个视频素材到素材库中,然后拖拽到时间线上.详细请见下图: 接下来我们右击素材,选择“时间效果”——“速度”,我们就会见到如下所示的窗口: 我们来看一下这个窗口.其中“

浅谈iOS视频开发

这段时间对视频开发进行了一些了解,在这里和大家分享一下我自己觉得学习步骤和资料,希望对那些对视频感兴趣的朋友有些帮助. 一.iOS系统自带播放器 要了解iOS视频开发,首先我们从系统自带的播放器说起,一.我们可以直接播放视频,看到效果,不然搞了半天还播放不了视频,会让大家失去兴趣.二.其实对于很多需求来说,系统的播放器就能够胜任.简单介绍下 1.MPMoviePlayerController 在iOS中播放视频可以使用MPMoviePlayerController类来完成,具备一般的播放器控制功

iOS视频开发经验

iOS视频开发经验 手机比PC的优势除了便携外,我认为最重要的就是可以快速方便的创作多媒体作品.照片分享,语音输入,视频录制,地理位置.一个成功的手机APP从产品形态上都有这其中的一项或多项,比如instagram,微信.如果把Web2.0的交互体验照搬到手机上就是死路一条. 当智能手机遇上视频就像潘金莲遇上西门庆,各取所需一拍即合,想不发生点事情都难.他们的结晶就是微视频.微视频可以说把手机的视频录制和碎片时间两个特点发挥到了极致,视频相关的APP现在无温不火的原因我认为跟坑爹的运营商有关.虽

iOS视频编辑SDK

IOS视频编辑SDK接入说明 一.名词解释 分辨率:用于计算机视频处理的图像,以水平和垂直方向上所能显示的像素数来表示分辨率.常见视频分辨率的有1080P即1920x1080,720P即1080x720,640x480等. 宽高比:视频分辨率的宽高比,常见的有16:9,4:3,1:1.锐动视频编辑SDK对各宽高比的视频都支持编辑,导出的默认分辨率是640x360,宽高比是16:9. 帧率:每秒的帧数(fps)或者说帧率表示图形处理器处理场时每秒钟能够更新的次数. 码率: 数据传输时单位时间传送的

iOS视频开发

视频实质: 纯粹的视频(不包括音频)实质上就是一组帧图片,经过视频编码成为视频(video)文件再把音频(audio)文件有些还有字幕文件组装在一起成为我们看到的视频(movie)文件.1秒内出现的图片数就是帧率,图片间隔越小画面就越流畅,所以帧率越高效果就越好,需要的存储空间也就越多. 视频编码: 因为不进行编码的视频数据量非常大,会造成存储和传输上的困难,所以视频文件都需要在录制完成后进行编码.视频编码主要从两个维度压缩数据. 1.单张图像某一区域相邻像素相似,比如一片红色只记录红色色值和区

iOS 视频开发学习

原文:浅谈iOS视频开发 这段时间对视频开发进行了一些了解,在这里和大家分享一下我自己觉得学习步骤和资料,希望对那些对视频感兴趣的朋友有些帮助. 一.iOS系统自带播放器 要了解iOS视频开发,首先我们从系统自带的播放器说起,一.我们可以直接播放视频,看到效果,不然搞了半天还播放不了视频,会让大家失去兴趣.二.其实对于很多需求来说,系统的播放器就能够胜任.简单介绍下 1.MPMoviePlayerController 在iOS中播放视频可以使用MPMoviePlayerController类来完

IOS 视频分解图片、图片合成视频

在IOS视频处理中,视频分解图片和图片合成视频是IOS视频处理中经常遇到的问题,这篇博客就这两个部分对IOS视频图像的相互转换做一下分析. (1)视频分解图片 这里视频分解图片使用的是AVAssetImageGenerator,利用这个class可以很方便的实现不同时间戳下,视频帧的抓取.注意一般这种视频分解图片帧的方法都是放在子线程中的,而UI更新操作都是放在主线程中的.下面来看看核心代码: _imageGenerator = [[AVAssetImageGenerator alloc] in

iOS 视频开发

MediaPlayer framework 播放电影文件: iOS sdk中可以使用MPMoviePlayerController来播放电影文件.但是在iOS设备上播放电影文件有严格的格式要求,只能播放下面两个格式的电影文件. ? H.264 (Baseline Profile Level 3.0)? MPEG-4 Part 2 video (Simple Profile) 幸运的是你可以先使用iTunes将文件转换成上面两个格式.MPMoviePlayerController还可以播放互联网上

&lt;精华篇&gt;:iOS视频大全-持续更新

注意:新浪微博分享的资料和简书分享的资料,略有不同! 小码哥swift3.0版 斗鱼项目视频:点击下载  iOS开发25个项目实战:点击下载 2016PHP全套下载:点击下载  黑马刀哥iOS视频精选:点击下载  支付宝生活圈开发:点击下载 lldb高级用法:点击下载  高级数据恢复系列视频:点击下载 逆向资源视频初级视频:点击下载 2016HTML5全套下载:点击下载 iOS单元测试视频高清:点击下载 面试上百个iOS后的求职感受:点击下载  优化TableView初体验:点击观看 看大神如何