Skip to content

Commit

Permalink
* Give users of FFmpegFrameGrabber and FFmpegFrameRecorder acces…
Browse files Browse the repository at this point in the history
…s to more options and metadata (issue #132)
  • Loading branch information
saudet committed May 6, 2015
1 parent 07f1e4f commit 70db4a5
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 8 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
@@ -1,6 +1,7 @@

* Give users of `FFmpegFrameGrabber` and `FFmpegFrameRecorder` access to more options and metadata ([issue #132](https://github.com/bytedeco/javacv/issues/132))
* Add the ability to specify from which video and audio streams `FFmpegFrameGrabber` should grab from ([issue #135](https://github.com/bytedeco/javacv/issues/135))
* Fix `Java2DFrameConverter` when used with `BufferedImage.TYPE_INT_RGB` or other types based on `int`
* Fix `Java2DFrameConverter` when used with `BufferedImage.TYPE_INT_RGB` or other types based on `int` ([issue #140](https://github.com/bytedeco/javacv/issues/140))
* Add new `WebcamAndMicrophoneCapture` sample ([pull #131](https://github.com/bytedeco/javacv/pull/131))
* Add `aspectRatio` property to `FrameGrabber` and `FrameRecorder`, to be able to use pixel aspect ratios other than 1.0 ([issue #90](https://github.com/bytedeco/javacv/issues/90))

Expand Down
36 changes: 34 additions & 2 deletions src/main/java/org/bytedeco/javacv/FFmpegFrameGrabber.java
Expand Up @@ -292,6 +292,30 @@ public void releaseUnsafe() throws Exception {
return audio_c == null ? super.getSampleRate() : audio_c.sample_rate();
}

@Override public String getMetadata(String key) {
if (oc == null) {
return super.getMetadata(key);
}
AVDictionaryEntry entry = av_dict_get(oc.metadata(), key, null, 0);
return entry == null || entry.value() == null ? null : entry.value().getString();
}

@Override public String getVideoMetadata(String key) {
if (video_st == null) {
return super.getVideoMetadata(key);
}
AVDictionaryEntry entry = av_dict_get(video_st.metadata(), key, null, 0);
return entry == null || entry.value() == null ? null : entry.value().getString();
}

@Override public String getAudioMetadata(String key) {
if (audio_st == null) {
return super.getAudioMetadata(key);
}
AVDictionaryEntry entry = av_dict_get(audio_st.metadata(), key, null, 0);
return entry == null || entry.value() == null ? null : entry.value().getString();
}

@Override public void setFrameNumber(int frameNumber) throws Exception {
// best guess, AVSEEK_FLAG_FRAME has not been implemented in FFmpeg...
setTimestamp(Math.round(1000000L * frameNumber / getFrameRate()));
Expand Down Expand Up @@ -440,8 +464,12 @@ public void startUnsafe() throws Exception {
throw new Exception("avcodec_find_decoder() error: Unsupported video format or codec not found: " + video_c.codec_id() + ".");
}

options = new AVDictionary(null);
for (Entry<String, String> e : videoOptions.entrySet()) {
av_dict_set(options, e.getKey(), e.getValue(), 0);
}
// Open video codec
if ((ret = avcodec_open2(video_c, codec, (PointerPointer)null)) < 0) {
if ((ret = avcodec_open2(video_c, codec, options)) < 0) {
throw new Exception("avcodec_open2() error " + ret + ": Could not open video codec.");
}

Expand Down Expand Up @@ -496,8 +524,12 @@ public void startUnsafe() throws Exception {
throw new Exception("avcodec_find_decoder() error: Unsupported audio format or codec not found: " + audio_c.codec_id() + ".");
}

options = new AVDictionary(null);
for (Entry<String, String> e : audioOptions.entrySet()) {
av_dict_set(options, e.getKey(), e.getValue(), 0);
}
// Open audio codec
if ((ret = avcodec_open2(audio_c, codec, (PointerPointer)null)) < 0) {
if ((ret = avcodec_open2(audio_c, codec, options)) < 0) {
throw new Exception("avcodec_open2() error " + ret + ": Could not open audio codec.");
}

Expand Down
35 changes: 30 additions & 5 deletions src/main/java/org/bytedeco/javacv/FFmpegFrameRecorder.java
Expand Up @@ -452,11 +452,16 @@ public void startUnsafe() throws Exception {
audio_c.channel_layout(av_get_default_channel_layout(audioChannels));
if (sampleFormat != AV_SAMPLE_FMT_NONE) {
audio_c.sample_fmt(sampleFormat);
} else if ((audio_codec.capabilities() & CODEC_CAP_EXPERIMENTAL) != 0
&& (audio_c.codec_id() == AV_CODEC_ID_VORBIS || audio_c.codec_id() == AV_CODEC_ID_AAC)) {
audio_c.sample_fmt(AV_SAMPLE_FMT_FLTP);
} else {
audio_c.sample_fmt(AV_SAMPLE_FMT_S16);
// use AV_SAMPLE_FMT_S16 by default, if available
audio_c.sample_fmt(AV_SAMPLE_FMT_FLTP);
IntPointer formats = audio_c.codec().sample_fmts();
for (int i = 0; formats.get(i) != -1; i++) {
if (formats.get(i) == AV_SAMPLE_FMT_S16) {
audio_c.sample_fmt(AV_SAMPLE_FMT_S16);
break;
}
}
}
audio_c.time_base().num(1).den(sampleRate);
audio_st.time_base().num(1).den(sampleRate);
Expand Down Expand Up @@ -538,6 +543,12 @@ public void startUnsafe() throws Exception {
release();
throw new Exception("av_frame_alloc() error: Could not allocate temporary picture.");
}

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);
}

if (audio_st != null) {
Expand Down Expand Up @@ -594,6 +605,12 @@ public void startUnsafe() throws Exception {
throw new Exception("av_frame_alloc() error: Could not allocate audio frame.");
}
frame.pts(0); // magic required by libvorbis and webm

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

/* open the output file, if needed */
Expand All @@ -606,8 +623,16 @@ public void startUnsafe() throws Exception {
oc.pb(pb);
}

AVDictionary options = new AVDictionary(null);
for (Entry<String, String> e : this.options.entrySet()) {
av_dict_set(options, e.getKey(), e.getValue(), 0);
}
AVDictionary metadata = new AVDictionary(null);
for (Entry<String, String> e : this.metadata.entrySet()) {
av_dict_set(metadata, e.getKey(), e.getValue(), 0);
}
/* write the stream header, if any */
avformat_write_header(oc, (PointerPointer)null);
avformat_write_header(oc.metadata(metadata), options);
}

public void stop() throws Exception {
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/org/bytedeco/javacv/FrameGrabber.java
Expand Up @@ -179,6 +179,11 @@ public static enum ImageMode {
protected double gamma = 0.0;
protected boolean deinterlace = false;
protected HashMap<String, String> options = new HashMap<String, String>();
protected HashMap<String, String> videoOptions = new HashMap<String, String>();
protected HashMap<String, String> audioOptions = new HashMap<String, String>();
protected HashMap<String, String> metadata = new HashMap<String, String>();
protected HashMap<String, String> videoMetadata = new HashMap<String, String>();
protected HashMap<String, String> audioMetadata = new HashMap<String, String>();
protected int frameNumber = 0;
protected long timestamp = 0;

Expand Down Expand Up @@ -350,6 +355,41 @@ public void setOption(String key, String value) {
options.put(key, value);
}

public String getVideoOption(String key) {
return videoOptions.get(key);
}
public void setVideoOption(String key, String value) {
videoOptions.put(key, value);
}

public String getAudioOption(String key) {
return audioOptions.get(key);
}
public void setAudioOption(String key, String value) {
audioOptions.put(key, value);
}

public String getMetadata(String key) {
return metadata.get(key);
}
public void setMetadata(String key, String value) {
metadata.put(key, value);
}

public String getVideoMetadata(String key) {
return videoMetadata.get(key);
}
public void setVideoMetadata(String key, String value) {
videoMetadata.put(key, value);
}

public String getAudioMetadata(String key) {
return audioMetadata.get(key);
}
public void setAudioMetadata(String key, String value) {
audioMetadata.put(key, value);
}

public int getFrameNumber() {
return frameNumber;
}
Expand Down
32 changes: 32 additions & 0 deletions src/main/java/org/bytedeco/javacv/FrameRecorder.java
Expand Up @@ -106,8 +106,12 @@ public static FrameRecorder create(String className, String filename, int width,
protected int sampleFormat, audioCodec, audioBitrate, sampleRate;
protected double audioQuality = -1;
protected boolean interleaved;
protected HashMap<String, String> options = new HashMap<String, String>();
protected HashMap<String, String> videoOptions = new HashMap<String, String>();
protected HashMap<String, String> audioOptions = new HashMap<String, String>();
protected HashMap<String, String> metadata = new HashMap<String, String>();
protected HashMap<String, String> videoMetadata = new HashMap<String, String>();
protected HashMap<String, String> audioMetadata = new HashMap<String, String>();
protected int frameNumber = 0;
protected long timestamp = 0;

Expand Down Expand Up @@ -244,6 +248,13 @@ public void setInterleaved(boolean interleaved) {
this.interleaved = interleaved;
}

public String getOption(String key) {
return options.get(key);
}
public void setOption(String key, String value) {
options.put(key, value);
}

public String getVideoOption(String key) {
return videoOptions.get(key);
}
Expand All @@ -258,6 +269,27 @@ public void setAudioOption(String key, String value) {
audioOptions.put(key, value);
}

public String getMetadata(String key) {
return metadata.get(key);
}
public void setMetadata(String key, String value) {
metadata.put(key, value);
}

public String getVideoMetadata(String key) {
return videoMetadata.get(key);
}
public void setVideoMetadata(String key, String value) {
videoMetadata.put(key, value);
}

public String getAudioMetadata(String key) {
return audioMetadata.get(key);
}
public void setAudioMetadata(String key, String value) {
audioMetadata.put(key, value);
}

public int getFrameNumber() {
return frameNumber;
}
Expand Down

0 comments on commit 70db4a5

Please sign in to comment.