[FFmpeg 전투] Flutter 오디오 및 비디오 자르기

저자 : JianLee 링크 : https://www.jianshu.com/p/868c8536a9b2

flutter_ffmpeg란 무엇인가요?

ffmpeg는 명령줄을 통해 오디오 및 비디오를 처리하는 오디오 및 비디오 처리 라이브러리입니다. MobileFFmpeg는
모바일 측에서 ffmpeg의 구현입니다. flutter_ffmpeg는 MobileFFmpeg의 캡슐화입니다. 예, ffmpeg를 사용하여 flutter에서 오디오 및 비디오를 쉽게 처리할 수 있습니다. .처리를 위해. flutter_ffmpeg는 FFmpeg와 FFprobe 두 부분으로 구성됩니다. FFmpeg는 오디오 및 비디오 처리를 담당하고 FFprobe는 주로 오디오 및 비디오 미디어 정보 쿼리를 담당합니다.

flutter_ffmpeg 주소

flutter_ffmpeg 사용
1단계: 설치
// 在pubspec.yaml的dependencies下添加:
  flutter_ffmpeg: ^0.3.1
2단계: 구성

안드로이드 프로젝트의 구성

// 在工程目录下的 /android/build.gradle下添加
ext {
    
    
    flutterFFmpegPackage  = "full-lts" 
}

참고: 위 구성의 "full-lts"는 flutter__ffmpeg의 각 릴리스 버전에 대한 등록입니다. 자세한 내용은 공식 문서를 확인하세요. 한 가지 설명할 만한 점은 flutter_ffmpeg에는 두 개의 릴리스 패키지가 있는데 하나는 Main Release이고 다른 하나는 LTS Release 패키지이며, 이들이 지원하는 Android API 레벨/iOS SDK와 하드웨어 아키텍처가 다르다는 것입니다. 더 광범위하게 지원되고 LTS 지원이 MAIN보다 크므로 LTS 버전을 사용하는 것이 좋습니다.
여기서 문제가 발생했습니다. full-lts 인코딩 형식을 사용할 때 서버에 업로드된 비디오를 재생할 수 없습니다.
그래서 내가 여기서 사용하는 것은 다음과 같습니다.

ext {
    
    
    flutterFFmpegPackage = "full-gpl-lts"
}

또한 flutter_ffmpeg를 사용할 때 비슷한 문제가 발생했습니다.

Caused by: java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/app.domain.name-Lq_GxP1CfMArHTpLoII-YA==/base.apk"],nativeLibraryDirectories=[/data/app/app.domain.name-Lq_GxP1CfMArHTpLoII-YA==/lib/arm64, /data/app/app.domain.name-Lq_GxP1CfMArHTpLoII-YA==/base.apk!/lib/arm64-v8a, /system/lib64, /system/vendor/lib64]]] couldn't find "libmobileffmpeg_abidetect.so"
        at java.lang.Runtime.loadLibrary0(Runtime.java:1011)
        at java.lang.System.loadLibrary(System.java:1657)
        at com.arthenica.mobileffmpeg.AbiDetect.<clinit>(Unknown Source:13)
        at com.arthenica.mobileffmpeg.AbiDetect.getNativeAbi(Native Method)
        at com.arthenica.mobileffmpeg.Config.<clinit>(Unknown Source:96)
        at com.arthenica.mobileffmpeg.Config.nativeFFmpegExecute(Native Method)
        at com.arthenica.mobileffmpeg.b.a(Unknown Source:0)
        at d.b.a.a.a.a(Unknown Source:31)
        at d.b.a.a.a.doInBackground(Unknown Source:2)
        at android.os.AsyncTask$2.call(AsyncTask.java:333)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636) 
        at java.lang.Thread.run(Thread.java:764) 

청소로 해결 가능해요

flutter clean
flutter build [artificat]

잘린 동영상이 기본적으로 x264를 사용하도록 하려면 gpl을 사용하세요.(그렇지 않으면 업로드된 동영상을 재생할 수 없는 문제가 발생합니다.) 자세한 내용은 flutter_ffmpeg 캡슐화 인코딩을 참조하세요.

img

이미지.png

3단계: 사용

flutter_ffmpge는 flutter에서 ffmpeg를 구현한 것입니다. ffmpeg는 명령줄을 통해 오디오 및 비디오를 편집하는 도구이므로 flutter_ffmpeg를 사용하면 자연스럽게 몇 가지 명령을 실행하여 기능을 구현하게 됩니다. 구체적인 사용 방법은 ffluter_ffmpegffmpeg 공식 문서를 직접 읽어보거나 좀 더 이해하기 쉬운 Ruan Yifeng 문서를 살펴보세요 . 다음으로 flutter_ffmpeg가 무엇을 할 수 있는지 주로 살펴보겠습니다.

