问题
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.
回答1:
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.
回答2:
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()
methodpublic 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.
来源:https://stackoverflow.com/questions/29973423/setting-video-stream-metadata-using-ffmpeg