1

I am aware that there already exists a solution to a very similar question, which can be found on the following link: Flutter/Dart: Find two video segments and merge them into a single valid video file? However, being relatively new to Flutter (and programming in general) I cannot seem to replicate the desired result.

My app is very simple and currently looks like this:

enter image description here

I click on the button "Record Video" to record two videos, which are both successfully stored into the device's gallery. Using the Flutter image_picker and gallery_saver packages and the following piece of code:

    void _recordVideo() async {
    ImagePicker.pickVideo(source: ImageSource.camera)
        .then((File recordedVideo) {
      if (recordedVideo != null && recordedVideo.path != null) {
        setState(() {
          _buttonText = 'Saving in Progress...';
        });
        GallerySaver.saveVideo(recordedVideo.path).then((_) {
          setState(() {
            _buttonText = 'Video Saved!\n\nClick to Record New Video';
            if (_storedVideoOne == null) {
              _storedVideoOne = recordedVideo;
              print('video 1 stored');
            } else {
              _storedVideoTwo = recordedVideo;
              print('video 2 stored');
              _videoMerger();
            }
          });
        });
      }
    });
  }

I can view these videos when I click on the button at the bottom "View Video From Gallery".

Next I try to merge these two stored video files, using the flutter_ffmpeg package, as well as following the solution provided in the stack overflow question mentioned above. I try and do this using the following function I wrote:

void _videoMerger() async {


    final appDir = await syspaths.getApplicationDocumentsDirectory();
    String rawDocumentPath = appDir.path;
    final outputPath = '$rawDocumentPath/output.mp4';

    final FlutterFFmpeg _flutterFFmpeg = new FlutterFFmpeg();

        String commandToExecute = '-i ${_storedVideoOne.path} -i ${_storedVideoTwo.path} -filter_complex \'[0:0][1:0]concat=n=2:v=1:a=0[out]\' -map \'[out]\' outputPath';
        _flutterFFmpeg.execute(commandToExecute).then((rc) => print("FFmpeg process exited with rc $rc"));

  }

But after running the function I do not seem to get a new combined video, which should be stored in outputPath and ideally also viewable in the gallery. Uploaded the Flutter project onto GitHub here:

https://github.com/IttaiBarkai/Flutter-Video-Merger

Any help would be greatly appreciated :)

Updated:

Below is the output displayed on my debug console when ffmpeg gets executed:

