Skip to content

Commit dbf43d0

Browse files
author
Noel Algora
committed
libstreaming added
1 parent d31a3c1 commit dbf43d0

38 files changed

Lines changed: 9569 additions & 0 deletions
Lines changed: 393 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,393 @@
1+
/*
2+
* Copyright (C) 2011-2015 GUIGUI Simon, fyhertz@gmail.com
3+
*
4+
* This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package d2d.testing.streaming;
20+
21+
import java.io.IOException;
22+
import java.io.OutputStream;
23+
import java.net.InetAddress;
24+
import java.util.Random;
25+
import net.majorkernelpanic.streaming.audio.AudioStream;
26+
import net.majorkernelpanic.streaming.rtp.AbstractPacketizer;
27+
import net.majorkernelpanic.streaming.video.VideoStream;
28+
import android.annotation.SuppressLint;
29+
import android.media.MediaCodec;
30+
import android.media.MediaRecorder;
31+
import android.net.LocalServerSocket;
32+
import android.net.LocalSocket;
33+
import android.net.LocalSocketAddress;
34+
import android.os.Build;
35+
import android.os.ParcelFileDescriptor;
36+
import android.util.Log;
37+
38+
/**
39+
* A MediaRecorder that streams what it records using a packetizer from the RTP package.
40+
* You can't use this class directly !
41+
*/
42+
public abstract class MediaStream implements Stream {
43+
44+
protected static final String TAG = "MediaStream";
45+
46+
/** Raw audio/video will be encoded using the MediaRecorder API. */
47+
public static final byte MODE_MEDIARECORDER_API = 0x01;
48+
49+
/** Raw audio/video will be encoded using the MediaCodec API with buffers. */
50+
public static final byte MODE_MEDIACODEC_API = 0x02;
51+
52+
/** Raw audio/video will be encoded using the MediaCode API with a surface. */
53+
public static final byte MODE_MEDIACODEC_API_2 = 0x05;
54+
55+
/** A LocalSocket will be used to feed the MediaRecorder object */
56+
public static final byte PIPE_API_LS = 0x01;
57+
58+
/** A ParcelFileDescriptor will be used to feed the MediaRecorder object */
59+
public static final byte PIPE_API_PFD = 0x02;
60+
61+
/** Prefix that will be used for all shared preferences saved by libstreaming */
62+
protected static final String PREF_PREFIX = "libstreaming-";
63+
64+
/** The packetizer that will read the output of the camera and send RTP packets over the networked. */
65+
protected AbstractPacketizer mPacketizer = null;
66+
67+
protected static byte sSuggestedMode = MODE_MEDIARECORDER_API;
68+
protected byte mMode, mRequestedMode;
69+
70+
/**
71+
* Starting lollipop the LocalSocket API cannot be used to feed a MediaRecorder object.
72+
* You can force what API to use to create the pipe that feeds it with this constant
73+
* by using {@link #PIPE_API_LS} and {@link #PIPE_API_PFD}.
74+
*/
75+
protected final static byte sPipeApi;
76+
77+
protected boolean mStreaming = false, mConfigured = false;
78+
protected int mRtpPort = 0, mRtcpPort = 0;
79+
protected byte mChannelIdentifier = 0;
80+
protected OutputStream mOutputStream = null;
81+
protected InetAddress mDestination;
82+
83+
protected ParcelFileDescriptor[] mParcelFileDescriptors;
84+
protected ParcelFileDescriptor mParcelRead;
85+
protected ParcelFileDescriptor mParcelWrite;
86+
87+
protected LocalSocket mReceiver, mSender = null;
88+
private LocalServerSocket mLss = null;
89+
private int mSocketId;
90+
91+
private int mTTL = 64;
92+
93+
protected MediaRecorder mMediaRecorder;
94+
protected MediaCodec mMediaCodec;
95+
96+
static {
97+
// We determine whether or not the MediaCodec API should be used
98+
try {
99+
Class.forName("android.media.MediaCodec");
100+
// Will be set to MODE_MEDIACODEC_API at some point...
101+
sSuggestedMode = MODE_MEDIACODEC_API;
102+
Log.i(TAG,"Phone supports the MediaCoded API");
103+
} catch (ClassNotFoundException e) {
104+
sSuggestedMode = MODE_MEDIARECORDER_API;
105+
Log.i(TAG,"Phone does not support the MediaCodec API");
106+
}
107+
108+
// Starting lollipop, the LocalSocket API cannot be used anymore to feed
109+
// a MediaRecorder object for security reasons
110+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
111+
sPipeApi = PIPE_API_PFD;
112+
} else {
113+
sPipeApi = PIPE_API_LS;
114+
}
115+
}
116+
117+
public MediaStream() {
118+
mRequestedMode = sSuggestedMode;
119+
mMode = sSuggestedMode;
120+
}
121+
122+
/**
123+
* Sets the destination IP address of the stream.
124+
* @param dest The destination address of the stream
125+
*/
126+
public void setDestinationAddress(InetAddress dest) {
127+
mDestination = dest;
128+
}
129+
130+
/**
131+
* Sets the destination ports of the stream.
132+
* If an odd number is supplied for the destination port then the next
133+
* lower even number will be used for RTP and it will be used for RTCP.
134+
* If an even number is supplied, it will be used for RTP and the next odd
135+
* number will be used for RTCP.
136+
* @param dport The destination port
137+
*/
138+
public void setDestinationPorts(int dport) {
139+
if (dport % 2 == 1) {
140+
mRtpPort = dport-1;
141+
mRtcpPort = dport;
142+
} else {
143+
mRtpPort = dport;
144+
mRtcpPort = dport+1;
145+
}
146+
}
147+
148+
/**
149+
* Sets the destination ports of the stream.
150+
* @param rtpPort Destination port that will be used for RTP
151+
* @param rtcpPort Destination port that will be used for RTCP
152+
*/
153+
public void setDestinationPorts(int rtpPort, int rtcpPort) {
154+
mRtpPort = rtpPort;
155+
mRtcpPort = rtcpPort;
156+
mOutputStream = null;
157+
}
158+
159+
/**
160+
* If a TCP is used as the transport protocol for the RTP session,
161+
* the output stream to which RTP packets will be written to must
162+
* be specified with this method.
163+
*/
164+
public void setOutputStream(OutputStream stream, byte channelIdentifier) {
165+
mOutputStream = stream;
166+
mChannelIdentifier = channelIdentifier;
167+
}
168+
169+
170+
/**
171+
* Sets the Time To Live of packets sent over the network.
172+
* @param ttl The time to live
173+
* @throws IOException
174+
*/
175+
public void setTimeToLive(int ttl) throws IOException {
176+
mTTL = ttl;
177+
}
178+
179+
/**
180+
* Returns a pair of destination ports, the first one is the
181+
* one used for RTP and the second one is used for RTCP.
182+
**/
183+
public int[] getDestinationPorts() {
184+
return new int[] {
185+
mRtpPort,
186+
mRtcpPort
187+
};
188+
}
189+
190+
/**
191+
* Returns a pair of source ports, the first one is the
192+
* one used for RTP and the second one is used for RTCP.
193+
**/
194+
public int[] getLocalPorts() {
195+
return mPacketizer.getRtpSocket().getLocalPorts();
196+
}
197+
198+
/**
199+
* Sets the streaming method that will be used.
200+
*
201+
* If the mode is set to {@link #MODE_MEDIARECORDER_API}, raw audio/video will be encoded
202+
* using the MediaRecorder API. <br />
203+
*
204+
* If the mode is set to {@link #MODE_MEDIACODEC_API} or to {@link #MODE_MEDIACODEC_API_2},
205+
* audio/video will be encoded with using the MediaCodec. <br />
206+
*
207+
* The {@link #MODE_MEDIACODEC_API_2} mode only concerns {@link VideoStream}, it makes
208+
* use of the createInputSurface() method of the MediaCodec API (Android 4.3 is needed there). <br />
209+
*
210+
* @param mode Can be {@link #MODE_MEDIARECORDER_API}, {@link #MODE_MEDIACODEC_API} or {@link #MODE_MEDIACODEC_API_2}
211+
*/
212+
public void setStreamingMethod(byte mode) {
213+
mRequestedMode = mode;
214+
}
215+
216+
/**
217+
* Returns the streaming method in use, call this after
218+
* {@link #configure()} to get an accurate response.
219+
*/
220+
public byte getStreamingMethod() {
221+
return mMode;
222+
}
223+
224+
/**
225+
* Returns the packetizer associated with the {@link MediaStream}.
226+
* @return The packetizer
227+
*/
228+
public AbstractPacketizer getPacketizer() {
229+
return mPacketizer;
230+
}
231+
232+
/**
233+
* Returns an approximation of the bit rate consumed by the stream in bit per seconde.
234+
*/
235+
public long getBitrate() {
236+
return !mStreaming ? 0 : mPacketizer.getRtpSocket().getBitrate();
237+
}
238+
239+
/**
240+
* Indicates if the {@link MediaStream} is streaming.
241+
* @return A boolean indicating if the {@link MediaStream} is streaming
242+
*/
243+
public boolean isStreaming() {
244+
return mStreaming;
245+
}
246+
247+
/**
248+
* Configures the stream with the settings supplied with
249+
* {@link VideoStream#setVideoQuality(net.majorkernelpanic.streaming.video.VideoQuality)}
250+
* for a {@link VideoStream} and {@link AudioStream#setAudioQuality(net.majorkernelpanic.streaming.audio.AudioQuality)}
251+
* for a {@link AudioStream}.
252+
*/
253+
public synchronized void configure() throws IllegalStateException, IOException {
254+
if (mStreaming) throw new IllegalStateException("Can't be called while streaming.");
255+
if (mPacketizer != null) {
256+
mPacketizer.setDestination(mDestination, mRtpPort, mRtcpPort);
257+
mPacketizer.getRtpSocket().setOutputStream(mOutputStream, mChannelIdentifier);
258+
}
259+
mMode = mRequestedMode;
260+
mConfigured = true;
261+
}
262+
263+
/** Starts the stream. */
264+
public synchronized void start() throws IllegalStateException, IOException {
265+
266+
if (mDestination==null)
267+
throw new IllegalStateException("No destination ip address set for the stream !");
268+
269+
if (mRtpPort<=0 || mRtcpPort<=0)
270+
throw new IllegalStateException("No destination ports set for the stream !");
271+
272+
mPacketizer.setTimeToLive(mTTL);
273+
274+
if (mMode != MODE_MEDIARECORDER_API) {
275+
encodeWithMediaCodec();
276+
} else {
277+
encodeWithMediaRecorder();
278+
}
279+
280+
}
281+
282+
/** Stops the stream. */
283+
@SuppressLint("NewApi")
284+
public synchronized void stop() {
285+
if (mStreaming) {
286+
try {
287+
if (mMode==MODE_MEDIARECORDER_API) {
288+
mMediaRecorder.stop();
289+
mMediaRecorder.release();
290+
mMediaRecorder = null;
291+
closeSockets();
292+
mPacketizer.stop();
293+
} else {
294+
mPacketizer.stop();
295+
mMediaCodec.stop();
296+
mMediaCodec.release();
297+
mMediaCodec = null;
298+
}
299+
} catch (Exception e) {
300+
e.printStackTrace();
301+
}
302+
mStreaming = false;
303+
}
304+
}
305+
306+
protected abstract void encodeWithMediaRecorder() throws IOException;
307+
308+
protected abstract void encodeWithMediaCodec() throws IOException;
309+
310+
/**
311+
* Returns a description of the stream using SDP.
312+
* This method can only be called after {@link Stream#configure()}.
313+
* @throws IllegalStateException Thrown when {@link Stream#configure()} was not called.
314+
*/
315+
public abstract String getSessionDescription();
316+
317+
/**
318+
* Returns the SSRC of the underlying {@link net.majorkernelpanic.streaming.rtp.RtpSocket}.
319+
* @return the SSRC of the stream
320+
*/
321+
public int getSSRC() {
322+
return getPacketizer().getSSRC();
323+
}
324+
325+
protected void createSockets() throws IOException {
326+
327+
if (sPipeApi == PIPE_API_LS) {
328+
329+
final String LOCAL_ADDR = "net.majorkernelpanic.streaming-";
330+
331+
for (int i=0;i<10;i++) {
332+
try {
333+
mSocketId = new Random().nextInt();
334+
mLss = new LocalServerSocket(LOCAL_ADDR+mSocketId);
335+
break;
336+
} catch (IOException e1) {}
337+
}
338+
339+
mReceiver = new LocalSocket();
340+
mReceiver.connect( new LocalSocketAddress(LOCAL_ADDR+mSocketId));
341+
mReceiver.setReceiveBufferSize(500000);
342+
mReceiver.setSoTimeout(3000);
343+
mSender = mLss.accept();
344+
mSender.setSendBufferSize(500000);
345+
346+
} else {
347+
Log.e(TAG, "parcelFileDescriptors createPipe version = Lollipop");
348+
mParcelFileDescriptors = ParcelFileDescriptor.createPipe();
349+
mParcelRead = new ParcelFileDescriptor(mParcelFileDescriptors[0]);
350+
mParcelWrite = new ParcelFileDescriptor(mParcelFileDescriptors[1]);
351+
}
352+
}
353+
354+
protected void closeSockets() {
355+
if (sPipeApi == PIPE_API_LS) {
356+
try {
357+
mReceiver.close();
358+
} catch (Exception e) {
359+
e.printStackTrace();
360+
}
361+
try {
362+
mSender.close();
363+
} catch (Exception e) {
364+
e.printStackTrace();
365+
}
366+
try {
367+
mLss.close();
368+
} catch (Exception e) {
369+
e.printStackTrace();
370+
}
371+
mLss = null;
372+
mSender = null;
373+
mReceiver = null;
374+
375+
} else {
376+
try {
377+
if (mParcelRead != null) {
378+
mParcelRead.close();
379+
}
380+
} catch (Exception e) {
381+
e.printStackTrace();
382+
}
383+
try {
384+
if (mParcelWrite != null) {
385+
mParcelWrite.close();
386+
}
387+
} catch (Exception e) {
388+
e.printStackTrace();
389+
}
390+
}
391+
}
392+
393+
}

0 commit comments

Comments
 (0)