flutter_ffmpge는 무엇을 할 수 있나요?

영상정보 보기

오디오 및 비디오 처리를 할 때 먼저 처리가 성공적인지, 효과가 좋은지 여부를 확실히 알고 싶습니다. 그런 다음 처리된 비디오 전후의 매개변수만 비교할 수 있으므로 첫 번째 단계에서 질문합니다. 자세한 정보를 제공하는 이 비디오 처리 라이브러리 오디오 및 비디오 정보인 flutter_ffmpeg의 FFProde는 이를 매우 효과적으로 달성할 수 있으며 사용이 매우 편리합니다.

final FlutterFFprobe _flutterFFprobe = new FlutterFFprobe();

_flutterFFprobe.getMediaInformation("<file path or uri>").then((info) {
    
    
    print("Media Information");

    print("Path: ${
      
      info.getMediaProperties()['filename']}");
    print("Format: ${
      
      info.getMediaProperties()['format_name']}");
    print("Duration: ${
      
      info.getMediaProperties()['duration']}");
    print("Start time: ${
      
      info.getMediaProperties()['start_time']}");
    print("Bitrate: ${
      
      info.getMediaProperties()['bit_rate']}");
    Map<dynamic, dynamic> tags = info.getMediaProperties()['tags'];
    if (tags != null) {
    
    
        tags.forEach((key, value) {
    
    
            print("Tag: " + key + ":" + value + "\n");
        });
    }

    if (info.getStreams() != null) {
    
    
        List<StreamInformation> streams = info.getStreams();

        if (streams.length > 0) {
    
    
            for (var stream in streams) {
    
    
                print("Stream id: ${
      
      stream.getAllProperties()['index']}");
                print("Stream type: ${
      
      stream.getAllProperties()['codec_type']}");
                print("Stream codec: ${
      
      stream.getAllProperties()['codec_name']}");
                print("Stream full codec: ${
      
      stream.getAllProperties()['codec_long_name']}");
                print("Stream format: ${
      
      stream.getAllProperties()['pix_fmt']}");
                print("Stream width: ${
      
      stream.getAllProperties()['width']}");
                print("Stream height: ${
      
      stream.getAllProperties()['height']}");
                print("Stream bitrate: ${
      
      stream.getAllProperties()['bit_rate']}");
                print("Stream sample rate: ${
      
      stream.getAllProperties()['sample_rate']}");
                print("Stream sample format: ${
      
      stream.getAllProperties()['sample_fmt']}");
                print("Stream channel layout: ${
      
      stream.getAllProperties()['channel_layout']}");
                print("Stream sar: ${
      
      stream.getAllProperties()['sample_aspect_ratio']}");
                print("Stream dar: ${
      
      stream.getAllProperties()['display_aspect_ratio']}");
                print("Stream average frame rate: ${
      
      stream.getAllProperties()['avg_frame_rate']}");
                print("Stream real frame rate: ${
      
      stream.getAllProperties()['r_frame_rate']}");
                print("Stream time base: ${
      
      stream.getAllProperties()['time_base']}");
                print("Stream codec time base: ${
      
      stream.getAllProperties()['codec_time_base']}");

                Map<dynamic, dynamic> tags = stream.getAllProperties()['tags'];
                if (tags != null) {
    
    
                  tags.forEach((key, value) {
    
    
                    print("Stream tag: " + key + ":" + value + "\n");
                  });
                }
            }
        }
    }
});

비디오 압축

  • 프레임 속도 변경
ffmpeg -i Desktop/吉他.mp4  -r 20  Desktop/output1.mp4

-r 20: 프레임 속도가 20fps로 설정되었음을 나타냅니다.

  • 파일 크기 지정
ffmpeg -i Desktop/吉他.mp4  -fs 15MB  Desktop/output1.mp4

fs 20: 최대 파일 크기가 15MB
이고 비디오의 일부가 잘리는 것을 나타냅니다. 이 방법은 권장되지 않습니다.

  • 해상도 변경
ffmpeg -i Desktop/1.mov -s vga Desktop/1.mp4

-s vga: 해상도를 지정합니다. vga는 600*480을 나타내며 다른 값으로 변경할 수도 있습니다.

  • 비트레이트를 변경해 보세요.
    영상의 원래 비트레이트는 2.1Mb/s이고 압축률은 1.5Mb/s입니다.
ffmpeg -i Desktop/1.mov -b:v 1.5M  Desktop/1.mp4

