The purpose of this class is to detect and by-pass some bugs (or underspecified configuration) that
85 | encoders available through the MediaCodec API may have.
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 4.0.0
5 |
6 | net.majorkernelpanic
7 | libstreaming
8 | libstreaming
9 | 3.0
10 | apklib
11 | libstreaming is an API that allows you, with only a few lines of code, to stream the camera and/or microphone of an android powered device using RTP over UDP.
12 | https://github.com/fyhertz/libstreaming
13 | 2013
14 |
15 |
16 | https://github.com/fyhertz/libstreaming
17 | scm:git:git://github.com/fyhertz/libstreaming
18 |
19 |
20 |
21 |
22 | fyhertz
23 | fyhertz@gmail.com
24 | fyhertz
25 |
26 | developer
27 |
28 |
29 |
30 |
31 |
32 |
33 | GNU General Public License Version 3
34 | http://www.gnu.org/licenses/gpl.html
35 | repo
36 |
37 |
38 |
39 |
40 | GitHub Issues
41 | https://github.com/fyhertz/libstreaming/issues
42 |
43 |
44 |
45 |
46 | com.google.android
47 | android
48 | 4.3_r2
49 | provided
50 |
51 |
52 |
53 |
54 | src
55 | test
56 |
57 |
58 |
59 | com.jayway.maven.plugins.android.generation2
60 | android-maven-plugin
61 |
62 |
63 | 18
64 |
65 |
66 | true
67 |
68 |
69 |
70 | org.apache.maven.plugins
71 | maven-javadoc-plugin
72 |
73 | true
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-21
12 | android.library=true
13 |
--------------------------------------------------------------------------------
/res/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ziggeo/android-libstreaming/17e3499f3aa8182c713198d2d8d0ebf6be4d8893/res/.gitkeep
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/Stream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming;
22 |
23 | import java.io.IOException;
24 | import java.io.OutputStream;
25 | import java.net.InetAddress;
26 |
27 | /**
28 | * An interface that represents a Stream.
29 | */
30 | public interface Stream {
31 |
32 | /**
33 | * Configures the stream. You need to call this before calling {@link #getSessionDescription()}
34 | * to apply your configuration of the stream.
35 | */
36 | public void configure() throws IllegalStateException, IOException;
37 |
38 | /**
39 | * Starts the stream.
40 | * This method can only be called after {@link Stream#configure()}.
41 | */
42 | public void start() throws IllegalStateException, IOException;
43 |
44 | /**
45 | * Stops the stream.
46 | */
47 | public void stop();
48 |
49 | /**
50 | * Sets the Time To Live of packets sent over the network.
51 | * @param ttl The time to live
52 | * @throws IOException
53 | */
54 | public void setTimeToLive(int ttl) throws IOException;
55 |
56 | /**
57 | * Sets the destination ip address of the stream.
58 | * @param dest The destination address of the stream
59 | */
60 | public void setDestinationAddress(InetAddress dest);
61 |
62 | /**
63 | * Sets the destination ports of the stream.
64 | * If an odd number is supplied for the destination port then the next
65 | * lower even number will be used for RTP and it will be used for RTCP.
66 | * If an even number is supplied, it will be used for RTP and the next odd
67 | * number will be used for RTCP.
68 | * @param dport The destination port
69 | */
70 | public void setDestinationPorts(int dport);
71 |
72 | /**
73 | * Sets the destination ports of the stream.
74 | * @param rtpPort Destination port that will be used for RTP
75 | * @param rtcpPort Destination port that will be used for RTCP
76 | */
77 | public void setDestinationPorts(int rtpPort, int rtcpPort);
78 |
79 | /**
80 | * If a TCP is used as the transport protocol for the RTP session,
81 | * the output stream to which RTP packets will be written to must
82 | * be specified with this method.
83 | */
84 | public void setOutputStream(OutputStream stream, byte channelIdentifier);
85 |
86 | /**
87 | * Returns a pair of source ports, the first one is the
88 | * one used for RTP and the second one is used for RTCP.
89 | **/
90 | public int[] getLocalPorts();
91 |
92 | /**
93 | * Returns a pair of destination ports, the first one is the
94 | * one used for RTP and the second one is used for RTCP.
95 | **/
96 | public int[] getDestinationPorts();
97 |
98 |
99 | /**
100 | * Returns the SSRC of the underlying {@link net.majorkernelpanic.streaming.rtp.RtpSocket}.
101 | * @return the SSRC of the stream.
102 | */
103 | public int getSSRC();
104 |
105 | /**
106 | * Returns an approximation of the bit rate consumed by the stream in bit per seconde.
107 | */
108 | public long getBitrate();
109 |
110 | /**
111 | * Returns a description of the stream using SDP.
112 | * This method can only be called after {@link Stream#configure()}.
113 | * @throws IllegalStateException Thrown when {@link Stream#configure()} wa not called.
114 | */
115 | public String getSessionDescription() throws IllegalStateException;
116 |
117 | public boolean isStreaming();
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/audio/AMRNBStream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.audio;
22 |
23 | import java.io.IOException;
24 | import java.lang.reflect.Field;
25 |
26 | import net.majorkernelpanic.streaming.SessionBuilder;
27 | import net.majorkernelpanic.streaming.rtp.AMRNBPacketizer;
28 | import android.media.MediaRecorder;
29 | import android.service.textservice.SpellCheckerService.Session;
30 |
31 | /**
32 | * A class for streaming AAC from the camera of an android device using RTP.
33 | * You should use a {@link Session} instantiated with {@link SessionBuilder} instead of using this class directly.
34 | * Call {@link #setDestinationAddress(InetAddress)}, {@link #setDestinationPorts(int)} and {@link #setAudioQuality(AudioQuality)}
35 | * to configure the stream. You can then call {@link #start()} to start the RTP stream.
36 | * Call {@link #stop()} to stop the stream.
37 | */
38 | public class AMRNBStream extends AudioStream {
39 |
40 | public AMRNBStream() {
41 | super();
42 |
43 | mPacketizer = new AMRNBPacketizer();
44 |
45 | setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
46 |
47 | try {
48 | // RAW_AMR was deprecated in API level 16.
49 | Field deprecatedName = MediaRecorder.OutputFormat.class.getField("RAW_AMR");
50 | setOutputFormat(deprecatedName.getInt(null));
51 | } catch (Exception e) {
52 | setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
53 | }
54 |
55 | setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
56 |
57 | }
58 |
59 | /**
60 | * Starts the stream.
61 | */
62 | public synchronized void start() throws IllegalStateException, IOException {
63 | if (!mStreaming) {
64 | configure();
65 | super.start();
66 | }
67 | }
68 |
69 | public synchronized void configure() throws IllegalStateException, IOException {
70 | super.configure();
71 | mMode = MODE_MEDIARECORDER_API;
72 | mQuality = mRequestedQuality.clone();
73 | }
74 |
75 | /**
76 | * Returns a description of the stream using SDP. It can then be included in an SDP file.
77 | */
78 | public String getSessionDescription() {
79 | return "m=audio "+String.valueOf(getDestinationPorts()[0])+" RTP/AVP 96\r\n" +
80 | "a=rtpmap:96 AMR/8000\r\n" +
81 | "a=fmtp:96 octet-align=1;\r\n";
82 | }
83 |
84 | @Override
85 | protected void encodeWithMediaCodec() throws IOException {
86 | super.encodeWithMediaRecorder();
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/audio/AudioQuality.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.audio;
22 |
23 | /**
24 | * A class that represents the quality of an audio stream.
25 | */
26 | public class AudioQuality {
27 |
28 | /** Default audio stream quality. */
29 | public final static AudioQuality DEFAULT_AUDIO_QUALITY = new AudioQuality(8000,32000);
30 |
31 | /** Represents a quality for a video stream. */
32 | public AudioQuality() {}
33 |
34 | /**
35 | * Represents a quality for an audio stream.
36 | * @param samplingRate The sampling rate
37 | * @param bitRate The bitrate in bit per seconds
38 | */
39 | public AudioQuality(int samplingRate, int bitRate) {
40 | this.samplingRate = samplingRate;
41 | this.bitRate = bitRate;
42 | }
43 |
44 | public int samplingRate = 0;
45 | public int bitRate = 0;
46 |
47 | public boolean equals(AudioQuality quality) {
48 | if (quality==null) return false;
49 | return (quality.samplingRate == this.samplingRate &
50 | quality.bitRate == this.bitRate);
51 | }
52 |
53 | public AudioQuality clone() {
54 | return new AudioQuality(samplingRate, bitRate);
55 | }
56 |
57 | public static AudioQuality parseQuality(String str) {
58 | AudioQuality quality = DEFAULT_AUDIO_QUALITY.clone();
59 | if (str != null) {
60 | String[] config = str.split("-");
61 | try {
62 | quality.bitRate = Integer.parseInt(config[0])*1000; // conversion to bit/s
63 | quality.samplingRate = Integer.parseInt(config[1]);
64 | }
65 | catch (IndexOutOfBoundsException ignore) {}
66 | }
67 | return quality;
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/audio/AudioStream.java:
--------------------------------------------------------------------------------
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 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.audio;
22 |
23 | import java.io.FileDescriptor;
24 | import java.io.IOException;
25 | import java.io.InputStream;
26 |
27 | import net.majorkernelpanic.streaming.MediaStream;
28 | import android.media.MediaRecorder;
29 | import android.os.ParcelFileDescriptor;
30 | import android.util.Log;
31 |
32 | /**
33 | * Don't use this class directly.
34 | */
35 | public abstract class AudioStream extends MediaStream {
36 |
37 | protected int mAudioSource;
38 | protected int mOutputFormat;
39 | protected int mAudioEncoder;
40 | protected AudioQuality mRequestedQuality = AudioQuality.DEFAULT_AUDIO_QUALITY.clone();
41 | protected AudioQuality mQuality = mRequestedQuality.clone();
42 |
43 | public AudioStream() {
44 | setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
45 | }
46 |
47 | public void setAudioSource(int audioSource) {
48 | mAudioSource = audioSource;
49 | }
50 |
51 | public void setAudioQuality(AudioQuality quality) {
52 | mRequestedQuality = quality;
53 | }
54 |
55 | /**
56 | * Returns the quality of the stream.
57 | */
58 | public AudioQuality getAudioQuality() {
59 | return mQuality;
60 | }
61 |
62 | protected void setAudioEncoder(int audioEncoder) {
63 | mAudioEncoder = audioEncoder;
64 | }
65 |
66 | protected void setOutputFormat(int outputFormat) {
67 | mOutputFormat = outputFormat;
68 | }
69 |
70 | @Override
71 | protected void encodeWithMediaRecorder() throws IOException {
72 |
73 | // We need a local socket to forward data output by the camera to the packetizer
74 | createSockets();
75 |
76 | Log.v(TAG,"Requested audio with "+mQuality.bitRate/1000+"kbps"+" at "+mQuality.samplingRate/1000+"kHz");
77 |
78 | mMediaRecorder = new MediaRecorder();
79 | mMediaRecorder.setAudioSource(mAudioSource);
80 | mMediaRecorder.setOutputFormat(mOutputFormat);
81 | mMediaRecorder.setAudioEncoder(mAudioEncoder);
82 | mMediaRecorder.setAudioChannels(1);
83 | mMediaRecorder.setAudioSamplingRate(mQuality.samplingRate);
84 | mMediaRecorder.setAudioEncodingBitRate(mQuality.bitRate);
85 |
86 | // We write the output of the camera in a local socket instead of a file !
87 | // This one little trick makes streaming feasible quiet simply: data from the camera
88 | // can then be manipulated at the other end of the socket
89 | FileDescriptor fd = null;
90 | if (sPipeApi == PIPE_API_PFD) {
91 | fd = mParcelWrite.getFileDescriptor();
92 | } else {
93 | fd = mSender.getFileDescriptor();
94 | }
95 | mMediaRecorder.setOutputFile(fd);
96 | mMediaRecorder.setOutputFile(fd);
97 |
98 | mMediaRecorder.prepare();
99 | mMediaRecorder.start();
100 |
101 | InputStream is = null;
102 |
103 | if (sPipeApi == PIPE_API_PFD) {
104 | is = new ParcelFileDescriptor.AutoCloseInputStream(mParcelRead);
105 | } else {
106 | try {
107 | // mReceiver.getInputStream contains the data from the camera
108 | is = mReceiver.getInputStream();
109 | } catch (IOException e) {
110 | stop();
111 | throw new IOException("Something happened with the local sockets :/ Start failed !");
112 | }
113 | }
114 |
115 | // the mPacketizer encapsulates this stream in an RTP stream and send it over the network
116 | mPacketizer.setInputStream(is);
117 | mPacketizer.start();
118 | mStreaming = true;
119 |
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/exceptions/CameraInUseException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.exceptions;
22 |
23 | public class CameraInUseException extends RuntimeException {
24 |
25 | public CameraInUseException(String message) {
26 | super(message);
27 | }
28 |
29 | private static final long serialVersionUID = -1866132102949435675L;
30 | }
31 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/exceptions/ConfNotSupportedException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.exceptions;
22 |
23 | public class ConfNotSupportedException extends RuntimeException {
24 |
25 | public ConfNotSupportedException(String message) {
26 | super(message);
27 | }
28 |
29 | private static final long serialVersionUID = 5876298277802827615L;
30 | }
31 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/exceptions/InvalidSurfaceException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.exceptions;
22 |
23 | public class InvalidSurfaceException extends RuntimeException {
24 |
25 | private static final long serialVersionUID = -7238661340093544496L;
26 |
27 | public InvalidSurfaceException(String message) {
28 | super(message);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/exceptions/StorageUnavailableException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.exceptions;
22 |
23 | import java.io.IOException;
24 |
25 | public class StorageUnavailableException extends IOException {
26 |
27 | public StorageUnavailableException(String message) {
28 | super(message);
29 | }
30 |
31 | private static final long serialVersionUID = -7537890350373995089L;
32 | }
33 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/hw/CodecManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.hw;
22 |
23 | import java.util.ArrayList;
24 | import java.util.HashSet;
25 | import java.util.Set;
26 |
27 | import android.annotation.SuppressLint;
28 | import android.media.MediaCodecInfo;
29 | import android.media.MediaCodecList;
30 | import android.util.Log;
31 |
32 | @SuppressLint("InlinedApi")
33 | public class CodecManager {
34 |
35 | public final static String TAG = "CodecManager";
36 |
37 | public static final int[] SUPPORTED_COLOR_FORMATS = {
38 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
39 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar,
40 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
41 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar,
42 | MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar
43 | };
44 |
45 | private static Codec[] sEncoders = null;
46 | private static Codec[] sDecoders = null;
47 |
48 | static class Codec {
49 | public Codec(String name, Integer[] formats) {
50 | this.name = name;
51 | this.formats = formats;
52 | }
53 | public String name;
54 | public Integer[] formats;
55 | }
56 |
57 | /**
58 | * Lists all encoders that claim to support a color format that we know how to use.
59 | * @return A list of those encoders
60 | */
61 | @SuppressLint("NewApi")
62 | public synchronized static Codec[] findEncodersForMimeType(String mimeType) {
63 | if (sEncoders != null) return sEncoders;
64 |
65 | ArrayList encoders = new ArrayList();
66 |
67 | // We loop through the encoders, apparently this can take up to a sec (testes on a GS3)
68 | for(int j = MediaCodecList.getCodecCount() - 1; j >= 0; j--){
69 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(j);
70 | if (!codecInfo.isEncoder()) continue;
71 |
72 | String[] types = codecInfo.getSupportedTypes();
73 | for (int i = 0; i < types.length; i++) {
74 | if (types[i].equalsIgnoreCase(mimeType)) {
75 | try {
76 | MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
77 | Set formats = new HashSet();
78 |
79 | // And through the color formats supported
80 | for (int k = 0; k < capabilities.colorFormats.length; k++) {
81 | int format = capabilities.colorFormats[k];
82 |
83 | for (int l=0;l decoders = new ArrayList();
112 |
113 | // We loop through the decoders, apparently this can take up to a sec (testes on a GS3)
114 | for(int j = MediaCodecList.getCodecCount() - 1; j >= 0; j--){
115 | MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(j);
116 | if (codecInfo.isEncoder()) continue;
117 |
118 | String[] types = codecInfo.getSupportedTypes();
119 | for (int i = 0; i < types.length; i++) {
120 | if (types[i].equalsIgnoreCase(mimeType)) {
121 | try {
122 | MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
123 | Set formats = new HashSet();
124 |
125 | // And through the color formats supported
126 | for (int k = 0; k < capabilities.colorFormats.length; k++) {
127 | int format = capabilities.colorFormats[k];
128 |
129 | for (int l=0;l0) {
132 | System.arraycopy(data, 0, mBuffer, 0, mSize);
133 | System.arraycopy(data, mSize, mBuffer, mSize+mYPadding, mSize/2);
134 | return mBuffer;
135 | }
136 | return data;
137 | }
138 | } else {
139 | if (mSliceHeight==mHeight && mStride==mWidth) {
140 | // De-interleave U and V
141 | if (!mPanesReversed) {
142 | for (int i = 0; i < mSize/4; i+=1) {
143 | mBuffer[i] = data[mSize+2*i+1];
144 | mBuffer[mSize/4+i] = data[mSize+2*i];
145 | }
146 | } else {
147 | for (int i = 0; i < mSize/4; i+=1) {
148 | mBuffer[i] = data[mSize+2*i];
149 | mBuffer[mSize/4+i] = data[mSize+2*i+1];
150 | }
151 | }
152 | if (mYPadding == 0) {
153 | System.arraycopy(mBuffer, 0, data, mSize, mSize/2);
154 | } else {
155 | System.arraycopy(data, 0, mBuffer, 0, mSize);
156 | System.arraycopy(mBuffer, 0, mBuffer, mSize+mYPadding, mSize/2);
157 | return mBuffer;
158 | }
159 | return data;
160 | }
161 | }
162 |
163 | return data;
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/mp4/MP4Config.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.mp4;
22 | import java.io.FileNotFoundException;
23 | import java.io.IOException;
24 |
25 | import android.util.Base64;
26 | import android.util.Log;
27 |
28 | /**
29 | * Finds SPS & PPS parameters in mp4 file.
30 | */
31 | public class MP4Config {
32 |
33 | public final static String TAG = "MP4Config";
34 |
35 | private MP4Parser mp4Parser;
36 | private String mProfilLevel, mPPS, mSPS;
37 |
38 | public MP4Config(String profil, String sps, String pps) {
39 | mProfilLevel = profil;
40 | mPPS = pps;
41 | mSPS = sps;
42 | }
43 |
44 | public MP4Config(String sps, String pps) {
45 | mPPS = pps;
46 | mSPS = sps;
47 | mProfilLevel = MP4Parser.toHexString(Base64.decode(sps, Base64.NO_WRAP),1,3);
48 | }
49 |
50 | public MP4Config(byte[] sps, byte[] pps) {
51 | mPPS = Base64.encodeToString(pps, 0, pps.length, Base64.NO_WRAP);
52 | mSPS = Base64.encodeToString(sps, 0, sps.length, Base64.NO_WRAP);
53 | mProfilLevel = MP4Parser.toHexString(sps,1,3);
54 | }
55 |
56 | /**
57 | * Finds SPS & PPS parameters inside a .mp4.
58 | * @param path Path to the file to analyze
59 | * @throws IOException
60 | * @throws FileNotFoundException
61 | */
62 | public MP4Config (String path) throws IOException, FileNotFoundException {
63 |
64 | StsdBox stsdBox;
65 |
66 | // We open the mp4 file and parse it
67 | try {
68 | mp4Parser = MP4Parser.parse(path);
69 | } catch (IOException ignore) {
70 | // Maybe enough of the file has been parsed and we can get the stsd box
71 | }
72 |
73 | // We find the stsdBox
74 | stsdBox = mp4Parser.getStsdBox();
75 | mPPS = stsdBox.getB64PPS();
76 | mSPS = stsdBox.getB64SPS();
77 | mProfilLevel = stsdBox.getProfileLevel();
78 |
79 | mp4Parser.close();
80 |
81 | }
82 |
83 | public String getProfileLevel() {
84 | return mProfilLevel;
85 | }
86 |
87 | public String getB64PPS() {
88 | Log.d(TAG, "PPS: "+mPPS);
89 | return mPPS;
90 | }
91 |
92 | public String getB64SPS() {
93 | Log.d(TAG, "SPS: "+mSPS);
94 | return mSPS;
95 | }
96 |
97 | }
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/rtp/AACADTSPacketizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.rtp;
22 |
23 | import java.io.IOException;
24 |
25 | import net.majorkernelpanic.streaming.audio.AACStream;
26 | import android.os.SystemClock;
27 | import android.util.Log;
28 |
29 | /**
30 | *
31 | * RFC 3640.
32 | *
33 | * This packetizer must be fed with an InputStream containing ADTS AAC.
34 | * AAC will basically be rewrapped in an RTP stream and sent over the network.
35 | * This packetizer only implements the aac-hbr mode (High Bit-rate AAC) and
36 | * each packet only carry a single and complete AAC access unit.
37 | *
38 | */
39 | public class AACADTSPacketizer extends AbstractPacketizer implements Runnable {
40 |
41 | private final static String TAG = "AACADTSPacketizer";
42 |
43 | private Thread t;
44 | private int samplingRate = 8000;
45 |
46 | public AACADTSPacketizer() {
47 | super();
48 | }
49 |
50 | public void start() {
51 | if (t==null) {
52 | t = new Thread(this);
53 | t.start();
54 | }
55 | }
56 |
57 | public void stop() {
58 | if (t != null) {
59 | try {
60 | is.close();
61 | } catch (IOException ignore) {}
62 | t.interrupt();
63 | try {
64 | t.join();
65 | } catch (InterruptedException e) {}
66 | t = null;
67 | }
68 | }
69 |
70 | public void setSamplingRate(int samplingRate) {
71 | this.samplingRate = samplingRate;
72 | socket.setClockFrequency(samplingRate);
73 | }
74 |
75 | public void run() {
76 |
77 | Log.d(TAG,"AAC ADTS packetizer started !");
78 |
79 | // "A packet SHALL carry either one or more complete Access Units, or a
80 | // single fragment of an Access Unit. Fragments of the same Access Unit
81 | // have the same time stamp but different RTP sequence numbers. The
82 | // marker bit in the RTP header is 1 on the last fragment of an Access
83 | // Unit, and 0 on all other fragments." RFC 3640
84 |
85 | // ADTS header fields that we need to parse
86 | boolean protection;
87 | int frameLength, sum, length, nbau, nbpk, samplingRateIndex, profile;
88 | long oldtime = SystemClock.elapsedRealtime(), now = oldtime;
89 | byte[] header = new byte[8];
90 |
91 | try {
92 | while (!Thread.interrupted()) {
93 |
94 | // Synchronisation: ADTS packet starts with 12bits set to 1
95 | while (true) {
96 | if ( (is.read()&0xFF) == 0xFF ) {
97 | header[1] = (byte) is.read();
98 | if ( (header[1]&0xF0) == 0xF0) break;
99 | }
100 | }
101 |
102 | // Parse adts header (ADTS packets start with a 7 or 9 byte long header)
103 | fill(header, 2, 5);
104 |
105 | // The protection bit indicates whether or not the header contains the two extra bytes
106 | protection = (header[1]&0x01)>0 ? true : false;
107 | frameLength = (header[3]&0x03) << 11 |
108 | (header[4]&0xFF) << 3 |
109 | (header[5]&0xFF) >> 5 ;
110 | frameLength -= (protection ? 7 : 9);
111 |
112 | // Number of AAC frames in the ADTS frame
113 | nbau = (header[6]&0x03) + 1;
114 |
115 | // The number of RTP packets that will be sent for this ADTS frame
116 | nbpk = frameLength/MAXPACKETSIZE + 1;
117 |
118 | // Read CRS if any
119 | if (!protection) is.read(header,0,2);
120 |
121 | samplingRate = AACStream.AUDIO_SAMPLING_RATES[(header[2]&0x3C) >> 2];
122 | profile = ( (header[2]&0xC0) >> 6 ) + 1 ;
123 |
124 | // We update the RTP timestamp
125 | ts += 1024L*1000000000L/samplingRate; //stats.average();
126 |
127 | //Log.d(TAG,"frameLength: "+frameLength+" protection: "+protection+" p: "+profile+" sr: "+samplingRate);
128 |
129 | sum = 0;
130 | while (sum MAXPACKETSIZE-rtphl-4) {
137 | length = MAXPACKETSIZE-rtphl-4;
138 | }
139 | else {
140 | length = frameLength-sum;
141 | socket.markNextPacket();
142 | }
143 | sum += length;
144 | fill(buffer, rtphl+4, length);
145 |
146 | // AU-headers-length field: contains the size in bits of a AU-header
147 | // 13+3 = 16 bits -> 13bits for AU-size and 3bits for AU-Index / AU-Index-delta
148 | // 13 bits will be enough because ADTS uses 13 bits for frame length
149 | buffer[rtphl] = 0;
150 | buffer[rtphl+1] = 0x10;
151 |
152 | // AU-size
153 | buffer[rtphl+2] = (byte) (frameLength>>5);
154 | buffer[rtphl+3] = (byte) (frameLength<<3);
155 |
156 | // AU-Index
157 | buffer[rtphl+3] &= 0xF8;
158 | buffer[rtphl+3] |= 0x00;
159 |
160 | send(rtphl+4+length);
161 |
162 | }
163 |
164 | }
165 | } catch (IOException e) {
166 | // Ignore
167 | } catch (ArrayIndexOutOfBoundsException e) {
168 | Log.e(TAG,"ArrayIndexOutOfBoundsException: "+(e.getMessage()!=null?e.getMessage():"unknown error"));
169 | e.printStackTrace();
170 | } catch (InterruptedException ignore) {}
171 |
172 | Log.d(TAG,"AAC ADTS packetizer stopped !");
173 |
174 | }
175 |
176 | private int fill(byte[] buffer, int offset,int length) throws IOException {
177 | int sum = 0, len;
178 | while (sum0) {
89 |
90 | bufferInfo = ((MediaCodecInputStream)is).getLastBufferInfo();
91 | //Log.d(TAG,"length: "+length+" ts: "+bufferInfo.presentationTimeUs);
92 | oldts = ts;
93 | ts = bufferInfo.presentationTimeUs*1000;
94 |
95 | // Seems to happen sometimes
96 | if (oldts>ts) {
97 | socket.commitBuffer();
98 | continue;
99 | }
100 |
101 | socket.markNextPacket();
102 | socket.updateTimestamp(ts);
103 |
104 | // AU-headers-length field: contains the size in bits of a AU-header
105 | // 13+3 = 16 bits -> 13bits for AU-size and 3bits for AU-Index / AU-Index-delta
106 | // 13 bits will be enough because ADTS uses 13 bits for frame length
107 | buffer[rtphl] = 0;
108 | buffer[rtphl+1] = 0x10;
109 |
110 | // AU-size
111 | buffer[rtphl+2] = (byte) (length>>5);
112 | buffer[rtphl+3] = (byte) (length<<3);
113 |
114 | // AU-Index
115 | buffer[rtphl+3] &= 0xF8;
116 | buffer[rtphl+3] |= 0x00;
117 |
118 | send(rtphl+length+4);
119 |
120 | } else {
121 | socket.commitBuffer();
122 | }
123 |
124 | }
125 | } catch (IOException e) {
126 | } catch (ArrayIndexOutOfBoundsException e) {
127 | Log.e(TAG,"ArrayIndexOutOfBoundsException: "+(e.getMessage()!=null?e.getMessage():"unknown error"));
128 | e.printStackTrace();
129 | } catch (InterruptedException ignore) {}
130 |
131 | Log.d(TAG,"AAC LATM packetizer stopped !");
132 |
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/rtp/AMRNBPacketizer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.rtp;
22 |
23 | import java.io.IOException;
24 |
25 | import android.util.Log;
26 |
27 | /**
28 | *
29 | * RFC 3267.
30 | *
31 | * AMR Streaming over RTP.
32 | *
33 | * Must be fed with an InputStream containing raw AMR NB
34 | * Stream must begin with a 6 bytes long header: "#!AMR\n", it will be skipped
35 | *
36 | */
37 | public class AMRNBPacketizer extends AbstractPacketizer implements Runnable {
38 |
39 | public final static String TAG = "AMRNBPacketizer";
40 |
41 | private final int AMR_HEADER_LENGTH = 6; // "#!AMR\n"
42 | private static final int AMR_FRAME_HEADER_LENGTH = 1; // Each frame has a short header
43 | private static final int[] sFrameBits = {95, 103, 118, 134, 148, 159, 204, 244};
44 | private int samplingRate = 8000;
45 |
46 | private Thread t;
47 |
48 | public AMRNBPacketizer() {
49 | super();
50 | socket.setClockFrequency(samplingRate);
51 | }
52 |
53 | public void start() {
54 | if (t==null) {
55 | t = new Thread(this);
56 | t.start();
57 | }
58 | }
59 |
60 | public void stop() {
61 | if (t != null) {
62 | try {
63 | is.close();
64 | } catch (IOException ignore) {}
65 | t.interrupt();
66 | try {
67 | t.join();
68 | } catch (InterruptedException e) {}
69 | t = null;
70 | }
71 | }
72 |
73 | public void run() {
74 |
75 | int frameLength, frameType;
76 | long now = System.nanoTime(), oldtime = now;
77 | byte[] header = new byte[AMR_HEADER_LENGTH];
78 |
79 | try {
80 |
81 | // Skip raw AMR header
82 | fill(header,0,AMR_HEADER_LENGTH);
83 |
84 | if (header[5] != '\n') {
85 | Log.e(TAG,"Bad header ! AMR not correcty supported by the phone !");
86 | return;
87 | }
88 |
89 | while (!Thread.interrupted()) {
90 |
91 | buffer = socket.requestBuffer();
92 | buffer[rtphl] = (byte) 0xF0;
93 |
94 | // First we read the frame header
95 | fill(buffer, rtphl+1,AMR_FRAME_HEADER_LENGTH);
96 |
97 | // Then we calculate the frame payload length
98 | frameType = (Math.abs(buffer[rtphl + 1]) >> 3) & 0x0f;
99 | frameLength = (sFrameBits[frameType]+7)/8;
100 |
101 | // And we read the payload
102 | fill(buffer, rtphl+2,frameLength);
103 |
104 | //Log.d(TAG,"Frame length: "+frameLength+" frameType: "+frameType);
105 |
106 | // RFC 3267 Page 14: "For AMR, the sampling frequency is 8 kHz"
107 | // FIXME: Is this really always the case ??
108 | ts += 160L*1000000000L/samplingRate; //stats.average();
109 | socket.updateTimestamp(ts);
110 | socket.markNextPacket();
111 |
112 | //Log.d(TAG,"expected: "+ expected + " measured: "+measured);
113 |
114 | send(rtphl+1+AMR_FRAME_HEADER_LENGTH+frameLength);
115 |
116 | }
117 |
118 | } catch (IOException e) {
119 | } catch (InterruptedException e) {}
120 |
121 | Log.d(TAG,"AMR packetizer stopped !");
122 |
123 | }
124 |
125 | private int fill(byte[] buffer, int offset,int length) throws IOException {
126 | int sum = 0, len;
127 | while (sumperiod) {
135 | elapsed = 0;
136 | long now = System.nanoTime();
137 | if (!initoffset || (now - start < 0)) {
138 | start = now;
139 | duration = 0;
140 | initoffset = true;
141 | }
142 | // Prevents drifting issues by comparing the real duration of the
143 | // stream with the sum of all temporal lengths of RTP packets.
144 | value += (now - start) - duration;
145 | //Log.d(TAG, "sum1: "+duration/1000000+" sum2: "+(now-start)/1000000+" drift: "+((now-start)-duration)/1000000+" v: "+value/1000000);
146 | }
147 | if (c<5) {
148 | // We ignore the first 20 measured values because they may not be accurate
149 | c++;
150 | m = value;
151 | } else {
152 | m = (m*q+value)/(q+1);
153 | if (q>2;
99 | //Log.d(TAG,"j: "+j+" buffer: "+printBuffer(rtphl, rtphl+5)+" tr: "+tr);
100 | if (firstFragment) {
101 | // This is the first fragment of the frame -> header is set to 0x0400
102 | buffer[rtphl] = 4;
103 | firstFragment = false;
104 | } else {
105 | buffer[rtphl] = 0;
106 | }
107 | if (j>0) {
108 | // We have found the end of the frame
109 | stats.push(duration);
110 | ts+= stats.average(); duration = 0;
111 | //Log.d(TAG,"End of frame ! duration: "+stats.average());
112 | // The last fragment of a frame has to be marked
113 | socket.markNextPacket();
114 | send(j);
115 | nextBuffer = socket.requestBuffer();
116 | System.arraycopy(buffer,j+2,nextBuffer,rtphl+2,MAXPACKETSIZE-j-2);
117 | buffer = nextBuffer;
118 | j = MAXPACKETSIZE-j-2;
119 | firstFragment = true;
120 | } else {
121 | // We have not found the beginning of another frame
122 | // The whole packet is a fragment of a frame
123 | send(MAXPACKETSIZE);
124 | }
125 | }
126 | } catch (IOException e) {
127 | } catch (InterruptedException e) {}
128 |
129 | Log.d(TAG,"H263 Packetizer stopped !");
130 |
131 | }
132 |
133 | private int fill(int offset,int length) throws IOException {
134 |
135 | int sum = 0, len;
136 |
137 | while (sum=0 ){
76 | //Log.d(TAG,"Index: "+mIndex+" Time: "+mBufferInfo.presentationTimeUs+" size: "+mBufferInfo.size);
77 | mBuffer = mBuffers[mIndex];
78 | mBuffer.position(0);
79 | break;
80 | } else if (mIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
81 | mBuffers = mMediaCodec.getOutputBuffers();
82 | } else if (mIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
83 | mMediaFormat = mMediaCodec.getOutputFormat();
84 | Log.i(TAG,mMediaFormat.toString());
85 | } else if (mIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
86 | Log.v(TAG,"No buffer available...");
87 | //return 0;
88 | } else {
89 | Log.e(TAG,"Message: "+mIndex);
90 | //return 0;
91 | }
92 | }
93 | }
94 |
95 | if (mClosed) throw new IOException("This InputStream was closed");
96 |
97 | min = length < mBufferInfo.size - mBuffer.position() ? length : mBufferInfo.size - mBuffer.position();
98 | mBuffer.get(buffer, offset, min);
99 | if (mBuffer.position()>=mBufferInfo.size) {
100 | mMediaCodec.releaseOutputBuffer(mIndex, false);
101 | mBuffer = null;
102 | }
103 |
104 | } catch (RuntimeException e) {
105 | e.printStackTrace();
106 | }
107 |
108 | return min;
109 | }
110 |
111 | public int available() {
112 | if (mBuffer != null)
113 | return mBufferInfo.size - mBuffer.position();
114 | else
115 | return 0;
116 | }
117 |
118 | public BufferInfo getLastBufferInfo() {
119 | return mBufferInfo;
120 | }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/rtsp/RtcpDeinterleaver.java:
--------------------------------------------------------------------------------
1 | package net.majorkernelpanic.streaming.rtsp;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.io.PipedInputStream;
6 | import java.io.PipedOutputStream;
7 |
8 | class RtcpDeinterleaver extends InputStream implements Runnable {
9 |
10 | public final static String TAG = "RtcpDeinterleaver";
11 |
12 | private IOException mIOException;
13 | private InputStream mInputStream;
14 | private PipedInputStream mPipedInputStream;
15 | private PipedOutputStream mPipedOutputStream;
16 | private byte[] mBuffer;
17 |
18 | public RtcpDeinterleaver(InputStream inputStream) {
19 | mInputStream = inputStream;
20 | mPipedInputStream = new PipedInputStream(4096);
21 | try {
22 | mPipedOutputStream = new PipedOutputStream(mPipedInputStream);
23 | } catch (IOException e) {}
24 | mBuffer = new byte[1024];
25 | new Thread(this).start();
26 | }
27 |
28 | @Override
29 | public void run() {
30 | try {
31 | while (true) {
32 | int len = mInputStream.read(mBuffer, 0, 1024);
33 | mPipedOutputStream.write(mBuffer, 0, len);
34 | }
35 | } catch (IOException e) {
36 | try {
37 | mPipedInputStream.close();
38 | } catch (IOException ignore) {}
39 | mIOException = e;
40 | }
41 | }
42 |
43 | @Override
44 | public int read(byte[] buffer) throws IOException {
45 | if (mIOException != null) {
46 | throw mIOException;
47 | }
48 | return mPipedInputStream.read(buffer);
49 | }
50 |
51 | @Override
52 | public int read(byte[] buffer, int offset, int length) throws IOException {
53 | if (mIOException != null) {
54 | throw mIOException;
55 | }
56 | return mPipedInputStream.read(buffer, offset, length);
57 | }
58 |
59 | @Override
60 | public int read() throws IOException {
61 | if (mIOException != null) {
62 | throw mIOException;
63 | }
64 | return mPipedInputStream.read();
65 | }
66 |
67 | @Override
68 | public void close() throws IOException {
69 | mInputStream.close();
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/video/H263Stream.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.video;
22 |
23 | import java.io.IOException;
24 |
25 | import net.majorkernelpanic.streaming.SessionBuilder;
26 | import net.majorkernelpanic.streaming.rtp.H263Packetizer;
27 | import android.graphics.ImageFormat;
28 | import android.hardware.Camera.CameraInfo;
29 | import android.media.MediaRecorder;
30 | import android.service.textservice.SpellCheckerService.Session;
31 |
32 | /**
33 | * A class for streaming H.263 from the camera of an android device using RTP.
34 | * You should use a {@link Session} instantiated with {@link SessionBuilder} instead of using this class directly.
35 | * Call {@link #setDestinationAddress(InetAddress)}, {@link #setDestinationPorts(int)} and {@link #setVideoQuality(VideoQuality)}
36 | * to configure the stream. You can then call {@link #start()} to start the RTP stream.
37 | * Call {@link #stop()} to stop the stream.
38 | */
39 | public class H263Stream extends VideoStream {
40 |
41 | /**
42 | * Constructs the H.263 stream.
43 | * Uses CAMERA_FACING_BACK by default.
44 | * @throws IOException
45 | */
46 | public H263Stream() throws IOException {
47 | this(CameraInfo.CAMERA_FACING_BACK);
48 | }
49 |
50 | /**
51 | * Constructs the H.263 stream.
52 | * @param cameraId Can be either CameraInfo.CAMERA_FACING_BACK or CameraInfo.CAMERA_FACING_FRONT
53 | * @throws IOException
54 | */
55 | public H263Stream(int cameraId) {
56 | super(cameraId);
57 | mCameraImageFormat = ImageFormat.NV21;
58 | mVideoEncoder = MediaRecorder.VideoEncoder.H263;
59 | mPacketizer = new H263Packetizer();
60 | }
61 |
62 | /**
63 | * Starts the stream.
64 | */
65 | public synchronized void start() throws IllegalStateException, IOException {
66 | if (!mStreaming) {
67 | configure();
68 | super.start();
69 | }
70 | }
71 |
72 | public synchronized void configure() throws IllegalStateException, IOException {
73 | super.configure();
74 | mMode = MODE_MEDIARECORDER_API;
75 | mQuality = mRequestedQuality.clone();
76 | }
77 |
78 | /**
79 | * Returns a description of the stream using SDP. It can then be included in an SDP file.
80 | */
81 | public String getSessionDescription() {
82 | return "m=video "+String.valueOf(getDestinationPorts()[0])+" RTP/AVP 96\r\n" +
83 | "a=rtpmap:96 H263-1998/90000\r\n";
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/net/majorkernelpanic/streaming/video/VideoQuality.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
3 | *
4 | * This file is part of libstreaming (https://github.com/fyhertz/libstreaming)
5 | *
6 | * Spydroid is free software; you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation; either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This source code is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this source code; if not, write to the Free Software
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | */
20 |
21 | package net.majorkernelpanic.streaming.video;
22 |
23 | import java.util.Iterator;
24 | import java.util.List;
25 |
26 | import android.hardware.Camera;
27 | import android.hardware.Camera.Size;
28 | import android.util.Log;
29 |
30 | /**
31 | * A class that represents the quality of a video stream.
32 | * It contains the resolution, the framerate (in fps) and the bitrate (in bps) of the stream.
33 | */
34 | public class VideoQuality {
35 |
36 | // Tag
37 | private final static String TAG = "VideoQuality";
38 |
39 | // Default video stream quality.
40 | public final static VideoQuality DEFAULT_VIDEO_QUALITY = new VideoQuality(176,144,20,500000);
41 |
42 | public int framerate = 0;
43 | public int bitrate = 0;
44 | public int resX = 0;
45 | public int resY = 0;
46 |
47 |
48 | /**
49 | * Represents a quality for a video stream.
50 | * @param resX The horizontal resolution
51 | * @param resY The vertical resolution
52 | */
53 | public VideoQuality(int resX, int resY) {
54 | this.resX = resX;
55 | this.resY = resY;
56 | this.bitrate = DEFAULT_VIDEO_QUALITY.bitrate;
57 | this.framerate = DEFAULT_VIDEO_QUALITY.framerate;
58 | }
59 |
60 | /**
61 | * Represents a quality for a video stream.
62 | * @param resX The horizontal resolution
63 | * @param resY The vertical resolution
64 | * @param framerate The framerate in frame per seconds
65 | */
66 | public VideoQuality(int resX, int resY, int framerate) {
67 | this.framerate = framerate;
68 | this.bitrate = DEFAULT_VIDEO_QUALITY.bitrate;
69 | this.resX = resX;
70 | this.resY = resY;
71 | }
72 |
73 | /**
74 | * Represents a quality for a video stream.
75 | * @param resX The horizontal resolution
76 | * @param resY The vertical resolution
77 | * @param framerate The framerate in frame per seconds
78 | * @param bitrate The bitrate in bit per seconds
79 | */
80 | public VideoQuality(int resX, int resY, int framerate, int bitrate) {
81 | this.framerate = framerate;
82 | this.bitrate = bitrate;
83 | this.resX = resX;
84 | this.resY = resY;
85 | }
86 |
87 | public boolean equals(VideoQuality quality) {
88 | if (quality==null) return false;
89 | return (quality.resX == this.resX &
90 | quality.resY == this.resY &
91 | quality.framerate == this.framerate &
92 | quality.bitrate == this.bitrate);
93 | }
94 |
95 | public VideoQuality clone() {
96 | return new VideoQuality(resX,resY,framerate,bitrate);
97 | }
98 |
99 | public static VideoQuality parseQuality(String str) {
100 | VideoQuality quality = DEFAULT_VIDEO_QUALITY.clone();
101 | if (str != null) {
102 | String[] config = str.split("-");
103 | try {
104 | quality.bitrate = Integer.parseInt(config[0])*1000; // conversion to bit/s
105 | quality.framerate = Integer.parseInt(config[1]);
106 | quality.resX = Integer.parseInt(config[2]);
107 | quality.resY = Integer.parseInt(config[3]);
108 | }
109 | catch (IndexOutOfBoundsException ignore) {}
110 | }
111 | return quality;
112 | }
113 |
114 | public String toString() {
115 | return resX+"x"+resY+" px, "+framerate+" fps, "+bitrate/1000+" kbps";
116 | }
117 |
118 | /**
119 | * Checks if the requested resolution is supported by the camera.
120 | * If not, it modifies it by supported parameters.
121 | **/
122 | public static VideoQuality determineClosestSupportedResolution(List supportedSizes, VideoQuality quality) {
123 | VideoQuality v = quality.clone();
124 | int minDist = Integer.MAX_VALUE;
125 | String supportedSizesStr = "Supported resolutions: ";
126 | for (Iterator it = supportedSizes.iterator(); it.hasNext();) {
127 | Size size = it.next();
128 | supportedSizesStr += size.width+"x"+size.height+(it.hasNext()?", ":"");
129 | int dist = Math.abs(quality.resX - size.width);
130 | if (dist"+v.resX+"x"+v.resY);
139 | }
140 |
141 | return v;
142 | }
143 |
144 | public static VideoQuality getHighestQuality(Camera.Parameters parameters) {
145 | List supportedSizes = parameters.getSupportedPreviewSizes();
146 | Size highestQuality = supportedSizes.get(0);
147 | return new VideoQuality(highestQuality.width, highestQuality.height);
148 | }
149 |
150 | public static int[] determineMaximumSupportedFramerate(Camera.Parameters parameters) {
151 | int[] maxFps = new int[]{0,0};
152 | String supportedFpsRangesStr = "Supported frame rates: ";
153 | List supportedFpsRanges = parameters.getSupportedPreviewFpsRange();
154 | for (Iterator it = supportedFpsRanges.iterator(); it.hasNext();) {
155 | int[] interval = it.next();
156 | // Intervals are returned as integers, for example "29970" means "29.970" FPS.
157 | supportedFpsRangesStr += interval[0]/1000+"-"+interval[1]/1000+"fps"+(it.hasNext()?", ":"");
158 | if (interval[1]>maxFps[1] || (interval[0]>maxFps[0] && interval[1]==maxFps[1])) {
159 | maxFps = interval;
160 | }
161 | }
162 | Log.v(TAG,supportedFpsRangesStr);
163 | return maxFps;
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------