fffmpeg.dart 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'dart:typed_data';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'dart:ui' as UI;
  7. import 'dart:ui';
  8. enum WaterMarkPosition{
  9. LeftTop,
  10. RightTop,
  11. LeftBottom,
  12. RightBottom
  13. }
  14. class Fffmpeg {
  15. static const MethodChannel _channel =
  16. const MethodChannel('fffmpeg');
  17. static Future<String> get platformVersion async {
  18. final String version = await _channel.invokeMethod('getPlatformVersion');
  19. return version;
  20. }
  21. //android 传命令 ios传路径
  22. static Future<String> exeCommand(String command) async {
  23. String version="";
  24. if(Platform.isIOS){
  25. var split = command.split(",");
  26. version = await _channel.invokeMethod('exeCommand',{'arguments': {"inputPath":split[0],"outputPath":split[1]}});
  27. }else if(Platform.isAndroid){
  28. version = await _channel.invokeMethod('exeCommand',{'arguments': parseArguments(command)});
  29. }
  30. return version;
  31. }
  32. static Future<String> addWatermarkToVedio(String inputPath,String outputPath,String watermarkPath,WaterMarkPosition waterMarkPosition)async{
  33. print("input=="+inputPath);
  34. print("output=="+outputPath);
  35. String s="";
  36. String position="";
  37. if(Platform.isIOS){
  38. if(waterMarkPosition==WaterMarkPosition.LeftTop){
  39. position="LeftTop";
  40. }else if(waterMarkPosition==WaterMarkPosition.RightTop){
  41. position="RightTop";
  42. }else if(waterMarkPosition==WaterMarkPosition.RightBottom){
  43. position="RightBottom";
  44. }else if(waterMarkPosition==WaterMarkPosition.LeftBottom){
  45. position="LeftBottom";
  46. }
  47. s = await _channel.invokeMethod('addwaterMark',{'arguments': {"inputPath":inputPath,"outputPath":outputPath,"watermarkPath":watermarkPath,"position":position}});
  48. return s;
  49. }else if(Platform.isAndroid){
  50. s= await executeWithArguments(getCommand(inputPath, outputPath, watermarkPath, waterMarkPosition));
  51. return s;
  52. }
  53. }
  54. static Future<String> executeWithArguments(List<String> arguments) async {
  55. if(Platform.isIOS){
  56. return "Platform.isIOS!!error";
  57. }
  58. try {
  59. String version = await _channel
  60. .invokeMethod('exeCommand', {'arguments': arguments});
  61. return version;
  62. } on PlatformException catch (e) {
  63. print("Plugin error: ${e.message}");
  64. return "error";
  65. }
  66. }
  67. //解析输入的命令
  68. static List<String> parseArguments(String command) {
  69. List<String> argumentList = new List();
  70. StringBuffer currentArgument = new StringBuffer();
  71. bool singleQuoteStarted = false;
  72. bool doubleQuoteStarted = false;
  73. for (int i = 0; i < command.length; i++) {
  74. var previousChar;
  75. if (i > 0) {
  76. previousChar = command.codeUnitAt(i - 1);
  77. } else {
  78. previousChar = null;
  79. }
  80. var currentChar = command.codeUnitAt(i);
  81. if (currentChar == ' '.codeUnitAt(0)) {
  82. if (singleQuoteStarted || doubleQuoteStarted) {
  83. currentArgument.write(String.fromCharCode(currentChar));
  84. } else if (currentArgument.length > 0) {
  85. argumentList.add(currentArgument.toString());
  86. currentArgument = new StringBuffer();
  87. }
  88. } else if (currentChar == '\''.codeUnitAt(0) &&
  89. (previousChar == null || previousChar != '\\'.codeUnitAt(0))) {
  90. if (singleQuoteStarted) {
  91. singleQuoteStarted = false;
  92. } else if (doubleQuoteStarted) {
  93. currentArgument.write(String.fromCharCode(currentChar));
  94. } else {
  95. singleQuoteStarted = true;
  96. }
  97. } else if (currentChar == '\"'.codeUnitAt(0) &&
  98. (previousChar == null || previousChar != '\\'.codeUnitAt(0))) {
  99. if (doubleQuoteStarted) {
  100. doubleQuoteStarted = false;
  101. } else if (singleQuoteStarted) {
  102. currentArgument.write(String.fromCharCode(currentChar));
  103. } else {
  104. doubleQuoteStarted = true;
  105. }
  106. } else {
  107. currentArgument.write(String.fromCharCode(currentChar));
  108. }
  109. }
  110. if (currentArgument.length > 0) {
  111. argumentList.add(currentArgument.toString());
  112. }
  113. return argumentList;
  114. }
  115. static List<String> getCommand(String inputPath,String outputPath,String imagesPath,WaterMarkPosition waterMarkPosition){
  116. List<String> commands=List<String>(26);
  117. if(waterMarkPosition==WaterMarkPosition.LeftTop){
  118. commands[5] = "overlay=10:10";
  119. }else if(waterMarkPosition==WaterMarkPosition.RightTop){
  120. commands[5] = "overlay= main_w-overlay_w:0";
  121. }else if(waterMarkPosition==WaterMarkPosition.RightBottom){
  122. commands[5] = "overlay= main_w-overlay_w:main_h-overlay_h";
  123. }else if(waterMarkPosition==WaterMarkPosition.LeftBottom){
  124. commands[5] = "overlay=0: main_h-overlay_h";
  125. }
  126. commands[0] = "-i";
  127. commands[1] = inputPath;
  128. commands[2] = "-i";
  129. commands[3] = imagesPath;
  130. commands[4] = "-filter_complex";
  131. commands[6] = "-y";
  132. commands[7] = "-strict";
  133. commands[8] = "-2";
  134. commands[9] = "-vcodec";
  135. commands[10] = "libx264";
  136. commands[11] = "-preset";
  137. commands[12] = "ultrafast";
  138. //-crf 用于指定输出视频的质量,取值范围是0-51,默认值为23,数字越小输出视频的质量越高。
  139. // 这个选项会直接影响到输出视频的码率。一般来说,压制480p我会用20左右,压制720p我会用16-18
  140. commands[13] = "-crf";
  141. commands[14] = "29";
  142. commands[15] = "-threads";
  143. commands[16] = "2";
  144. commands[17] = "-acodec";
  145. commands[18] = "aac";
  146. commands[19] = "-ar";
  147. commands[20] = "44100";
  148. commands[21] = "-ac";
  149. commands[22] = "2";
  150. commands[23] = "-b:a";
  151. commands[24] = "32k";
  152. //下面两行用于设置视频大小
  153. // commands[23] = "-s";
  154. // commands[24] = "480x480";
  155. commands[25] = outputPath;
  156. return commands;
  157. }
  158. }