-b:v 1.5M: 비트 전송률을 지정합니다.
-b:v: 비디오 비트 전송률을 지정합니다.
-b:a: 오디오 비트 전송률을 지정합니다
. 1.5M: 비트 전송률 값 1.5M은 1.5Mb/s를 의미합니다.

비디오 자르기

예를 들어 내 프로젝트에서는 서버에 업로드되는 비디오의 지속 시간을 60초 이내에 제어해야 하는 기능을 사용합니다.

 String inputFilePath = inputFile.path;
                    String outputFilePath =
                        await FileUtils.outputFileNameStr(inputFile);
                    var ffmpeg = new FlutterFFmpeg();
                    ffmpeg
                        .execute(
                            "-i $inputFilePath -ss 0 -to 60 -c:v libx264 $outputFilePath")//这里是ffmpeg指令 裁剪60s视频
                        .then((rc) async {
    
    
                      if (rc == 0) {
    
    
                        //rc=0表示成功
                        //裁剪60s 转换 libx264
                      } else {
    
    
                        showToast('视频裁剪出现异常,请重试', context);
                        Navigator.pop(context);
                      }
                    });

비디오 형식 변환

var ffmpeg = new FlutterFFmpeg();
  Directory tempDir = await getTemporaryDirectory();;
  var tmpVideo = tempDir.path + '/1.webm';
  var cmd = '-i ${
      
      widget.tmpPath} $tmpVideo';
  print('命令是:'+cmd);
  ffmpeg.execute(cmd).then((rc) {
    
    
    print("处理的结果是:$rc");
    if(rc == 0) {
    
    
      initController(tmpVideo); //使用临时地址可以播放
    } else {
    
    
      print('处理失败');
    }
  });

아래에는 일반적으로 사용되는 ffmpeg 명령어가 나열되어 있습니다.

비디오 자르기

n부터 m초 길이의 영상을 자릅니다.

// 从10s开始裁剪连续5s 也就是10-15s的视频
ffmpeg -i input.mp4 -ss 10 -t 5 output.mp4

n초부터 시작하여 비디오를 m초로 자릅니다.

// 从10s开始,裁剪到20秒
ffmpeg -i input.mp4 -ss 10 -to 20 output.mp4
필터

https://www.cnblogs.com/tocy/p/ffmpeg-filter-intro.html

두 개의 비디오 오버레이:
ffmpeg -i 1.mp4 -i douyin.mp4 -filter_complex " blend=all_mode='overlay':all_opacity=0.2" blend.mp4
100*100 영상을 잘라서 새로운 영상의 중심점이 입력된 영상과 일치하게 합니다.
ffmpeg -i douyin.mp4  -vf "crop=100:100" crop.mp4
비디오에 프레임 그리기 상자를 그립니다.
// [email protected] 50%的透明度红色
// t=1 线条的粗细 默认3  fill的话 自动填充
ffmpeg -i input.mp4 -vf "drawbox=10:10:30:30:[email protected]:t=1" output.mp4
ffmpeg -i input.mp4 -vf "drawbox=x=10:y=10:width=30:[email protected]:t=1" output.mp4
비디오에 그리드 그리기 그리드 그리기
画尺寸是20*20的网格
ffmpeg -i 1.mp4 -vf "drawgrid=width=20:height=20:color=red" output.mp4
画2*2的网格
ffmpeg -i 1.mp4 -vf "drawgrid=width=iw/2:height=ih/2:color=red:t=1:[email protected]" output.mp4
비디오 드로텍스트에 쓰기
ffmpeg -i 99.mp4 -vf "drawtext=fontsize=32:fontcolor=red:text='hello world':alpha=0.8" font.mp4

//前五秒显示 
// gte(t\,5) 大于等于5秒
// between(t\,5\,10)5到10秒
// lt(t\, 5)小于5秒
ffmpeg -i 99.mp4 -vf "drawtext=fontsize=32:fontcolor=red:text='hello world':enable='lt(t\,5)' " font.mp4

//水平居中 (w-text_w)/2
//垂直居中 (h-text_h)/2
ffmpeg -i 99.mp4 -vf "drawtext=fontsize=32:fontcolor=#cccccc:text='hello world':x=(w-text_w)/2" font2.mp4

//向右对齐 
ffmpeg -i 99.mp4 -vf "drawtext=fontsize=32:fontcolor=#cccccc:text='hello world':x=(w-text_w)" font2.mp4

// 水平垂直居中
ffmpeg -i 99.mp4 -vf "drawtext=fontsize=32:fontcolor=#cccccc:text='hello world':x=(w-text_w)/2:y=(h-text_h)/2" font2.mp4

