├── VideoReverseDemo
├── test.mp4
├── ViewController.h
├── AppDelegate.h
├── main.m
├── HKMediaOperationTools.h
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── AppDelegate.m
├── ViewController.m
└── HKMediaOperationTools.m
├── 9805248486F9DA46E2737CFA3CD393F2.png
├── D0AAB2A9A3AD9E24FA0107E6C3AF38EE.png
├── VideoReverseDemo.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── HuangKai.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── VideoReverseDemo.xcscheme
└── project.pbxproj
├── VideoReverseDemoTests
├── Info.plist
└── VideoReverseDemoTests.m
├── VideoReverseDemoUITests
├── Info.plist
└── VideoReverseDemoUITests.m
└── README.md
/VideoReverseDemo/test.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KayWong/VideoReverse/HEAD/VideoReverseDemo/test.mp4
--------------------------------------------------------------------------------
/9805248486F9DA46E2737CFA3CD393F2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KayWong/VideoReverse/HEAD/9805248486F9DA46E2737CFA3CD393F2.png
--------------------------------------------------------------------------------
/D0AAB2A9A3AD9E24FA0107E6C3AF38EE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KayWong/VideoReverse/HEAD/D0AAB2A9A3AD9E24FA0107E6C3AF38EE.png
--------------------------------------------------------------------------------
/VideoReverseDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/VideoReverseDemo/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // VideoReverseDemo
4 | //
5 | // Created by HuangKai on 15/12/22.
6 | // Copyright © 2015年 HuangKai. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/VideoReverseDemo/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // VideoReverseDemo
4 | //
5 | // Created by HuangKai on 15/12/22.
6 | // Copyright © 2015年 HuangKai. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/VideoReverseDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // VideoReverseDemo
4 | //
5 | // Created by HuangKai on 15/12/22.
6 | // Copyright © 2015年 HuangKai. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/VideoReverseDemo/HKMediaOperationTools.h:
--------------------------------------------------------------------------------
1 | //
2 | // HKMediaOperationTools.h
3 | // YeahMV
4 | //
5 | // Created by HuangKai on 15/12/18.
6 | // Copyright © 2015年 QiuShiBaiKe. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | typedef void(^HKProgressHandle)(CGFloat progress);
13 |
14 | @interface HKMediaOperationTools : NSObject
15 | + (AVAsset *)assetByReversingAsset:(AVAsset *)asset videoComposition:(AVMutableVideoComposition *)videoComposition duration:(CMTime)duration outputURL:(NSURL *)outputURL progressHandle:(HKProgressHandle)progressHandle cancle:(BOOL *)cancle;
16 | @end
17 |
--------------------------------------------------------------------------------
/VideoReverseDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/VideoReverseDemoTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/VideoReverseDemoUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/VideoReverseDemo.xcodeproj/xcuserdata/HuangKai.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | VideoReverseDemo.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 37B998061C287CE200710151
16 |
17 | primary
18 |
19 |
20 | 37B9981F1C287CE200710151
21 |
22 | primary
23 |
24 |
25 | 37B9982A1C287CE200710151
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/VideoReverseDemoTests/VideoReverseDemoTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // VideoReverseDemoTests.m
3 | // VideoReverseDemoTests
4 | //
5 | // Created by HuangKai on 15/12/22.
6 | // Copyright © 2015年 HuangKai. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface VideoReverseDemoTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation VideoReverseDemoTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/VideoReverseDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/VideoReverseDemoUITests/VideoReverseDemoUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // VideoReverseDemoUITests.m
3 | // VideoReverseDemoUITests
4 | //
5 | // Created by HuangKai on 15/12/22.
6 | // Copyright © 2015年 HuangKai. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface VideoReverseDemoUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation VideoReverseDemoUITests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 |
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 |
22 | // In UI tests it is usually best to stop immediately when a failure occurs.
23 | self.continueAfterFailure = NO;
24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
25 | [[[XCUIApplication alloc] init] launch];
26 |
27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
28 | }
29 |
30 | - (void)tearDown {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | [super tearDown];
33 | }
34 |
35 | - (void)testExample {
36 | // Use recording to get started writing UI tests.
37 | // Use XCTAssert and related functions to verify your tests produce the correct results.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/VideoReverseDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/VideoReverseDemo/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // VideoReverseDemo
4 | //
5 | // Created by HuangKai on 15/12/22.
6 | // Copyright © 2015年 HuangKai. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VideoReverse
2 | # 使用AVFoundation实现视频倒序
3 |
4 | #背景
5 | 最近在做一个视频类的APP,在视频的编辑模块有一个视频倒序文件的需求,这个和倒序播放的需求不一样,要求的是生成新的倒序视频文件
6 | #研究过程
7 | 一开始原本以为这个需求很简单,但是实现的时候遇到了各种麻烦,在最初,本以为可以直接使用AVMutableComposition对Track直接反转的操作就可以实现视频的倒序了(看来我真是Too Naive了),然后发现AVFoundation没有提供这样的接口。后面通过各种Google、StackOverflow,还是让我找到了一些实现的思路,谢谢下面俩哥们的实现思路给了我极大的启发
8 | https://github.com/mikaelhellqvist/ReverseClip
9 | https://github.com/whydna/ReverseAVAsset
10 |
11 |
12 | **先讲一下视频倒序的实现原理:**
13 | 视频文件如果除去哪些杂七杂八的元数据,那剩下的就是一帧帧的视频图片了(视频帧的分类这里不展开讨论),简单的来讲,就是把视频文件里的每一帧按照相反的方向去排列就可以让视频倒着播放了。
14 |
15 | 大体的实现思路如下:
16 | 
17 |
18 | 图解:
19 |
20 | 1. 把一个视频拆分成多个AVAssetTrack,这样做的原因是因为,使用AVAssetReader读取每一帧SampleBuffer的数据是需要把数据加载到内存里面去的,如果直接把整个视频的SampleBuffer加载到内存,会造成闪退
21 |
22 | 2. 拆分成多个AVAssetTrack之后,使用AVAssetReader**从最后一个AVAssetTrack**读取SampleBuffer,这样我们可以从后往前获取视频的帧数据
23 |
24 | 3. 获取数据之后通过AVAssetWriterInputPixelBufferAdaptor的- appendPixelBuffer:withPresentationTime:方法来把我们获取到的SampleBuffer倒序写入一个文件
25 |
26 | #问题
27 | 1. 经过一番折腾,惊喜的发现Demo已经可以Run起来,并且可以输出倒序的视频文件,但是发现最后的时间总是不对,会少了一截,因为加载AVAsset的时候并没有把准确的视频长度信息加载进来,所以我们在加载AVAsset的时候需要调用loadValuesAsynchronouslyForKeys方法来获取准确的视频信息。
28 | 2. 但是对于我的需求却并未满足,因为我们在上面说到,我们获取的时候是一个原始的视频文件,这个视频文件没有经过任何的编辑,所以一切看起来都没啥问题,但是有时候我们需要对视频进行一些编辑,比如合成,比如剪切,比如对视频做一些Transform等等的一些编辑操作,水印,动画等等,但是这些操作只是生成了一个新的AVMutableComposition,并没有生成新的视频文件,这个时候如果我们想要直接在经过编辑的文件来生成一个倒序的视频怎么办?在网上找了很久资料,还是没有找到实现的办法,最后翻阅了官方文档之后看到了AVAssetReaderOutput有一个子类AVAssetReaderVideoCompositionOutput,这个子类有一个属性videoComposition,可以赋值一个AVVideoComposition指令容器,这样我们就可以通过这个属性来准确的加载被编辑过的视频的编辑指令了。但是指令有一个timeRange属性,我们直接生成的一组AVAssetTrack和原来的AVVideoComposition并不匹配,这个时候我用到了一个Trick的方法来对应
29 |
30 | 
31 |
32 | ##解决办法
33 | 我在分割AVAsset的时候,在生成一个新的AVComposition的实例时使用insertTimeRange方法插入的时间并非是kCMTimeZero,而是每一个AVAssetTrack在原来的AVAsset对应的开始和结束位置,这样使用AVAssetReaderVideoCompositionOutput来配置videoComposition,Reader就可以准确的读取包含了编辑指令每一帧的SampleBuffer了,然后再按照上面的方法来倒序写入SampleBuffer即可
34 |
35 | #经验总结
36 | * appendPixelBuffer:withPresentationTime:方法不能从后往前写,如果你这样做,会写入失败
37 | * 兼顾了编辑指令的实现方法会对性能有一定的浪费,因为我们不在kCMTimeZero时间点生成的AVAssetTrack会在Reader读取的时候获取的都是黑帧,随着视频的分割段数越多,视频的时间越长,倒序生成的时间也就越长
38 | * 目前分割的段数是以每段视频长度最长为1s来分割,如果来操作很长很大的视频,可能需要耗费很长的时间
39 | * 分割视频的时候目前没有对编辑过和没有编辑过的视频作为区分,如果只针对原视频倒序输出,可以把每个AVAssetTrack的开始时间设置为kCMTimeZero,这样性能会有很大的提升
40 |
--------------------------------------------------------------------------------
/VideoReverseDemo/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // VideoReverseDemo
4 | //
5 | // Created by HuangKai on 15/12/22.
6 | // Copyright © 2015年 HuangKai. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import
11 | #import "HKMediaOperationTools.h"
12 | @interface ViewController ()
13 | @property (weak, nonatomic) IBOutlet UIButton *startReverseButton;
14 | @property (nonatomic , assign) BOOL isCancel;
15 | @property (nonatomic , strong) AVAsset *asset;
16 | @property (weak, nonatomic) IBOutlet UILabel *progressLabel;
17 | @end
18 |
19 | @implementation ViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 |
24 |
25 |
26 |
27 |
28 | self.startReverseButton.enabled = NO;
29 | self.isCancel = NO;
30 | NSString *sourceMoviePath = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"mp4"];
31 | NSURL *sourceMovieURL = [NSURL fileURLWithPath:sourceMoviePath];
32 | self.asset = [AVAsset assetWithURL:sourceMovieURL];
33 | [self.asset loadValuesAsynchronouslyForKeys:@[@"duration", @"tracks"] completionHandler:^{
34 | dispatch_async(dispatch_get_main_queue(), ^{
35 | self.startReverseButton.enabled = YES;
36 | });
37 | }];
38 | // Do any additional setup after loading the view, typically from a nib.
39 | }
40 |
41 | - (void)didReceiveMemoryWarning {
42 | [super didReceiveMemoryWarning];
43 | // Dispose of any resources that can be recreated.
44 | }
45 | - (IBAction)startReverse:(id)sender {
46 | self.isCancel = NO;
47 | NSString * temppath = NSTemporaryDirectory();
48 | temppath = [temppath stringByAppendingPathComponent:@"reversed.video"];
49 | BOOL exists =[[NSFileManager defaultManager] fileExistsAtPath:temppath isDirectory:NULL];
50 | if (!exists) {
51 | [[NSFileManager defaultManager] createDirectoryAtPath:temppath withIntermediateDirectories:YES attributes:nil error:NULL];
52 | }
53 | NSString *filename = @"reversed.mp4";
54 | temppath = [temppath stringByAppendingPathComponent:filename];
55 | if ([[NSFileManager defaultManager] fileExistsAtPath:temppath isDirectory:NULL]) {
56 | [[NSFileManager defaultManager] removeItemAtPath:temppath error:NULL];
57 | }
58 | NSLog(@"%@",temppath);
59 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
60 | [HKMediaOperationTools assetByReversingAsset:self.asset videoComposition:nil duration:self.asset.duration outputURL:[NSURL fileURLWithPath:temppath] progressHandle:^(CGFloat progress) {
61 | dispatch_async(dispatch_get_main_queue(), ^{
62 | self.progressLabel.text = [NSString stringWithFormat:@"%@ %%",@(progress*100)];
63 | });
64 | NSLog(@"%@",@(progress*100));
65 | } cancle:&_isCancel];
66 | });
67 |
68 | }
69 | - (IBAction)cancelReverse:(id)sender {
70 | self.isCancel = YES;
71 | }
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/VideoReverseDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
28 |
36 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/VideoReverseDemo.xcodeproj/xcuserdata/HuangKai.xcuserdatad/xcschemes/VideoReverseDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/VideoReverseDemo/HKMediaOperationTools.m:
--------------------------------------------------------------------------------
1 | //
2 | // HKMediaOperationTools.m
3 | // YeahMV
4 | //
5 | // Created by HuangKai on 15/12/18.
6 | // Copyright © 2015年 QiuShiBaiKe. All rights reserved.
7 | //
8 |
9 | #import "HKMediaOperationTools.h"
10 |
11 | @implementation HKMediaOperationTools
12 |
13 | + (AVAsset *)assetByReversingAsset:(AVAsset *)asset videoComposition:(AVMutableVideoComposition *)videoComposition duration:(CMTime)duration outputURL:(NSURL *)outputURL progressHandle:(HKProgressHandle)progressHandle cancle:(BOOL *)cancle {
14 | if (*(cancle)) {
15 | return nil;
16 | }
17 | NSError *error;
18 | //获取视频的总轨道
19 | AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] lastObject];
20 | //按照每秒一个视频的长度,分割轨道,生成对应的时间范围
21 | NSMutableArray *timeRangeArray = [NSMutableArray array];
22 | NSMutableArray *startTimeArray = [NSMutableArray array];
23 | CMTime startTime = kCMTimeZero;
24 | for (NSInteger i = 0; i <(CMTimeGetSeconds(duration)); i ++) {
25 | CMTimeRange timeRange = CMTimeRangeMake(startTime, CMTimeMakeWithSeconds(1, duration.timescale));
26 | if (CMTimeRangeContainsTimeRange(videoTrack.timeRange, timeRange)) {
27 | [timeRangeArray addObject:[NSValue valueWithCMTimeRange:timeRange]];
28 | } else {
29 | timeRange = CMTimeRangeMake(startTime, CMTimeSubtract(duration, startTime));
30 | [timeRangeArray addObject:[NSValue valueWithCMTimeRange:timeRange]];
31 | }
32 | [startTimeArray addObject:[NSValue valueWithCMTime:startTime]];
33 | startTime = CMTimeAdd(timeRange.start, timeRange.duration);
34 | }
35 |
36 | NSMutableArray *tracks = [NSMutableArray array];
37 | NSMutableArray *assets = [NSMutableArray array];
38 |
39 |
40 | for (NSInteger i = 0; i < timeRangeArray.count; i ++) {
41 | AVMutableComposition *subAsset = [[AVMutableComposition alloc]init];
42 | AVMutableCompositionTrack *subTrack = [subAsset addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
43 | [subTrack insertTimeRange:[timeRangeArray[i] CMTimeRangeValue] ofTrack:videoTrack atTime:[startTimeArray[i] CMTimeValue] error:nil];
44 | AVAsset *assetNew = [subAsset copy];
45 | AVAssetTrack *assetTrackNew = [[assetNew tracksWithMediaType:AVMediaTypeVideo] lastObject];
46 | [tracks addObject:assetTrackNew];
47 | [assets addObject:assetNew];
48 | }
49 |
50 | AVAssetReader *totalReader = nil ;;
51 |
52 | NSDictionary *totalReaderOutputSettings = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange], kCVPixelBufferPixelFormatTypeKey, nil];
53 | AVAssetReaderOutput *totalReaderOutput = nil;
54 | if (videoComposition) {
55 | totalReaderOutput = [AVAssetReaderVideoCompositionOutput assetReaderVideoCompositionOutputWithVideoTracks:@[videoTrack] videoSettings:totalReaderOutputSettings];
56 | ((AVAssetReaderVideoCompositionOutput *)totalReaderOutput).videoComposition = videoComposition;
57 | } else {
58 | totalReaderOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:totalReaderOutputSettings];
59 | }
60 | totalReader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
61 | if([totalReader canAddOutput:totalReaderOutput]){
62 | [totalReader addOutput:totalReaderOutput];
63 | } else {
64 | return nil;
65 | }
66 | [totalReader startReading];
67 | NSMutableArray *sampleTimes = [NSMutableArray array];
68 | CMSampleBufferRef totalSample;
69 |
70 | while((totalSample = [totalReaderOutput copyNextSampleBuffer])) {
71 | CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp(totalSample);
72 | [sampleTimes addObject:[NSValue valueWithCMTime:presentationTime]];
73 | CFRelease(totalSample);
74 | }
75 |
76 | //配置Writer
77 | AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:outputURL
78 | fileType:AVFileTypeMPEG4
79 | error:&error];
80 | NSDictionary *videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
81 | @(videoTrack.estimatedDataRate), AVVideoAverageBitRateKey,
82 | nil];
83 | CGFloat width = videoTrack.naturalSize.width;
84 | CGFloat height = videoTrack.naturalSize.height;
85 | if (videoComposition) {
86 | width = videoComposition.renderSize.width;
87 | width = videoComposition.renderSize.height;
88 | }
89 | NSDictionary *writerOutputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
90 | AVVideoCodecH264, AVVideoCodecKey,
91 | [NSNumber numberWithInt:height], AVVideoHeightKey,
92 | [NSNumber numberWithInt:width], AVVideoWidthKey,
93 | videoCompressionProps, AVVideoCompressionPropertiesKey,
94 | nil];
95 | AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
96 | outputSettings:writerOutputSettings
97 | sourceFormatHint:(__bridge CMFormatDescriptionRef)[videoTrack.formatDescriptions lastObject]];
98 | [writerInput setExpectsMediaDataInRealTime:NO];
99 |
100 | // Initialize an input adaptor so that we can append PixelBuffer
101 | AVAssetWriterInputPixelBufferAdaptor *pixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil];
102 |
103 | [writer addInput:writerInput];
104 |
105 | [writer startWriting];
106 | [writer startSessionAtSourceTime:videoTrack.timeRange.start];
107 |
108 | NSInteger counter = 0;
109 | size_t countOfFrames = 0;
110 | size_t totalCountOfArray = 40;
111 | size_t arrayIncreasment = 40;
112 | CMSampleBufferRef *sampleBufferRefs = (CMSampleBufferRef *) malloc(totalCountOfArray * sizeof(CMSampleBufferRef *));
113 | memset(sampleBufferRefs, 0, sizeof(CMSampleBufferRef *) * totalCountOfArray);
114 | for (NSInteger i = tracks.count -1; i <= tracks.count; i --) {
115 | if (*(cancle)) {
116 | [writer cancelWriting];
117 | free(sampleBufferRefs);
118 | return nil;
119 | }
120 | AVAssetReader *reader = nil;
121 |
122 | countOfFrames = 0;
123 | AVAssetReaderOutput *readerOutput = nil;
124 | if (videoComposition) {
125 | readerOutput = [AVAssetReaderVideoCompositionOutput assetReaderVideoCompositionOutputWithVideoTracks:@[tracks[i]] videoSettings:totalReaderOutputSettings];
126 | ((AVAssetReaderVideoCompositionOutput *)readerOutput).videoComposition = videoComposition;
127 | } else {
128 | readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:tracks[i] outputSettings:totalReaderOutputSettings];
129 | }
130 |
131 | reader = [[AVAssetReader alloc] initWithAsset:assets[i] error:&error];
132 | if([reader canAddOutput:readerOutput]){
133 | [reader addOutput:readerOutput];
134 | } else {
135 | break;
136 | }
137 | [reader startReading];
138 |
139 | CMSampleBufferRef sample;
140 | while((sample = [readerOutput copyNextSampleBuffer])) {
141 | CMTime presentationTime = CMSampleBufferGetPresentationTimeStamp(sample);
142 | if (CMTIME_COMPARE_INLINE(presentationTime, >=, [startTimeArray[i] CMTimeValue])) {
143 | if (countOfFrames + 1 > totalCountOfArray) {
144 | totalCountOfArray += arrayIncreasment;
145 | sampleBufferRefs = (CMSampleBufferRef *)realloc(sampleBufferRefs, totalCountOfArray);
146 | }
147 | *(sampleBufferRefs + countOfFrames) = sample;
148 | countOfFrames++;
149 | } else {
150 | if (sample != NULL) {
151 | CFRelease(sample);
152 | }
153 | }
154 | }
155 | [reader cancelReading];
156 | for(NSInteger j = 0; j < countOfFrames; j++) {
157 | // Get the presentation time for the frame
158 | if (counter > sampleTimes.count - 1) {
159 | break;
160 | }
161 | CMTime presentationTime = [sampleTimes[counter] CMTimeValue];
162 |
163 | // take the image/pixel buffer from tail end of the array
164 | CMSampleBufferRef bufferRef = *(sampleBufferRefs + countOfFrames - j - 1);
165 | CVPixelBufferRef imageBufferRef = CMSampleBufferGetImageBuffer(bufferRef);
166 |
167 | while (!writerInput.readyForMoreMediaData) {
168 | [NSThread sleepForTimeInterval:0.1];
169 | }
170 | [pixelBufferAdaptor appendPixelBuffer:imageBufferRef withPresentationTime:presentationTime];
171 | progressHandle(((CGFloat)counter/(CGFloat)sampleTimes.count));
172 | counter++;
173 | CFRelease(bufferRef);
174 | *(sampleBufferRefs + countOfFrames - j - 1) = NULL;
175 | }
176 | }
177 | free(sampleBufferRefs);
178 |
179 | [writer finishWriting];
180 | return [AVAsset assetWithURL:outputURL];
181 | }
182 | @end
183 |
--------------------------------------------------------------------------------
/VideoReverseDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 37B9980C1C287CE200710151 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B9980B1C287CE200710151 /* main.m */; };
11 | 37B9980F1C287CE200710151 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B9980E1C287CE200710151 /* AppDelegate.m */; };
12 | 37B998121C287CE200710151 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B998111C287CE200710151 /* ViewController.m */; };
13 | 37B998151C287CE200710151 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37B998131C287CE200710151 /* Main.storyboard */; };
14 | 37B998171C287CE200710151 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37B998161C287CE200710151 /* Assets.xcassets */; };
15 | 37B9981A1C287CE200710151 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37B998181C287CE200710151 /* LaunchScreen.storyboard */; };
16 | 37B998251C287CE200710151 /* VideoReverseDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B998241C287CE200710151 /* VideoReverseDemoTests.m */; };
17 | 37B998301C287CE200710151 /* VideoReverseDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B9982F1C287CE200710151 /* VideoReverseDemoUITests.m */; };
18 | 37B9983F1C287D0300710151 /* HKMediaOperationTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 37B9983E1C287D0300710151 /* HKMediaOperationTools.m */; };
19 | 37B998411C28851200710151 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37B998401C28851200710151 /* AVFoundation.framework */; };
20 | 37B998431C28881800710151 /* test.mp4 in Sources */ = {isa = PBXBuildFile; fileRef = 37B998421C2885C500710151 /* test.mp4 */; };
21 | 37B998441C28887600710151 /* test.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 37B998421C2885C500710151 /* test.mp4 */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXContainerItemProxy section */
25 | 37B998211C287CE200710151 /* PBXContainerItemProxy */ = {
26 | isa = PBXContainerItemProxy;
27 | containerPortal = 37B997FF1C287CE200710151 /* Project object */;
28 | proxyType = 1;
29 | remoteGlobalIDString = 37B998061C287CE200710151;
30 | remoteInfo = VideoReverseDemo;
31 | };
32 | 37B9982C1C287CE200710151 /* PBXContainerItemProxy */ = {
33 | isa = PBXContainerItemProxy;
34 | containerPortal = 37B997FF1C287CE200710151 /* Project object */;
35 | proxyType = 1;
36 | remoteGlobalIDString = 37B998061C287CE200710151;
37 | remoteInfo = VideoReverseDemo;
38 | };
39 | /* End PBXContainerItemProxy section */
40 |
41 | /* Begin PBXFileReference section */
42 | 37B998071C287CE200710151 /* VideoReverseDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VideoReverseDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
43 | 37B9980B1C287CE200710151 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
44 | 37B9980D1C287CE200710151 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
45 | 37B9980E1C287CE200710151 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
46 | 37B998101C287CE200710151 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
47 | 37B998111C287CE200710151 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
48 | 37B998141C287CE200710151 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
49 | 37B998161C287CE200710151 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
50 | 37B998191C287CE200710151 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
51 | 37B9981B1C287CE200710151 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
52 | 37B998201C287CE200710151 /* VideoReverseDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VideoReverseDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
53 | 37B998241C287CE200710151 /* VideoReverseDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoReverseDemoTests.m; sourceTree = ""; };
54 | 37B998261C287CE200710151 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
55 | 37B9982B1C287CE200710151 /* VideoReverseDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VideoReverseDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
56 | 37B9982F1C287CE200710151 /* VideoReverseDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoReverseDemoUITests.m; sourceTree = ""; };
57 | 37B998311C287CE200710151 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | 37B9983D1C287D0300710151 /* HKMediaOperationTools.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HKMediaOperationTools.h; sourceTree = ""; };
59 | 37B9983E1C287D0300710151 /* HKMediaOperationTools.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HKMediaOperationTools.m; sourceTree = ""; };
60 | 37B998401C28851200710151 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
61 | 37B998421C2885C500710151 /* test.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = test.mp4; sourceTree = ""; };
62 | /* End PBXFileReference section */
63 |
64 | /* Begin PBXFrameworksBuildPhase section */
65 | 37B998041C287CE200710151 /* Frameworks */ = {
66 | isa = PBXFrameworksBuildPhase;
67 | buildActionMask = 2147483647;
68 | files = (
69 | 37B998411C28851200710151 /* AVFoundation.framework in Frameworks */,
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | 37B9981D1C287CE200710151 /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | );
78 | runOnlyForDeploymentPostprocessing = 0;
79 | };
80 | 37B998281C287CE200710151 /* Frameworks */ = {
81 | isa = PBXFrameworksBuildPhase;
82 | buildActionMask = 2147483647;
83 | files = (
84 | );
85 | runOnlyForDeploymentPostprocessing = 0;
86 | };
87 | /* End PBXFrameworksBuildPhase section */
88 |
89 | /* Begin PBXGroup section */
90 | 37B997FE1C287CE200710151 = {
91 | isa = PBXGroup;
92 | children = (
93 | 37B998401C28851200710151 /* AVFoundation.framework */,
94 | 37B998091C287CE200710151 /* VideoReverseDemo */,
95 | 37B998231C287CE200710151 /* VideoReverseDemoTests */,
96 | 37B9982E1C287CE200710151 /* VideoReverseDemoUITests */,
97 | 37B998081C287CE200710151 /* Products */,
98 | );
99 | sourceTree = "";
100 | };
101 | 37B998081C287CE200710151 /* Products */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 37B998071C287CE200710151 /* VideoReverseDemo.app */,
105 | 37B998201C287CE200710151 /* VideoReverseDemoTests.xctest */,
106 | 37B9982B1C287CE200710151 /* VideoReverseDemoUITests.xctest */,
107 | );
108 | name = Products;
109 | sourceTree = "";
110 | };
111 | 37B998091C287CE200710151 /* VideoReverseDemo */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 37B9983D1C287D0300710151 /* HKMediaOperationTools.h */,
115 | 37B9983E1C287D0300710151 /* HKMediaOperationTools.m */,
116 | 37B9980D1C287CE200710151 /* AppDelegate.h */,
117 | 37B9980E1C287CE200710151 /* AppDelegate.m */,
118 | 37B998101C287CE200710151 /* ViewController.h */,
119 | 37B998111C287CE200710151 /* ViewController.m */,
120 | 37B998421C2885C500710151 /* test.mp4 */,
121 | 37B998131C287CE200710151 /* Main.storyboard */,
122 | 37B998161C287CE200710151 /* Assets.xcassets */,
123 | 37B998181C287CE200710151 /* LaunchScreen.storyboard */,
124 | 37B9981B1C287CE200710151 /* Info.plist */,
125 | 37B9980A1C287CE200710151 /* Supporting Files */,
126 | );
127 | path = VideoReverseDemo;
128 | sourceTree = "";
129 | };
130 | 37B9980A1C287CE200710151 /* Supporting Files */ = {
131 | isa = PBXGroup;
132 | children = (
133 | 37B9980B1C287CE200710151 /* main.m */,
134 | );
135 | name = "Supporting Files";
136 | sourceTree = "";
137 | };
138 | 37B998231C287CE200710151 /* VideoReverseDemoTests */ = {
139 | isa = PBXGroup;
140 | children = (
141 | 37B998241C287CE200710151 /* VideoReverseDemoTests.m */,
142 | 37B998261C287CE200710151 /* Info.plist */,
143 | );
144 | path = VideoReverseDemoTests;
145 | sourceTree = "";
146 | };
147 | 37B9982E1C287CE200710151 /* VideoReverseDemoUITests */ = {
148 | isa = PBXGroup;
149 | children = (
150 | 37B9982F1C287CE200710151 /* VideoReverseDemoUITests.m */,
151 | 37B998311C287CE200710151 /* Info.plist */,
152 | );
153 | path = VideoReverseDemoUITests;
154 | sourceTree = "";
155 | };
156 | /* End PBXGroup section */
157 |
158 | /* Begin PBXNativeTarget section */
159 | 37B998061C287CE200710151 /* VideoReverseDemo */ = {
160 | isa = PBXNativeTarget;
161 | buildConfigurationList = 37B998341C287CE200710151 /* Build configuration list for PBXNativeTarget "VideoReverseDemo" */;
162 | buildPhases = (
163 | 37B998031C287CE200710151 /* Sources */,
164 | 37B998041C287CE200710151 /* Frameworks */,
165 | 37B998051C287CE200710151 /* Resources */,
166 | );
167 | buildRules = (
168 | );
169 | dependencies = (
170 | );
171 | name = VideoReverseDemo;
172 | productName = VideoReverseDemo;
173 | productReference = 37B998071C287CE200710151 /* VideoReverseDemo.app */;
174 | productType = "com.apple.product-type.application";
175 | };
176 | 37B9981F1C287CE200710151 /* VideoReverseDemoTests */ = {
177 | isa = PBXNativeTarget;
178 | buildConfigurationList = 37B998371C287CE200710151 /* Build configuration list for PBXNativeTarget "VideoReverseDemoTests" */;
179 | buildPhases = (
180 | 37B9981C1C287CE200710151 /* Sources */,
181 | 37B9981D1C287CE200710151 /* Frameworks */,
182 | 37B9981E1C287CE200710151 /* Resources */,
183 | );
184 | buildRules = (
185 | );
186 | dependencies = (
187 | 37B998221C287CE200710151 /* PBXTargetDependency */,
188 | );
189 | name = VideoReverseDemoTests;
190 | productName = VideoReverseDemoTests;
191 | productReference = 37B998201C287CE200710151 /* VideoReverseDemoTests.xctest */;
192 | productType = "com.apple.product-type.bundle.unit-test";
193 | };
194 | 37B9982A1C287CE200710151 /* VideoReverseDemoUITests */ = {
195 | isa = PBXNativeTarget;
196 | buildConfigurationList = 37B9983A1C287CE200710151 /* Build configuration list for PBXNativeTarget "VideoReverseDemoUITests" */;
197 | buildPhases = (
198 | 37B998271C287CE200710151 /* Sources */,
199 | 37B998281C287CE200710151 /* Frameworks */,
200 | 37B998291C287CE200710151 /* Resources */,
201 | );
202 | buildRules = (
203 | );
204 | dependencies = (
205 | 37B9982D1C287CE200710151 /* PBXTargetDependency */,
206 | );
207 | name = VideoReverseDemoUITests;
208 | productName = VideoReverseDemoUITests;
209 | productReference = 37B9982B1C287CE200710151 /* VideoReverseDemoUITests.xctest */;
210 | productType = "com.apple.product-type.bundle.ui-testing";
211 | };
212 | /* End PBXNativeTarget section */
213 |
214 | /* Begin PBXProject section */
215 | 37B997FF1C287CE200710151 /* Project object */ = {
216 | isa = PBXProject;
217 | attributes = {
218 | LastUpgradeCheck = 0710;
219 | ORGANIZATIONNAME = HuangKai;
220 | TargetAttributes = {
221 | 37B998061C287CE200710151 = {
222 | CreatedOnToolsVersion = 7.1.1;
223 | };
224 | 37B9981F1C287CE200710151 = {
225 | CreatedOnToolsVersion = 7.1.1;
226 | TestTargetID = 37B998061C287CE200710151;
227 | };
228 | 37B9982A1C287CE200710151 = {
229 | CreatedOnToolsVersion = 7.1.1;
230 | TestTargetID = 37B998061C287CE200710151;
231 | };
232 | };
233 | };
234 | buildConfigurationList = 37B998021C287CE200710151 /* Build configuration list for PBXProject "VideoReverseDemo" */;
235 | compatibilityVersion = "Xcode 3.2";
236 | developmentRegion = English;
237 | hasScannedForEncodings = 0;
238 | knownRegions = (
239 | en,
240 | Base,
241 | );
242 | mainGroup = 37B997FE1C287CE200710151;
243 | productRefGroup = 37B998081C287CE200710151 /* Products */;
244 | projectDirPath = "";
245 | projectRoot = "";
246 | targets = (
247 | 37B998061C287CE200710151 /* VideoReverseDemo */,
248 | 37B9981F1C287CE200710151 /* VideoReverseDemoTests */,
249 | 37B9982A1C287CE200710151 /* VideoReverseDemoUITests */,
250 | );
251 | };
252 | /* End PBXProject section */
253 |
254 | /* Begin PBXResourcesBuildPhase section */
255 | 37B998051C287CE200710151 /* Resources */ = {
256 | isa = PBXResourcesBuildPhase;
257 | buildActionMask = 2147483647;
258 | files = (
259 | 37B998441C28887600710151 /* test.mp4 in Resources */,
260 | 37B9981A1C287CE200710151 /* LaunchScreen.storyboard in Resources */,
261 | 37B998171C287CE200710151 /* Assets.xcassets in Resources */,
262 | 37B998151C287CE200710151 /* Main.storyboard in Resources */,
263 | );
264 | runOnlyForDeploymentPostprocessing = 0;
265 | };
266 | 37B9981E1C287CE200710151 /* Resources */ = {
267 | isa = PBXResourcesBuildPhase;
268 | buildActionMask = 2147483647;
269 | files = (
270 | );
271 | runOnlyForDeploymentPostprocessing = 0;
272 | };
273 | 37B998291C287CE200710151 /* Resources */ = {
274 | isa = PBXResourcesBuildPhase;
275 | buildActionMask = 2147483647;
276 | files = (
277 | );
278 | runOnlyForDeploymentPostprocessing = 0;
279 | };
280 | /* End PBXResourcesBuildPhase section */
281 |
282 | /* Begin PBXSourcesBuildPhase section */
283 | 37B998031C287CE200710151 /* Sources */ = {
284 | isa = PBXSourcesBuildPhase;
285 | buildActionMask = 2147483647;
286 | files = (
287 | 37B998431C28881800710151 /* test.mp4 in Sources */,
288 | 37B998121C287CE200710151 /* ViewController.m in Sources */,
289 | 37B9980F1C287CE200710151 /* AppDelegate.m in Sources */,
290 | 37B9980C1C287CE200710151 /* main.m in Sources */,
291 | 37B9983F1C287D0300710151 /* HKMediaOperationTools.m in Sources */,
292 | );
293 | runOnlyForDeploymentPostprocessing = 0;
294 | };
295 | 37B9981C1C287CE200710151 /* Sources */ = {
296 | isa = PBXSourcesBuildPhase;
297 | buildActionMask = 2147483647;
298 | files = (
299 | 37B998251C287CE200710151 /* VideoReverseDemoTests.m in Sources */,
300 | );
301 | runOnlyForDeploymentPostprocessing = 0;
302 | };
303 | 37B998271C287CE200710151 /* Sources */ = {
304 | isa = PBXSourcesBuildPhase;
305 | buildActionMask = 2147483647;
306 | files = (
307 | 37B998301C287CE200710151 /* VideoReverseDemoUITests.m in Sources */,
308 | );
309 | runOnlyForDeploymentPostprocessing = 0;
310 | };
311 | /* End PBXSourcesBuildPhase section */
312 |
313 | /* Begin PBXTargetDependency section */
314 | 37B998221C287CE200710151 /* PBXTargetDependency */ = {
315 | isa = PBXTargetDependency;
316 | target = 37B998061C287CE200710151 /* VideoReverseDemo */;
317 | targetProxy = 37B998211C287CE200710151 /* PBXContainerItemProxy */;
318 | };
319 | 37B9982D1C287CE200710151 /* PBXTargetDependency */ = {
320 | isa = PBXTargetDependency;
321 | target = 37B998061C287CE200710151 /* VideoReverseDemo */;
322 | targetProxy = 37B9982C1C287CE200710151 /* PBXContainerItemProxy */;
323 | };
324 | /* End PBXTargetDependency section */
325 |
326 | /* Begin PBXVariantGroup section */
327 | 37B998131C287CE200710151 /* Main.storyboard */ = {
328 | isa = PBXVariantGroup;
329 | children = (
330 | 37B998141C287CE200710151 /* Base */,
331 | );
332 | name = Main.storyboard;
333 | sourceTree = "";
334 | };
335 | 37B998181C287CE200710151 /* LaunchScreen.storyboard */ = {
336 | isa = PBXVariantGroup;
337 | children = (
338 | 37B998191C287CE200710151 /* Base */,
339 | );
340 | name = LaunchScreen.storyboard;
341 | sourceTree = "";
342 | };
343 | /* End PBXVariantGroup section */
344 |
345 | /* Begin XCBuildConfiguration section */
346 | 37B998321C287CE200710151 /* Debug */ = {
347 | isa = XCBuildConfiguration;
348 | buildSettings = {
349 | ALWAYS_SEARCH_USER_PATHS = NO;
350 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
351 | CLANG_CXX_LIBRARY = "libc++";
352 | CLANG_ENABLE_MODULES = YES;
353 | CLANG_ENABLE_OBJC_ARC = YES;
354 | CLANG_WARN_BOOL_CONVERSION = YES;
355 | CLANG_WARN_CONSTANT_CONVERSION = YES;
356 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
357 | CLANG_WARN_EMPTY_BODY = YES;
358 | CLANG_WARN_ENUM_CONVERSION = YES;
359 | CLANG_WARN_INT_CONVERSION = YES;
360 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
361 | CLANG_WARN_UNREACHABLE_CODE = YES;
362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
364 | COPY_PHASE_STRIP = NO;
365 | DEBUG_INFORMATION_FORMAT = dwarf;
366 | ENABLE_STRICT_OBJC_MSGSEND = YES;
367 | ENABLE_TESTABILITY = YES;
368 | GCC_C_LANGUAGE_STANDARD = gnu99;
369 | GCC_DYNAMIC_NO_PIC = NO;
370 | GCC_NO_COMMON_BLOCKS = YES;
371 | GCC_OPTIMIZATION_LEVEL = 0;
372 | GCC_PREPROCESSOR_DEFINITIONS = (
373 | "DEBUG=1",
374 | "$(inherited)",
375 | );
376 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
377 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
378 | GCC_WARN_UNDECLARED_SELECTOR = YES;
379 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
380 | GCC_WARN_UNUSED_FUNCTION = YES;
381 | GCC_WARN_UNUSED_VARIABLE = YES;
382 | IPHONEOS_DEPLOYMENT_TARGET = 9.1;
383 | MTL_ENABLE_DEBUG_INFO = YES;
384 | ONLY_ACTIVE_ARCH = YES;
385 | SDKROOT = iphoneos;
386 | };
387 | name = Debug;
388 | };
389 | 37B998331C287CE200710151 /* Release */ = {
390 | isa = XCBuildConfiguration;
391 | buildSettings = {
392 | ALWAYS_SEARCH_USER_PATHS = NO;
393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
394 | CLANG_CXX_LIBRARY = "libc++";
395 | CLANG_ENABLE_MODULES = YES;
396 | CLANG_ENABLE_OBJC_ARC = YES;
397 | CLANG_WARN_BOOL_CONVERSION = YES;
398 | CLANG_WARN_CONSTANT_CONVERSION = YES;
399 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
400 | CLANG_WARN_EMPTY_BODY = YES;
401 | CLANG_WARN_ENUM_CONVERSION = YES;
402 | CLANG_WARN_INT_CONVERSION = YES;
403 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
404 | CLANG_WARN_UNREACHABLE_CODE = YES;
405 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
406 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
407 | COPY_PHASE_STRIP = NO;
408 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
409 | ENABLE_NS_ASSERTIONS = NO;
410 | ENABLE_STRICT_OBJC_MSGSEND = YES;
411 | GCC_C_LANGUAGE_STANDARD = gnu99;
412 | GCC_NO_COMMON_BLOCKS = YES;
413 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
414 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
415 | GCC_WARN_UNDECLARED_SELECTOR = YES;
416 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
417 | GCC_WARN_UNUSED_FUNCTION = YES;
418 | GCC_WARN_UNUSED_VARIABLE = YES;
419 | IPHONEOS_DEPLOYMENT_TARGET = 9.1;
420 | MTL_ENABLE_DEBUG_INFO = NO;
421 | SDKROOT = iphoneos;
422 | VALIDATE_PRODUCT = YES;
423 | };
424 | name = Release;
425 | };
426 | 37B998351C287CE200710151 /* Debug */ = {
427 | isa = XCBuildConfiguration;
428 | buildSettings = {
429 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
430 | INFOPLIST_FILE = VideoReverseDemo/Info.plist;
431 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
432 | PRODUCT_BUNDLE_IDENTIFIER = com.KayWong.VideoReverseDemo;
433 | PRODUCT_NAME = "$(TARGET_NAME)";
434 | };
435 | name = Debug;
436 | };
437 | 37B998361C287CE200710151 /* Release */ = {
438 | isa = XCBuildConfiguration;
439 | buildSettings = {
440 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
441 | INFOPLIST_FILE = VideoReverseDemo/Info.plist;
442 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
443 | PRODUCT_BUNDLE_IDENTIFIER = com.KayWong.VideoReverseDemo;
444 | PRODUCT_NAME = "$(TARGET_NAME)";
445 | };
446 | name = Release;
447 | };
448 | 37B998381C287CE200710151 /* Debug */ = {
449 | isa = XCBuildConfiguration;
450 | buildSettings = {
451 | BUNDLE_LOADER = "$(TEST_HOST)";
452 | INFOPLIST_FILE = VideoReverseDemoTests/Info.plist;
453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
454 | PRODUCT_BUNDLE_IDENTIFIER = com.KayWong.VideoReverseDemoTests;
455 | PRODUCT_NAME = "$(TARGET_NAME)";
456 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoReverseDemo.app/VideoReverseDemo";
457 | };
458 | name = Debug;
459 | };
460 | 37B998391C287CE200710151 /* Release */ = {
461 | isa = XCBuildConfiguration;
462 | buildSettings = {
463 | BUNDLE_LOADER = "$(TEST_HOST)";
464 | INFOPLIST_FILE = VideoReverseDemoTests/Info.plist;
465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
466 | PRODUCT_BUNDLE_IDENTIFIER = com.KayWong.VideoReverseDemoTests;
467 | PRODUCT_NAME = "$(TARGET_NAME)";
468 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoReverseDemo.app/VideoReverseDemo";
469 | };
470 | name = Release;
471 | };
472 | 37B9983B1C287CE200710151 /* Debug */ = {
473 | isa = XCBuildConfiguration;
474 | buildSettings = {
475 | INFOPLIST_FILE = VideoReverseDemoUITests/Info.plist;
476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
477 | PRODUCT_BUNDLE_IDENTIFIER = com.KayWong.VideoReverseDemoUITests;
478 | PRODUCT_NAME = "$(TARGET_NAME)";
479 | TEST_TARGET_NAME = VideoReverseDemo;
480 | USES_XCTRUNNER = YES;
481 | };
482 | name = Debug;
483 | };
484 | 37B9983C1C287CE200710151 /* Release */ = {
485 | isa = XCBuildConfiguration;
486 | buildSettings = {
487 | INFOPLIST_FILE = VideoReverseDemoUITests/Info.plist;
488 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
489 | PRODUCT_BUNDLE_IDENTIFIER = com.KayWong.VideoReverseDemoUITests;
490 | PRODUCT_NAME = "$(TARGET_NAME)";
491 | TEST_TARGET_NAME = VideoReverseDemo;
492 | USES_XCTRUNNER = YES;
493 | };
494 | name = Release;
495 | };
496 | /* End XCBuildConfiguration section */
497 |
498 | /* Begin XCConfigurationList section */
499 | 37B998021C287CE200710151 /* Build configuration list for PBXProject "VideoReverseDemo" */ = {
500 | isa = XCConfigurationList;
501 | buildConfigurations = (
502 | 37B998321C287CE200710151 /* Debug */,
503 | 37B998331C287CE200710151 /* Release */,
504 | );
505 | defaultConfigurationIsVisible = 0;
506 | defaultConfigurationName = Release;
507 | };
508 | 37B998341C287CE200710151 /* Build configuration list for PBXNativeTarget "VideoReverseDemo" */ = {
509 | isa = XCConfigurationList;
510 | buildConfigurations = (
511 | 37B998351C287CE200710151 /* Debug */,
512 | 37B998361C287CE200710151 /* Release */,
513 | );
514 | defaultConfigurationIsVisible = 0;
515 | };
516 | 37B998371C287CE200710151 /* Build configuration list for PBXNativeTarget "VideoReverseDemoTests" */ = {
517 | isa = XCConfigurationList;
518 | buildConfigurations = (
519 | 37B998381C287CE200710151 /* Debug */,
520 | 37B998391C287CE200710151 /* Release */,
521 | );
522 | defaultConfigurationIsVisible = 0;
523 | };
524 | 37B9983A1C287CE200710151 /* Build configuration list for PBXNativeTarget "VideoReverseDemoUITests" */ = {
525 | isa = XCConfigurationList;
526 | buildConfigurations = (
527 | 37B9983B1C287CE200710151 /* Debug */,
528 | 37B9983C1C287CE200710151 /* Release */,
529 | );
530 | defaultConfigurationIsVisible = 0;
531 | };
532 | /* End XCConfigurationList section */
533 | };
534 | rootObject = 37B997FF1C287CE200710151 /* Project object */;
535 | }
536 |
--------------------------------------------------------------------------------