D/flutter-ffmpeg( 4146): Running FFmpeg with arguments: [-i, /storage/emulated/0/Android/data/com.example.video_merger_two/files/Pictures/d2b7a612-7c6d-48fe-8d06-85ceeb10e2f584195978113840656.mp4, -i, /storage/emulated/0/Android/data/com.example.video_merger_two/files/Pictures/b6cb83a3-10ac-49c7-80f3-3447bebe93ac5245748251872788895.mp4, -filter_complex, [0:0][1:0]concat=n=2:v=1:a=0[out], -map, [out], outputPath.mp4].
I/mobile-ffmpeg( 4146): ffmpeg version git-2020-01-25-fd11dd500
I/mobile-ffmpeg( 4146):  Copyright (c) 2000-2020 the FFmpeg developers
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):   built with Android (5220042 based on r346389c) clang version 8.0.7 (https://android.googlesource.com/toolchain/clang b55f2d4ebfd35bf643d27dbca1bb228957008617) (https://android.googlesource.com/toolchain/llvm 3c393fe7a7e13b0fba4ac75a01aa683d7a5b11cd) (based on LLVM 8.0.7svn)
I/mobile-ffmpeg( 4146):   configuration: --cross-prefix=i686-linux-android- --sysroot=/files/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot --prefix=/home/taner/Projects/mobile-ffmpeg/prebuilt/android-x86/ffmpeg --pkg-config=/usr/bin/pkg-config --enable-version3 --arch=i686 --cpu=i686 --cc=i686-linux-android24-clang --cxx=i686-linux-android24-clang++ --target-os=android --disable-neon --disable-asm --disable-inline-asm --enable-cross-compile --enable-pic --enable-jni --enable-optimizations --enable-swscale --enable-shared --disable-v4l2-m2m --disable-outdev=v4l2 --disable-outdev=fbdev --disable-indev=v4l2 --disable-indev=fbdev --enable-small --disable-openssl --disable-xmm-clobber-test --disable-debug --enable-lto --disable-neon-clobber-test --disable-programs --disable-postproc --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages --disable-static --disable-sndio --disable-schannel --disable-securetransport --disable-xlib --disable-cuda --disable-cuvid --disable-nvenc --di
I/mobile-ffmpeg( 4146):   libavutil      56. 38.100 / 56. 38.100
I/mobile-ffmpeg( 4146):   libavcodec     58. 65.102 / 58. 65.102
I/mobile-ffmpeg( 4146):   libavformat    58. 35.101 / 58. 35.101
I/mobile-ffmpeg( 4146):   libavdevice    58.  9.103 / 58.  9.103
I/mobile-ffmpeg( 4146):   libavfilter     7. 70.101 /  7. 70.101
I/mobile-ffmpeg( 4146):   libswscale      5.  6.100 /  5.  6.100
I/mobile-ffmpeg( 4146):   libswresample   3.  6.100 /  3.  6.100
I/mobile-ffmpeg( 4146): Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/storage/emulated/0/Android/data/com.example.video_merger_two/files/Pictures/d2b7a612-7c6d-48fe-8d06-85ceeb10e2f584195978113840656.mp4':
I/mobile-ffmpeg( 4146):   Metadata:
I/mobile-ffmpeg( 4146):     major_brand     :
I/mobile-ffmpeg( 4146): mp42
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     minor_version   :
I/mobile-ffmpeg( 4146): 0
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     compatible_brands:
I/mobile-ffmpeg( 4146): isommp42
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     creation_time   :
I/mobile-ffmpeg( 4146): 2020-06-17T12:07:20.000000Z
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     com.android.version:
I/mobile-ffmpeg( 4146): 10
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):   Duration:
I/mobile-ffmpeg( 4146): 27:34:19.40
I/mobile-ffmpeg( 4146): , start:
I/mobile-ffmpeg( 4146): 0.000000
I/mobile-ffmpeg( 4146): , bitrate:
I/mobile-ffmpeg( 4146): 0 kb/s
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Stream #0:0
I/mobile-ffmpeg( 4146): (eng)
I/mobile-ffmpeg( 4146): : Video: h264 (avc1 / 0x31637661), yuv420p(tv, GBR), 1280x720, 3536 kb/s
I/mobile-ffmpeg( 4146): , SAR 1:1 DAR 16:9
I/mobile-ffmpeg( 4146): ,
I/mobile-ffmpeg( 4146): 28.75 fps,
I/mobile-ffmpeg( 4146): 29.08 tbr,
I/mobile-ffmpeg( 4146): 90k tbn,
I/mobile-ffmpeg( 4146): 180k tbc
I/mobile-ffmpeg( 4146):  (default)
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Metadata:
I/mobile-ffmpeg( 4146):       rotate          :
I/mobile-ffmpeg( 4146): 90
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):       creation_time   :
I/mobile-ffmpeg( 4146): 2020-06-17T12:07:20.000000Z
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):       handler_name    :
I/mobile-ffmpeg( 4146): VideoHandle
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Side data:
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146): displaymatrix: rotation of -90.00 degrees
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Stream #0:1
I/mobile-ffmpeg( 4146): (eng)
I/mobile-ffmpeg( 4146): : Audio: amr_nb (samr / 0x726D6173), 8000 Hz, mono, flt, 12 kb/s
I/mobile-ffmpeg( 4146):  (default)
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Metadata:
I/mobile-ffmpeg( 4146):       creation_time   :
I/mobile-ffmpeg( 4146): 2020-06-17T12:07:20.000000Z
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):       handler_name    :
I/mobile-ffmpeg( 4146): SoundHandle
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146): Input #1, mov,mp4,m4a,3gp,3g2,mj2, from '/storage/emulated/0/Android/data/com.example.video_merger_two/files/Pictures/b6cb83a3-10ac-49c7-80f3-3447bebe93ac5245748251872788895.mp4':
I/mobile-ffmpeg( 4146):   Metadata:
I/mobile-ffmpeg( 4146):     major_brand     :
I/mobile-ffmpeg( 4146): mp42
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     minor_version   :
I/mobile-ffmpeg( 4146): 0
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     compatible_brands:
I/mobile-ffmpeg( 4146): isommp42
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     creation_time   :
I/mobile-ffmpeg( 4146): 2020-06-17T12:07:32.000000Z
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     com.android.version:
I/mobile-ffmpeg( 4146): 10
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):   Duration:
I/mobile-ffmpeg( 4146): 27:34:19.35
I/mobile-ffmpeg( 4146): , start:
I/mobile-ffmpeg( 4146): 0.000000
I/mobile-ffmpeg( 4146): , bitrate:
I/mobile-ffmpeg( 4146): 0 kb/s
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Stream #1:0
I/mobile-ffmpeg( 4146): (eng)
I/mobile-ffmpeg( 4146): : Video: h264 (avc1 / 0x31637661), yuv420p(tv, GBR), 1280x720, 3561 kb/s
I/mobile-ffmpeg( 4146): , SAR 1:1 DAR 16:9
I/mobile-ffmpeg( 4146): ,
I/mobile-ffmpeg( 4146): 28.95 fps,
I/mobile-ffmpeg( 4146): 29 tbr,
I/mobile-ffmpeg( 4146): 90k tbn,
I/mobile-ffmpeg( 4146): 180k tbc
I/mobile-ffmpeg( 4146):  (default)
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Metadata:
I/mobile-ffmpeg( 4146):       rotate          
:
I/mobile-ffmpeg( 4146): 90
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):       creation_time   :
I/mobile-ffmpeg( 4146): 2020-06-17T12:07:32.000000Z
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):       handler_name    :
I/mobile-ffmpeg( 4146): VideoHandle
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Side data:
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146): displaymatrix: rotation of -90.00 degrees
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Stream #1:1
I/mobile-ffmpeg( 4146): (eng)
I/mobile-ffmpeg( 4146): : Audio: amr_nb (samr / 0x726D6173), 8000 Hz, mono, flt, 12 kb/s
I/mobile-ffmpeg( 4146):  (default)
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):     Metadata:
I/mobile-ffmpeg( 4146):       creation_time   :
I/mobile-ffmpeg( 4146): 2020-06-17T12:07:32.000000Z
I/mobile-ffmpeg( 4146):
I/mobile-ffmpeg( 4146):       handler_name    :
I/mobile-ffmpeg( 4146): SoundHandle
I/mobile-ffmpeg( 4146):
E/mobile-ffmpeg( 4146): outputPath.mp4: Read-only file system
D/flutter-ffmpeg( 4146): FFmpeg exited with rc: 1
I/flutter ( 4146): FFmpeg process exited with rc 1
Ittai Barkai
  • 120
  • 1
  • 9
  • Does ffmpeg even get executed? – llogan Jun 15 '20 at 19:10
  • 1
    @llogan, thanks for the response. Yes I did manage to get the flutter_ffmpeg package to execute. For Android emulators/phones I had to go to the android folder > app > src > build.gradle and change the minSdkVersion to 24. For the package to work on iOS I had to uncomment and change platform :iOS to '9.3' (this can be achieved in the iOS folder > Podfile) Furthermore I also had to change the target dependency in Xcode to 9.3 by going into the Runner.xcodeproj file of the current project. – Ittai Barkai Jun 16 '20 at 12:55
  • Was there an error from ffmpeg? It will output it to stderr, or add the `-report` option and it will create an annoyingly verbose `.log` file in the working directory. – llogan Jun 16 '20 at 21:12
  • @llogan, I updated my question and added the output I receive from the debug console. I tried adding the -report option, but do not seem to be getting the .log file. I am probably adding the option incorrectly. – Ittai Barkai Jun 17 '20 at 12:55
  • I didn't see `-report` in your `ffmpeg` command shown in your debug console. – llogan Jun 17 '20 at 18:03