// 滚动文本
ffmpeg -i 99.mp4 -vf "drawtext=fontsize=32:fontcolor=#cccccc:text='hello world':x=(w-50*t)" font2.mp4
채도/밝기/대비 eq
// contrast 对比度 -1000-1000
// brightness 亮度 -1.0-1.0
// saturation 饱和度 0.0-3.0
ffmpeg -i 1.mp4 -vf "eq=saturation=3:brightness=0.3:contrast=1000" output.mp4
페이드 인 페이드 아웃 페이드
// 从第0帧开始到30帧淡入
ffmpeg -i 1.mp4 -vf "fade=t=in:s=0:n=30" output.mp4

// 从10秒开始淡出5秒
ffmpeg -i 1.mp4 -vf "fade=t=out:st=10:d=5" output.mp4
프레임 속도 fps 조정
ffmpeg -i 1.mp4 -vf "fps=fps=24" output.mp4
비디오를 가로로 뒤집기
ffmpeg -i 1.mp4 -vf "hflip" output.mp4
뒤집다
ffmpeg -i input.mp4 -vf "reverse" output.mp4
회전회전
旋转45度
ffmpeg -i input.mp4 -vf "rotate=PI/4"

转圈圈
ffplay -i 99.mp4 -vf "rotate=n*PI/3:c=red"

调整输出尺寸 修改输出宽度等于输入宽度的对角线
ffplay -i 99.mp4 -vf "rotate=n*PI/3:ow=hypot(iw, ih):oh=ow"
규모
ffplay -i 99.mp4 -vf "scale=100x100"
ffplay -i 99.mp4 -vf "scale=w=0.8*iw:h=0.8*ih"


// 宽度为100,iw较小的值 高度等比例缩小
ffplay -i 99.mp4 -vf "scale=w=min(100\,iw):h=-1"
병합 연결
// 视频的尺寸要一样大 如果不一样大的话 可以使用pad补边界 
ffmpeg -i 1.mp4 -i douyin.mp4 -i 3.1.mp4 -filter_complex "[0:v:0][0:a:0][1:v:0][1:a:0][2:v:0][2:a:0]concat=n=3:v=1:a=1[ov][oa]" -map "[ov]" -map "[oa]" -vsync 2 output.mp4

// 图片和视频合并 需要调整视频图片的尺寸和视频一样
ffmpeg -loop 1 -framerate 25 -t 5 -i logo.png -i 3.mp4 -filter_complex "[0]scale=1280x720[01];[01][1:v:0]concat=n=2:v=1[ov]" -map "[ov]" 33.mp4

ffmpeg -loop 1 -framerate 25 -t 5 -i logo.png -i 3.mp4 -filter_complex "[0]pad=w=1280:h=720:w=(1280-iw)/2:y=(720-ih)/2[01];[01][1:v:0]concat=n=2:v=1[ov]" -map "[ov]" 33.mp4

ffmpeg -loop 1 -framerate 29.42 -t 10 -i 1.s.jpg -i 1.mp4 -filter_complex "[0]scale=480:480, setsar=1[im];[1]scale=480:480, setsar=1[iv];[im][1:a][iv][1:a]concat=n=2:v=1:a=1[outv][outa]" -map "[outa]" -map "[outv]" output.mp4

// 图片生成视频
ffmpeg -f lavfi -t 10 -i anullsrc -loop 1 -framerate 10 -t 5 -i 1.jpg -vf "scale=664x478" -pix_fmt yuv420p output.mp4

// 添加第三条音频
 ffmpeg -i im.mp4 -i douyin.mp4 -i 1.mp3 -filter_complex "[0]scale=544x960,setsar=1[iv];[1]setsar=1[dv];[iv][0:a:0][dv][1:a:0]concat=n=2:v=1:a=1[ov][oa]" -map "[ov]" -map "[oa]" -map 2:a:0 -vsync 2 output.mp4

ffmpeg -i im.mp4 -i douyin.mp4 -i 1.mp3 -filter_complex "[0]scale=544x960,setsar=1[iv];[1]setsar=1[dv];[iv][0:a:0][dv][1:a:0]concat=n=2:v=1:a=1[ov][oa];[2:a:0][oa]amix=inputs=2:duration=first:dropout_transition=3[aa]" -map "[ov]" -map "[aa]" -vsync 2 output.mp4
비디오 썸네일 추출
// 三秒一张 fps=1/3 
ffmpeg -i input.mp4 -vf "fps=1/3, scale=128:72" output_%d.jpg
  >>> 音视频开发 视频教程: https://ke.qq.com/course/3202131?flowToken=1031864 
  >>> 音视频开发学习资料、教学视频,免费分享有需要的可以自行添加学习交流群 739729163 领取

추천

출처blog.csdn.net/weixin_52622200/article/details/131537307