17

I'm using the JavaCV FFmpegFrameRecorder class to encode Android's camera preview frames into a video.

The goal would be to replicate the result of the following command line:

ffmpeg -i input.mp4 -metadata:s:v:0 rotate="90" output.mp4

I modified the startUnsafe() method as follows, but it failed to generate the desired output:

if ((video_st = avformat_new_stream(oc, video_codec)) != null) {
        video_c = video_st.codec();
        video_c.codec_id(oformat.video_codec());
        video_c.codec_type(AVMEDIA_TYPE_VIDEO);
        ...
        AVDictionary avDictionary = new AVDictionary(null);
        av_dict_set(avDictionary, "rotate", "90", 0);
        video_st.metadata(avDictionaty);
        ...
}
...
avformat_write_header(oc, (PointerPointer) null);

This still encodes the video correctly, but the added metadata never appears on ffprobe. If it helps, the video encoding is h264.

By the way, here's the ffprobe output:

ffprobe version 2.3.3 Copyright (c) 2007-2014 the FFmpeg developers
  built on Jan 22 2015 18:22:57 with Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/2.3.3 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-nonfree --enable-hardcoded-tables --enable-avresample --enable-vda --cc=clang --host-cflags= --host-ldflags= --enable-libx264 --enable-libfaac --enable-libmp3lame --enable-libxvid --enable-libfreetype --enable-libvorbis --enable-libvpx --enable-libass --enable-ffplay --enable-libfdk-aac --enable-libopus --enable-libquvi --enable-libx265
  libavutil      52. 92.100 / 52. 92.100
  libavcodec     55. 69.100 / 55. 69.100
  libavformat    55. 48.100 / 55. 48.100
  libavdevice    55. 13.102 / 55. 13.102
  libavfilter     4. 11.100 /  4. 11.100
  libavresample   1.  3.  0 /  1.  3.  0
  libswscale      2.  6.100 /  2.  6.100
  libswresample   0. 19.100 /  0. 19.100
  libpostproc    52.  3.100 / 52.  3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'abcd.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf56.15.102
  Duration: 00:00:19.48, start: 0.023220, bitrate: 572 kb/s
    Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 1280x720, 573 kb/s, 5.71 fps, 30 tbr, 15360 tbn, 60 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, mono, fltp, 64 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

Any suggestions on why is it failing? Thanks.

Edson Menegatti
  • 4,006
  • 2
  • 25
  • 40

2 Answers2

4

It seems this question generated a lot of interest so I'm adding some more info. Following this GitHub issue Samuel from JavaCV committed some changes to allow easier access to metadata settings.

Setting metadata can be achieved through the following code snippet:

AVDictionary metadata = new AVDictionary(null);
for (Entry<String, String> e : videoMetadata.entrySet()) {
    av_dict_set(metadata, e.getKey(), e.getValue(), 0);
}
video_st.metadata(metadata);

You can enable it right now by doing mvn install -Pffmpeg or wait until the next JavacV release, which should be 0.12.

PS: As you can see, this is pretty similar to what I presented in my question, so I'm not sure why it didn't work in the first place.

Edson Menegatti
  • 4,006
  • 2
  • 25
  • 40
1

The FFmpegFrameRecorder class you are using uses an AVFormatContext class. Around line 2579 you can see from the method signatures, that the AVFormatContext class uses native code to implement both

  • public native AVDictionary metadata() method
  • public native AVFormatContext metadata(AVDictionary metadata) method.

The answer to the link you provided says they used the metadata property of the AVFormatContext directly - something like the first method I think. But Line 649 of FFmpegFrameRecorder uses the second method - I suspect. i.e.:

AVDictionary metadata = new AVDictionary(null);
... code to fill up dictionary ...
...

avformat_write_header(oc.metadata(metadata), options);

Unfortunately I can't try this at the moment, but I wonder if you could do something like this:

AVDictionary metadata = co.metadata();
... code to fill up dictionary ...

//I would assume at this point that oc has the metadata so
avformat_write_header(oc, (PointerPointer) null);
//if not then maybe
// avformat_write_header(oc.metadata(metadata), options);

The signature shows it is public, so I don't see why you can't get the metadata dictionary from the AVFormatContext directly. I'm not sure how the avformat_write_header method works, so I suggested two things above.

Note: I have not used this library before. I tried, unsuccessfully to use Xuggler in the past for some basic encoding.

Vic
  • 437
  • 3
  • 8