FffmpegPlugin.m 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #import "FffmpegPlugin.h"
  2. #import <AVFoundation/AVFoundation.h>
  3. @implementation FffmpegPlugin
  4. + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  5. FlutterMethodChannel* channel = [FlutterMethodChannel
  6. methodChannelWithName:@"fffmpeg"
  7. binaryMessenger:[registrar messenger]];
  8. FffmpegPlugin* instance = [[FffmpegPlugin alloc] init];
  9. [registrar addMethodCallDelegate:instance channel:channel];
  10. }
  11. - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  12. if ([@"getPlatformVersion" isEqualToString:call.method]) {
  13. result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
  14. }
  15. if([@"exeCommand" isEqualToString:call.method]){
  16. NSDictionary* arguments = call.arguments[@"arguments"];
  17. NSString *input=arguments[@"inputPath"];
  18. NSString *output=arguments[@"outputPath"];
  19. NSLog(@"=======input=======",input);
  20. NSLog(@"========output======",output);
  21. [self addWaterPicWithVideoPath:input outPath:output result:result];
  22. // int rc = [MobileFFmpeg executeWithArguments:arguments];
  23. //
  24. // NSLog(@"FFmpeg exited with rc: %d\n", rc);
  25. //
  26. } else {
  27. result(FlutterMethodNotImplemented);
  28. }
  29. }
  30. - (void)addWaterPicWithVideoPath:(NSString*)path outPath:(NSString*)outPath result:(FlutterResult)result
  31. {
  32. //1 创建AVAsset实例
  33. AVURLAsset* videoAsset = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:path]];
  34. AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
  35. //3 视频通道
  36. AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
  37. preferredTrackID:kCMPersistentTrackID_Invalid];
  38. [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
  39. ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject]
  40. atTime:kCMTimeZero error:nil];
  41. //2 音频通道
  42. AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
  43. preferredTrackID:kCMPersistentTrackID_Invalid];
  44. [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
  45. ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeAudio] firstObject]
  46. atTime:kCMTimeZero error:nil];
  47. //3.1 AVMutableVideoCompositionInstruction 视频轨道中的一个视频,可以缩放、旋转等
  48. AVMutableVideoCompositionInstruction *mainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
  49. mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
  50. // 3.2 AVMutableVideoCompositionLayerInstruction 一个视频轨道,包含了这个轨道上的所有视频素材
  51. AVMutableVideoCompositionLayerInstruction *videolayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
  52. [videolayerInstruction setOpacity:0.0 atTime:videoAsset.duration];
  53. // 3.3 - Add instructions
  54. mainInstruction.layerInstructions = [NSArray arrayWithObjects:videolayerInstruction,nil];
  55. //AVMutableVideoComposition:管理所有视频轨道,水印添加就在这上面
  56. AVMutableVideoComposition *mainCompositionInst = [AVMutableVideoComposition videoComposition];
  57. CGAffineTransform translateToCenter;
  58. CGAffineTransform mixedTransform;
  59. translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0);
  60. mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2);
  61. mainCompositionInst.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height);
  62. AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
  63. CGSize naturalSize = videoAssetTrack.naturalSize;
  64. float renderWidth, renderHeight;
  65. renderWidth = naturalSize.width;
  66. renderHeight = naturalSize.height;
  67. mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight);
  68. mainCompositionInst.instructions = [NSArray arrayWithObject:mainInstruction];
  69. mainCompositionInst.frameDuration = CMTimeMake(1, 30);
  70. [self applyVideoEffectsToComposition:mainCompositionInst size:naturalSize];
  71. // // 4 - 输出路径
  72. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  73. NSString *documentsDirectory = [paths objectAtIndex:0];
  74. NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:
  75. [NSString stringWithFormat:@"FinalVideo-%d.mp4",arc4random() % 1000]];
  76. NSURL* videoUrl = [NSURL fileURLWithPath:outPath];
  77. // 5 - 视频文件输出
  78. AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
  79. presetName:AVAssetExportPresetHighestQuality];
  80. exporter.outputURL = videoUrl;
  81. exporter.outputFileType = AVFileTypeMPEG4;
  82. exporter.shouldOptimizeForNetworkUse = YES;
  83. exporter.videoComposition = mainCompositionInst;
  84. [exporter exportAsynchronouslyWithCompletionHandler:^{
  85. dispatch_async(dispatch_get_main_queue(), ^{
  86. if( exporter.status == AVAssetExportSessionStatusCompleted ){
  87. UISaveVideoAtPathToSavedPhotosAlbum(myPathDocs, nil, nil, nil);
  88. result(outPath);
  89. }else if( exporter.status == AVAssetExportSessionStatusFailed )
  90. {
  91. result(@"notok");
  92. NSLog(@"failed");
  93. }
  94. });
  95. }];
  96. }
  97. /**
  98. 设置水印及其对应视频的位置
  99. @param composition 视频的结构
  100. @param size 视频的尺寸
  101. */
  102. - (void)applyVideoEffectsToComposition:(AVMutableVideoComposition *)composition size:(CGSize)size
  103. {
  104. // 文字
  105. // CATextLayer *subtitle1Text = [[CATextLayer alloc] init];
  106. // // [subtitle1Text setFont:@"Helvetica-Bold"];
  107. // [subtitle1Text setFontSize:36];
  108. // [subtitle1Text setFrame:CGRectMake(10, size.height-10-100, size.width, 100)];
  109. // [subtitle1Text setString:@"ZHIMABAOBAO"];
  110. // // [subtitle1Text setAlignmentMode:kCAAlignmentCenter];
  111. // [subtitle1Text setForegroundColor:[[UIColor whiteColor] CGColor]];
  112. //图片
  113. CALayer*picLayer = [CALayer layer];
  114. picLayer.contents = (id)[UIImage imageNamed:@"watermarklogo"].CGImage;
  115. picLayer.frame = CGRectMake(20, size.height-120, 100, 102);
  116. // 2 - The usual overlay
  117. CALayer *overlayLayer = [CALayer layer];
  118. [overlayLayer addSublayer:picLayer];
  119. overlayLayer.frame = CGRectMake(0, 0, size.width, size.height);
  120. [overlayLayer setMasksToBounds:YES];
  121. CALayer *parentLayer = [CALayer layer];
  122. CALayer *videoLayer = [CALayer layer];
  123. parentLayer.frame = CGRectMake(0, 0, size.width, size.height);
  124. videoLayer.frame = CGRectMake(0, 0, size.width, size.height);
  125. [parentLayer addSublayer:videoLayer];
  126. [parentLayer addSublayer:overlayLayer];
  127. composition.animationTool = [AVVideoCompositionCoreAnimationTool
  128. videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
  129. }
  130. @end