-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
FFmpegStreamingTimeout.java
148 lines (135 loc) · 5.39 KB
/
FFmpegStreamingTimeout.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.ffmpeg.avformat.*;
import static org.bytedeco.ffmpeg.global.avformat.*;
/**
*
* @author Dmitriy Gerashenko <d.a.gerashenko@gmail.com>
*/
public class FFmpegStreamingTimeout {
/**
* There is no universal option for streaming timeout. Each of protocols has
* its own list of options.
*/
private static enum TimeoutOption {
/**
* Depends on protocol (FTP, HTTP, RTMP, RTSP, SMB, SSH, TCP, UDP, or UNIX).
* http://ffmpeg.org/ffmpeg-all.html
*
* Specific for RTSP:
* Set socket TCP I/O timeout in microseconds.
* http://ffmpeg.org/ffmpeg-all.html#rtsp
*/
TIMEOUT,
/**
* Protocols
*
* Maximum time to wait for (network) read/write operations to complete,
* in microseconds.
*
* http://ffmpeg.org/ffmpeg-all.html#Protocols
*/
RW_TIMEOUT;
public String getKey() {
return toString().toLowerCase();
}
}
private static final String SOURCE_RTSP = "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov";
private static final int TIMEOUT = 10; // In seconds.
public static void main(String[] args) {
rtspStreamingTest();
// testWithCallback(); // This is not working properly. It's just for test.
}
private static void rtspStreamingTest() {
try {
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(SOURCE_RTSP);
/**
* "rw_timeout" - IS IGNORED when a network cable have been
* unplugged before a connection but the option takes effect after a
* connection was established.
*
* "timeout" - works fine.
*/
grabber.setOption(
TimeoutOption.TIMEOUT.getKey(),
String.valueOf(TIMEOUT * 1000000)
); // In microseconds.
grabber.start();
Frame frame = null;
/**
* When network is disabled (before grabber was started) grabber
* throws exception: "org.bytedeco.javacv.FrameGrabber$Exception:
* avformat_open_input() error -138: Could not open input...".
*
* When connections is lost (after a few grabbed frames)
* grabber.grab() returns null without exception.
*/
while ((frame = grabber.grab()) != null) {
System.out.println("frame grabbed at " + grabber.getTimestamp());
}
System.out.println("loop end with frame: " + frame);
} catch (FrameGrabber.Exception ex) {
System.out.println("exception: " + ex);
}
System.out.println("end");
}
private static void testWithCallback() {
try {
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(SOURCE_RTSP);
/**
* grabber.getFormatContext() is null before grabber.start().
*
* But if network is disabled grabber.start() will never return.
*
* That's why interrupt_callback not suitable for "network disabled
* case".
*/
grabber.start();
final AtomicBoolean interruptFlag = new AtomicBoolean(false);
AVIOInterruptCB.Callback_Pointer cp = new AVIOInterruptCB.Callback_Pointer() {
@Override
public int call(Pointer pointer) {
// 0 - continue, 1 - exit
int interruptFlagInt = interruptFlag.get() ? 1 : 0;
System.out.println("callback, interrupt flag == " + interruptFlagInt);
return interruptFlagInt;
}
};
AVFormatContext oc = grabber.getFormatContext();
avformat_alloc_context();
AVIOInterruptCB cb = new AVIOInterruptCB();
cb.callback(cp);
oc.interrupt_callback(cb);
new Thread(new Runnable() { public void run() {
try {
TimeUnit.SECONDS.sleep(TIMEOUT);
interruptFlag.set(true);
System.out.println("interrupt flag was changed");
} catch (InterruptedException ex) {
System.out.println("exception in interruption thread: " + ex);
}
}}).start();
Frame frame = null;
/**
* On one of my RTSP cams grabber stops calling callback on
* connection lost. I think it's has something to do with message:
* "[swscaler @ 0000000029af49e0] deprecated pixel format used, make
* sure you did set range correctly".
*
* So there is at least one case when grabber stops calling
* callback.
*/
while ((frame = grabber.grab()) != null) {
System.out.println("frame grabbed at " + grabber.getTimestamp());
}
System.out.println("loop end with frame: " + frame);
} catch (FrameGrabber.Exception ex) {
System.out.println("exception: " + ex);
}
System.out.println("end");
}
}