Skip to content

Commit

Permalink
* Increase thread safety of FFmpegFrameFilter, `FFmpegFrameGrabber…
Browse files Browse the repository at this point in the history
…`, and `FFmpegFrameRecorder` with `volatile boolean started` flag (pull #1325)

Check if avformat_write_header() was successful in FFMpegFrameRecorder.start()
Prevent usage before successful start() for FFmpegFrame[Filter|Grabber|Recorder]
  • Loading branch information
Michael Fritscher authored and saudet committed Nov 1, 2019
1 parent d6b2c78 commit 05ad9ab
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Increase thread safety of `FFmpegFrameFilter`, `FFmpegFrameGrabber`, and `FFmpegFrameRecorder` with `volatile boolean started` flag ([pull #1325](https://github.com/bytedeco/javacv/pull/1325))
* Let `FFmpegFrameFilter.push(null)` indicate EOF to audio filters as well ([issue #1315](https://github.com/bytedeco/javacv/issues/1315))
* Add `RealSense2FrameGrabber` to capture images with librealsense2 ([pull #1316](https://github.com/bytedeco/javacv/pull/1316))
* Disable seek function in `FFmpegFrameGrabber` when `maximumSize <= 0` ([issue #1304](https://github.com/bytedeco/javacv/issues/1304))
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/bytedeco/javacv/FFmpegFrameFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ public FFmpegFrameFilter(String afilters, int audioChannels) {
}
}
public void releaseUnsafe() throws Exception {
started = false;

if (filter_graph != null) {
avfilter_graph_free(filter_graph);
buffersink_ctx = null;
Expand Down Expand Up @@ -202,6 +204,8 @@ public void releaseUnsafe() throws Exception {
Buffer[] samples_buf;
Frame frame, inframe;

private volatile boolean started = false;

@Override public int getImageWidth() {
return buffersink_ctx != null ? av_buffersink_get_w(buffersink_ctx) : super.getImageWidth();
}
Expand Down Expand Up @@ -277,6 +281,8 @@ public void startUnsafe() throws Exception {
if (afilters != null && audioChannels > 0 && audioInputs > 0) {
startAudioUnsafe();
}

started = true;
}

void startVideoUnsafe() throws Exception {
Expand Down Expand Up @@ -493,6 +499,10 @@ public void push(int n, Frame frame) throws Exception {
push(n, frame, frame != null && frame.opaque instanceof AVFrame ? ((AVFrame)frame.opaque).format() : AV_PIX_FMT_NONE);
}
public void push(int n, Frame frame, int pixelFormat) throws Exception {
if (!started) {
throw new Exception("start() was not called successfully!");
}

inframe = frame;
if (frame != null && frame.image != null && buffersrc_ctx != null) {
image_frame.pts(frame.timestamp * time_base.den() / (1000000L * time_base.num()));
Expand All @@ -515,6 +525,10 @@ public void push(int n, Frame frame, int pixelFormat) throws Exception {
}

public void pushImage(int n, int width, int height, int depth, int channels, int stride, int pixelFormat, Buffer ... image) throws Exception {
if (!started) {
throw new Exception("start() was not called successfully!");
}

int ret;
int step = stride * Math.abs(depth) / 8;
BytePointer data = image[0] instanceof ByteBuffer
Expand Down Expand Up @@ -555,6 +569,10 @@ public void pushImage(int n, int width, int height, int depth, int channels, int
}

public void pushSamples(int n, int audioChannels, int sampleRate, int sampleFormat, Buffer ... samples) throws Exception {
if (!started) {
throw new Exception("start() was not called successfully!");
}

int ret;
Pointer[] data = new Pointer[samples.length];
int sampleSize = samples != null ? ((samples[0].limit() - samples[0].position()) / (samples.length > 1 ? 1 : audioChannels)) : 0;
Expand Down Expand Up @@ -606,6 +624,10 @@ public void pushSamples(int n, int audioChannels, int sampleRate, int sampleForm
}

@Override public Frame pull() throws Exception {
if (!started) {
throw new Exception("start() was not called successfully!");
}

frame.keyFrame = false;
frame.imageWidth = 0;
frame.imageHeight = 0;
Expand Down Expand Up @@ -633,6 +655,10 @@ public void pushSamples(int n, int audioChannels, int sampleRate, int sampleForm
}

public Frame pullImage() throws Exception {
if (!started) {
throw new Exception("start() was not called successfully!");
}

av_frame_unref(filt_frame);

/* pull a filtered frame from the filtergraph */
Expand Down Expand Up @@ -678,6 +704,10 @@ public Frame pullImage() throws Exception {
}

public Frame pullSamples() throws Exception {
if (!started) {
throw new Exception("start() was not called successfully!");
}

av_frame_unref(filt_frame);

/* pull a filtered frame from the filtergraph */
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/org/bytedeco/javacv/FFmpegFrameGrabber.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ public void release() throws Exception {
}
}
public void releaseUnsafe() throws Exception {
started = false;
if (pkt != null && pkt2 != null) {
if (pkt2.size() > 0) {
av_packet_unref(pkt);
Expand Down Expand Up @@ -364,6 +365,8 @@ static class SeekCallback extends Seek_Pointer_long_int {
private boolean frameGrabbed;
private Frame frame;

private volatile boolean started = false;

public boolean isCloseInputStream() {
return closeInputStream;
}
Expand Down Expand Up @@ -976,6 +979,7 @@ public void startUnsafe() throws Exception {
samples_ptr = new BytePointer[] { null };
samples_buf = new Buffer[] { null };
}
started = true;
}

private void initPictureRGB() {
Expand Down Expand Up @@ -1216,6 +1220,10 @@ public Frame grabFrame(boolean doAudio, boolean doVideo, boolean doProcessing, b
} else if ((!doVideo || video_st == null) && (!doAudio || audio_st == null)) {
return null;
}
if (!started) {
throw new Exception("start() was not called successfully!");
}

boolean videoFrameGrabbed = frameGrabbed && frame.image != null;
boolean audioFrameGrabbed = frameGrabbed && frame.samples != null;
frameGrabbed = false;
Expand Down Expand Up @@ -1324,7 +1332,10 @@ public Frame grabFrame(boolean doAudio, boolean doVideo, boolean doProcessing, b

public AVPacket grabPacket() throws Exception {
if (oc == null || oc.isNull()) {
throw new Exception("Could not trigger: No AVFormatContext. (Has start() been called?)");
throw new Exception("Could not grab: No AVFormatContext. (Has start() been called?)");
}
if (!started) {
throw new Exception("start() was not called successfully!");
}

// Return the next frame of a stream.
Expand Down
28 changes: 25 additions & 3 deletions src/main/java/org/bytedeco/javacv/FFmpegFrameRecorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ public void release() throws Exception {
}
}
public void releaseUnsafe() throws Exception {
started = false;

/* close each codec */
if (video_c != null) {
avcodec_free_context(video_c);
Expand Down Expand Up @@ -348,6 +350,8 @@ static class WriteCallback extends Write_packet_Pointer_BytePointer_int {
private int[] got_video_packet, got_audio_packet;
private AVFormatContext ifmt_ctx;

private volatile boolean started = false;

public boolean isCloseOutputStream() {
return closeOutputStream;
}
Expand All @@ -370,8 +374,8 @@ public void setCloseOutputStream(boolean closeOutputStream) {
setFrameNumber((int)Math.round(timestamp * getFrameRate() / 1000000L));
}

public void start(AVFormatContext ifmt_ctx) throws Exception {
this.ifmt_ctx = ifmt_ctx;
public void start(AVFormatContext inputFormatContext) throws Exception {
this.ifmt_ctx = inputFormatContext;
start();
}

Expand Down Expand Up @@ -862,12 +866,18 @@ public void startUnsafe() throws Exception {
av_dict_set(metadata, e.getKey(), e.getValue(), 0);
}
/* write the stream header, if any */
avformat_write_header(oc.metadata(metadata), options);
if ((ret = avformat_write_header(oc.metadata(metadata), options)) < 0) {
releaseUnsafe();
av_dict_free(options);
throw new Exception("avformat_write_header error() error " + ret + ": Could not write header to '" + filename + "'");
}
av_dict_free(options);

if (av_log_get_level() >= AV_LOG_INFO) {
av_dump_format(oc, 0, filename, 1);
}

started = true;
}

public void flush() throws Exception {
Expand Down Expand Up @@ -918,6 +928,9 @@ public boolean recordImage(int width, int height, int depth, int channels, int s
if (video_st == null) {
throw new Exception("No video output stream (Is imageWidth > 0 && imageHeight > 0 and has start() been called?)");
}
if (!started) {
throw new Exception("start() was not called successfully!");
}
int ret;

if (image == null || image.length == 0) {
Expand Down Expand Up @@ -1026,6 +1039,9 @@ public boolean recordSamples(int sampleRate, int audioChannels, Buffer ... sampl
if (audio_st == null) {
throw new Exception("No audio output stream (Is audioChannels > 0 and has start() been called?)");
}
if (!started) {
throw new Exception("start() was not called successfully!");
}

if (samples == null && samples_out[0].position() > 0) {
// Typically samples_out[0].limit() is double the audio_input_frame_size --> sampleDivisor = 2
Expand Down Expand Up @@ -1230,6 +1246,12 @@ private void writePacket(int mediaType, AVPacket avPacket) throws Exception {
}

public boolean recordPacket(AVPacket pkt) throws Exception {
if (ifmt_ctx == null) {
throw new Exception("No input format context (Has start(AVFormatContext) been called?)");
}
if (!started) {
throw new Exception("start() was not called successfully!");
}

if (pkt == null) {
return false;
Expand Down

0 comments on commit 05ad9ab

Please sign in to comment.