2 Answers2

4

Your mistake is when you construct comandToExecute. You missed a $:

String commandToExecute = '-i ${_storedVideoOne.path} -i ${_storedVideoTwo.path} -filter_complex \'[0:0][1:0]concat=n=2:v=1:a=0[out]\' -map \'[out]\' $outputPath';
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • 1
    Thanks so much for pointing out that silly mistake. I made the change and then got the following message : `E/mobile-ffmpeg(27157): Not overwriting - exiting` therefore I added `-y` to the commandToExecute. Unfortunately, I am getting a bunch of new error messages now. These messages can again be viewed at the bottom of my original question where I display the debug console output (which I updated). Thanks again for the help! – Ittai Barkai Jun 17 '20 at 18:32
  • 1
    For the sake of community, and to improve your carma, don't 'hijack' your own question. One step settled, set the answer as accepted, open another question, link to the first one. – Alex Cohn Jun 17 '20 at 18:35
  • I edited this question back to its original form and for anyone interested the follow-up question can be found here: [https://stackoverflow.com/questions/62436443/flutter-dart-merge-two-videos-and-view-in-gallery-part-2] – Ittai Barkai Jun 17 '20 at 19:04
  • how we can add frame(png image) surrounding in video. – krunal Gajera Jul 26 '23 at 09:02
1

outputPath.mp4: Read-only file system

Output to a location that can be written to.

llogan
  • 121,796
  • 28
  • 232
  • 243
  • Thanks again for the response, like I mentioned to Alex Cohn, I made the change to fix that problem. Unfortunately I am getting a bunch of other error messages now, which I displayed in the debug console. – Ittai Barkai Jun 17 '20 at 18:38