45 | * You just have to add strings in string.xml (or some other file containing string resources) 46 | * and you will then be able to access those on the client side. 47 | */ 48 | public class ModInternationalization implements HttpRequestHandler { 49 | private static final String TAG = "ModInternationalization"; 50 | 51 | /** 52 | * Client side file name. 53 | */ 54 | public static final String PATTERN = "/strings.json"; 55 | 56 | /** 57 | * Prefix for strings. 58 | */ 59 | public static final String PREFIX = "web_"; 60 | 61 | private String mJSON = "{}"; 62 | 63 | public ModInternationalization(TinyHttpServer server) { 64 | super(); 65 | 66 | StringBuilder builder = new StringBuilder(); 67 | 68 | try { 69 | // Retrieves R.string 70 | Class> String = Class.forName(server.mContext.getPackageName() + ".R$string"); 71 | Field[] fields = String.getFields(); 72 | 73 | // Constructs a JSON with all members starting with PREFIX 74 | builder.append("{\""); 75 | for (int i = 0; i < fields.length; i++) { 76 | if (fields[i].getName().startsWith(PREFIX)) { 77 | builder.append(fields[i].getName()); 78 | builder.append("\":\""); 79 | builder.append((String) server.getString(fields[i].getInt(null))); 80 | builder.append("\",\""); 81 | } 82 | } 83 | 84 | if (builder.length() > 2) { 85 | builder.setLength(builder.length() - 2); 86 | } else { 87 | builder.setLength(builder.length() - 1); 88 | } 89 | builder.append("}"); 90 | 91 | mJSON = builder.toString(); 92 | 93 | } catch (Exception e) { 94 | Log.e(TAG, "Little problem with ModInternationalization !"); 95 | e.printStackTrace(); 96 | } 97 | } 98 | 99 | public void handle( 100 | final HttpRequest request, 101 | final HttpResponse response, 102 | final HttpContext context) throws HttpException, IOException { 103 | final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ENGLISH); 104 | if (!method.equals("GET") && !method.equals("HEAD")) { 105 | throw new MethodNotSupportedException(method + " method not supported"); 106 | } 107 | final EntityTemplate body = new EntityTemplate(new ContentProducer() { 108 | public void writeTo(final OutputStream outstream) throws IOException { 109 | OutputStreamWriter writer = new OutputStreamWriter(outstream, "UTF-8"); 110 | writer.write(mJSON); 111 | writer.flush(); 112 | } 113 | }); 114 | response.setStatusCode(HttpStatus.SC_OK); 115 | body.setContentType("text/json; charset=UTF-8"); 116 | response.setEntity(body); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/net/majorkernelpanic/onvif/DeviceBackBean.java: -------------------------------------------------------------------------------- 1 | package net.majorkernelpanic.onvif; 2 | 3 | public class DeviceBackBean { 4 | private String userName; 5 | 6 | private String psw; 7 | private String ipAddress; 8 | private String serviceUrl; 9 | private String uuid; 10 | private String mediaUrl; 11 | private String ptzUrl; 12 | private String imageUrl; 13 | private String eventUrl; 14 | 15 | private String videoEncodeFormat; 16 | 17 | private String sourceWidth = "800"; 18 | private String sourceHeight = "400"; 19 | private String encodeWidth = "480"; 20 | private String encodeHeight = "320"; 21 | 22 | private String frameRateLimit = "25"; 23 | private String bitrateLimit = "10000"; 24 | // the default port of which using to perform SOAP web service 25 | private String port = "8080"; 26 | // the default port of which using to RTSP streaming action 27 | private String rtspPort = "8086"; 28 | private String mediaTimeout = "PT30S"; 29 | 30 | public DeviceBackBean() { 31 | } 32 | 33 | public String getUserName() { 34 | return userName; 35 | } 36 | 37 | public void setUserName(String userName) { 38 | this.userName = userName; 39 | } 40 | 41 | public String getPsw() { 42 | return psw; 43 | } 44 | 45 | public void setPsw(String psw) { 46 | this.psw = psw; 47 | } 48 | 49 | public String getIpAddress() { 50 | return ipAddress; 51 | } 52 | 53 | public void setIpAddress(String ipAddress) { 54 | this.ipAddress = ipAddress; 55 | } 56 | 57 | public String getServiceUrl() { 58 | return serviceUrl; 59 | } 60 | 61 | public void setServiceUrl(String serviceUrl) { 62 | this.serviceUrl = serviceUrl; 63 | } 64 | 65 | public String getUuid() { 66 | return uuid; 67 | } 68 | 69 | public void setUuid(String uuid) { 70 | this.uuid = uuid; 71 | } 72 | 73 | public String getMediaUrl() { 74 | return mediaUrl; 75 | } 76 | 77 | public void setMediaUrl(String mediaUrl) { 78 | this.mediaUrl = mediaUrl; 79 | } 80 | 81 | public String getPtzUrl() { 82 | return ptzUrl; 83 | } 84 | 85 | public void setPtzUrl(String ptzUrl) { 86 | this.ptzUrl = ptzUrl; 87 | } 88 | 89 | public String getImageUrl() { 90 | return imageUrl; 91 | } 92 | 93 | public void setImageUrl(String imageUrl) { 94 | this.imageUrl = imageUrl; 95 | } 96 | 97 | public String getEventUrl() { 98 | return eventUrl; 99 | } 100 | 101 | public void setEventUrl(String eventUrl) { 102 | this.eventUrl = eventUrl; 103 | } 104 | 105 | public String getVideoEncodeFormat() { 106 | return videoEncodeFormat; 107 | } 108 | 109 | public void setVideoEncodeFormat(String videoEncodeFormat) { 110 | this.videoEncodeFormat = videoEncodeFormat; 111 | } 112 | 113 | public String getMediaTimeout() { 114 | return mediaTimeout; 115 | } 116 | 117 | public void setMediaTimeout(String mediaTimeout) { 118 | this.mediaTimeout = mediaTimeout; 119 | } 120 | 121 | public String getRtspPort() { 122 | return rtspPort; 123 | } 124 | 125 | public void setRtspPort(String rtspPort) { 126 | this.rtspPort = rtspPort; 127 | } 128 | 129 | public String getPort() { 130 | return port; 131 | } 132 | 133 | public void setPort(String port) { 134 | this.port = port; 135 | } 136 | 137 | public String getBitrateLimit() { 138 | return bitrateLimit; 139 | } 140 | 141 | public void setBitrateLimit(String bitrateLimit) { 142 | this.bitrateLimit = bitrateLimit; 143 | } 144 | 145 | public String getFrameRateLimit() { 146 | return frameRateLimit; 147 | } 148 | 149 | public void setFrameRateLimit(String frameRateLimit) { 150 | this.frameRateLimit = frameRateLimit; 151 | } 152 | 153 | public String getEncodeHeight() { 154 | return encodeHeight; 155 | } 156 | 157 | public void setEncodeHeight(String encodeHeight) { 158 | this.encodeHeight = encodeHeight; 159 | } 160 | 161 | public String getEncodeWidth() { 162 | return encodeWidth; 163 | } 164 | 165 | public void setEncodeWidth(String encodeWidth) { 166 | this.encodeWidth = encodeWidth; 167 | } 168 | 169 | public String getSourceHeight() { 170 | return sourceHeight; 171 | } 172 | 173 | public void setSourceHeight(String sourceHeight) { 174 | this.sourceHeight = sourceHeight; 175 | } 176 | 177 | public String getSourceWidth() { 178 | return sourceWidth; 179 | } 180 | 181 | public void setSourceWidth(String sourceWidth) { 182 | this.sourceWidth = sourceWidth; 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /app/src/main/java/net/majorkernelpanic/onvif/DeviceStaticInfo.java: -------------------------------------------------------------------------------- 1 | package net.majorkernelpanic.onvif; 2 | 3 | public interface DeviceStaticInfo { 4 | String USER_NAME = "ky_lab"; 5 | String USER_PSW = "123456"; 6 | String GET_MEDIA = "/onvif/Media"; 7 | String GET_PTZ = "/onvif/PTZ"; 8 | String GET_ANALYTICS = "/onvif/Analytics"; 9 | String GET_DEVICE_SERVICES = "/onvif/device_services"; 10 | String GET_EVENTS = "/onvif/Events"; 11 | String GET_IMAGING = "/onvif/Imaging"; 12 | /** 13 | * 获取推流视频的地址的URI 14 | */ 15 | String GET_STREAM_URI = "onvif/GetStreamUri"; 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/net/majorkernelpanic/onvif/ONVIFDevDiscoveryReqHeader.java: -------------------------------------------------------------------------------- 1 | package net.majorkernelpanic.onvif; 2 | 3 | public class ONVIFDevDiscoveryReqHeader { 4 | 5 | private String messageId; 6 | private String action; 7 | 8 | public ONVIFDevDiscoveryReqHeader() { 9 | } 10 | 11 | public String getMessageId() { 12 | return messageId; 13 | } 14 | 15 | public void setMessageId(String messageId) { 16 | this.messageId = messageId; 17 | } 18 | 19 | public String getAction() { 20 | return action; 21 | } 22 | 23 | public void setAction(String action) { 24 | this.action = action; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/net/majorkernelpanic/onvif/ONVIFPacketHandler.java: -------------------------------------------------------------------------------- 1 | package net.majorkernelpanic.onvif; 2 | 3 | import android.util.Log; 4 | 5 | import org.xml.sax.Attributes; 6 | import org.xml.sax.SAXException; 7 | import org.xml.sax.helpers.DefaultHandler; 8 | 9 | import javax.xml.parsers.SAXParser; 10 | 11 | 12 | /** 13 | * 解析我们接收到的ONVIF数据包 14 | * 包的格式本身是xml格式的(这是SOAP协议要求的) 15 | * 如果ONVIF协议继续更新的话,希望更新成FlatBuffer协议,比xml要快到很多,同时数据 16 | * 也精简很多. 17 | *
18 | * {@link SAXParser}会自动的将输入流绑定到当前的{@link DefaultHandler}当中。
19 | * 因此我们不需要处理数据源。
20 | * 输入源在某种程度上对我们是透明的.
21 | */
22 | public class ONVIFPacketHandler extends DefaultHandler {
23 | private static final String TAG = "ONVIFPacketHandler";
24 |
25 | private static final String LOCAL_MSG_ID = "a:MessageID";
26 | private static final String LOCAL_ACTION = "a:Action";
27 |
28 | private boolean isMsgID = false;
29 | private boolean isAction = false;
30 |
31 | private ONVIFDevDiscoveryReqHeader reqHeader;
32 |
33 | public ONVIFPacketHandler() {
34 | reqHeader = new ONVIFDevDiscoveryReqHeader();
35 | }
36 |
37 | @Override
38 | public void startDocument() throws SAXException {
39 | Log.d(TAG, "start parsing the document");
40 |
41 | }
42 |
43 | @Override
44 | public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
45 | Log.d(TAG, "element : " + String.format("uri = %s, localName = %s, qName = %s", uri, localName, qName));
46 | switch (qName) {
47 | case LOCAL_MSG_ID:
48 | isMsgID = true;
49 | break;
50 | case LOCAL_ACTION:
51 | isAction = true;
52 | break;
53 | }
54 | }
55 |
56 | @Override
57 | public void characters(char[] ch, int start, int length) throws SAXException {
58 | if (isMsgID) {
59 | String messageId = new String(ch, start, length);
60 | reqHeader.setMessageId(messageId.trim());
61 | Log.d(TAG, "the message ID we get are : " + messageId);
62 | isMsgID = false;
63 | } else if (isAction) {
64 | String action = new String(ch, start, length);
65 | reqHeader.setAction(action.trim());
66 | Log.d(TAG, "the action we get are : " + action);
67 | isAction = false;
68 | }
69 | }
70 |
71 | @Override
72 | public void endElement(String uri, String localName, String qName) throws SAXException {
73 | Log.d(TAG, String.format("end element of uri = %s, localName = %s, qName = %s", uri, localName, qName));
74 | }
75 |
76 | @Override
77 | public void endDocument() throws SAXException {
78 | Log.d(TAG, "End of parsing documents");
79 | }
80 |
81 | public ONVIFDevDiscoveryReqHeader getReqHeader() {
82 | return reqHeader;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/spydroid/api/CustomRtspServer.java:
--------------------------------------------------------------------------------
1 | package net.majorkernelpanic.spydroid.api;
2 |
3 | import net.majorkernelpanic.streaming.rtsp.RtspServer;
4 |
5 | public class CustomRtspServer extends RtspServer {
6 | public CustomRtspServer() {
7 | super();
8 | // 最原始程序实现当中,默认是关闭RTSP server的,
9 | // 如果打开RTSP server之后,我们就可以直接通过vlc来查看rtsp视频流了
10 | // 我们这里将RTSP再设置为默认打开.
11 | mEnabled = true;
12 | }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/spydroid/ui/AboutFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2013 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.spydroid.ui;
22 |
23 | import net.majorkernelpanic.spydroid.R;
24 | import net.majorkernelpanic.spydroid.SpydroidApplication;
25 |
26 | import android.content.Intent;
27 | import android.net.Uri;
28 | import android.os.Bundle;
29 | import android.support.v4.app.Fragment;
30 | import android.view.LayoutInflater;
31 | import android.view.View;
32 | import android.view.View.OnClickListener;
33 | import android.view.ViewGroup;
34 | import android.widget.Button;
35 |
36 | public class AboutFragment extends Fragment {
37 |
38 | private Button mButtonVisit;
39 | private Button mButtonRate;
40 | private Button mButtonLike;
41 |
42 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
43 | View rootView = inflater.inflate(R.layout.about, container, false);
44 |
45 | mButtonVisit = (Button) rootView.findViewById(R.id.visit);
46 | mButtonRate = (Button) rootView.findViewById(R.id.rate);
47 | mButtonLike = (Button) rootView.findViewById(R.id.like);
48 |
49 | mButtonVisit.setOnClickListener(new OnClickListener() {
50 | @Override
51 | public void onClick(View v) {
52 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://code.google.com/p/spydroid-ipcamera/"));
53 | intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
54 | startActivity(intent);
55 | }
56 | });
57 |
58 | mButtonRate.setOnClickListener(new OnClickListener() {
59 | @Override
60 | public void onClick(View v) {
61 | String appPackageName = SpydroidApplication.getInstance().getApplicationContext().getPackageName();
62 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName));
63 | intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
64 | startActivity(intent);
65 | }
66 | });
67 |
68 | mButtonLike.setOnClickListener(new OnClickListener() {
69 | @Override
70 | public void onClick(View v) {
71 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.facebook.com/spydroidipcamera"));
72 | intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
73 | startActivity(intent);
74 | }
75 | });
76 |
77 | return rootView;
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/spydroid/ui/PreviewFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2013 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.spydroid.ui;
22 |
23 | import net.majorkernelpanic.http.TinyHttpServer;
24 | import net.majorkernelpanic.spydroid.R;
25 | import net.majorkernelpanic.spydroid.api.CustomHttpServer;
26 | import net.majorkernelpanic.spydroid.api.CustomRtspServer;
27 | import net.majorkernelpanic.streaming.SessionBuilder;
28 | import net.majorkernelpanic.streaming.gl.SurfaceView;
29 | import net.majorkernelpanic.streaming.rtsp.RtspServer;
30 | import android.content.ComponentName;
31 | import android.content.Context;
32 | import android.content.Intent;
33 | import android.content.ServiceConnection;
34 | import android.os.Bundle;
35 | import android.os.IBinder;
36 | import android.support.v4.app.Fragment;
37 | import android.view.LayoutInflater;
38 | import android.view.SurfaceHolder;
39 | import android.view.View;
40 | import android.view.ViewGroup;
41 | import android.widget.TextView;
42 |
43 | public class PreviewFragment extends Fragment {
44 |
45 | public final static String TAG = "PreviewFragment";
46 |
47 | private SurfaceView mSurfaceView;
48 | private TextView mTextView;
49 | private CustomHttpServer mHttpServer;
50 | private RtspServer mRtspServer;
51 |
52 | @Override
53 | public void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | }
56 |
57 | @Override
58 | public void onPause() {
59 | super.onPause();
60 | getActivity().unbindService(mHttpServiceConnection);
61 | getActivity().unbindService(mRtspServiceConnection);
62 | }
63 |
64 | @Override
65 | public void onResume() {
66 | super.onResume();
67 | getActivity().bindService(new Intent(getActivity(),CustomHttpServer.class), mHttpServiceConnection, Context.BIND_AUTO_CREATE);
68 | getActivity().bindService(new Intent(getActivity(),CustomRtspServer.class), mRtspServiceConnection, Context.BIND_AUTO_CREATE);
69 | }
70 |
71 | @Override
72 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
73 | View rootView = inflater.inflate(R.layout.preview,container,false);
74 |
75 | mTextView = (TextView)rootView.findViewById(R.id.tooltip);
76 |
77 | if (((SpydroidActivity)getActivity()).device == ((SpydroidActivity)getActivity()).TABLET) {
78 |
79 | mSurfaceView = (SurfaceView)rootView.findViewById(R.id.tablet_camera_view);
80 | SessionBuilder.getInstance().setSurfaceView(mSurfaceView);
81 |
82 | }
83 |
84 | return rootView;
85 | }
86 |
87 | public void update() {
88 | getActivity().runOnUiThread(new Runnable() {
89 | @Override
90 | public void run() {
91 | if (mTextView != null) {
92 | if ((mRtspServer != null && mRtspServer.isStreaming()) || (mHttpServer != null && mHttpServer.isStreaming()))
93 | mTextView.setVisibility(View.INVISIBLE);
94 | else
95 | mTextView.setVisibility(View.VISIBLE);
96 | }
97 | }
98 | });
99 | }
100 |
101 | private final ServiceConnection mRtspServiceConnection = new ServiceConnection() {
102 | @Override
103 | public void onServiceConnected(ComponentName name, IBinder service) {
104 | mRtspServer = (RtspServer) ((RtspServer.LocalBinder)service).getService();
105 | update();
106 | }
107 | @Override
108 | public void onServiceDisconnected(ComponentName name) {}
109 | };
110 |
111 | private final ServiceConnection mHttpServiceConnection = new ServiceConnection() {
112 | @Override
113 | public void onServiceConnected(ComponentName name, IBinder service) {
114 | mHttpServer = (CustomHttpServer) ((TinyHttpServer.LocalBinder)service).getService();
115 | update();
116 | }
117 | @Override
118 | public void onServiceDisconnected(ComponentName name) {}
119 | };
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/spydroid/ui/TabletFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2013 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.spydroid.ui;
22 |
23 | import net.majorkernelpanic.spydroid.R;
24 | import android.os.Bundle;
25 | import android.support.v4.app.Fragment;
26 | import android.view.LayoutInflater;
27 | import android.view.View;
28 | import android.view.ViewGroup;
29 |
30 | public class TabletFragment extends Fragment {
31 |
32 | @Override
33 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
34 | View rootView = inflater.inflate(R.layout.tablet,container,false);
35 | return rootView ;
36 | }
37 |
38 | @Override
39 | public void onResume() {
40 | super.onResume();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/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.net.InetAddress;
25 |
26 | /**
27 | * An interface that represents a Stream.
28 | */
29 | public interface Stream {
30 |
31 | /**
32 | * Configures the stream. You need to call this before calling {@link #getSessionDescription()}
33 | * to apply your configuration of the stream.
34 | */
35 | void configure() throws IllegalStateException, IOException;
36 |
37 | /**
38 | * Starts the stream.
39 | * This method can only be called after {@link Stream#configure()}.
40 | */
41 | void start() throws IllegalStateException, IOException;
42 |
43 | /**
44 | * Stops the stream.
45 | */
46 | void stop();
47 |
48 | /**
49 | * Sets the Time To Live of packets sent over the network.
50 | *
51 | * @param ttl The time to live
52 | * @throws IOException
53 | */
54 | void setTimeToLive(int ttl) throws IOException;
55 |
56 | /**
57 | * Sets the destination ip address of the stream.
58 | *
59 | * @param dest The destination address of the stream
60 | */
61 | void setDestinationAddress(InetAddress dest);
62 |
63 | /**
64 | * Sets the destination ports of the stream.
65 | * If an odd number is supplied for the destination port then the next
66 | * lower even number will be used for RTP and it will be used for RTCP.
67 | * If an even number is supplied, it will be used for RTP and the next odd
68 | * number will be used for RTCP.
69 | *
70 | * @param dport The destination port
71 | */
72 | void setDestinationPorts(int dport);
73 |
74 | /**
75 | * Sets the destination ports of the stream.
76 | *
77 | * @param rtpPort Destination port that will be used for RTP
78 | * @param rtcpPort Destination port that will be used for RTCP
79 | */
80 | void setDestinationPorts(int rtpPort, int rtcpPort);
81 |
82 | /**
83 | * Returns a pair of source ports, the first one is the
84 | * one used for RTP and the second one is used for RTCP.
85 | **/
86 | int[] getLocalPorts();
87 |
88 | /**
89 | * Returns a pair of destination ports, the first one is the
90 | * one used for RTP and the second one is used for RTCP.
91 | **/
92 | int[] getDestinationPorts();
93 |
94 |
95 | /**
96 | * Returns the SSRC of the underlying {@link net.majorkernelpanic.streaming.rtp.RtpSocket}.
97 | *
98 | * @return the SSRC of the stream.
99 | */
100 | int getSSRC();
101 |
102 | /**
103 | * Returns an approximation of the bit rate consumed by the stream in bit per seconde.
104 | */
105 | long getBitrate();
106 |
107 | /**
108 | * Returns a description of the stream using SDP.
109 | * This method can only be called after {@link Stream#configure()}.
110 | *
111 | * @throws IllegalStateException Thrown when {@link Stream#configure()} wa not called.
112 | */
113 | String getSessionDescription() throws IllegalStateException;
114 |
115 | boolean isStreaming();
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/app/src/main/java/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 | configure();
64 | if (!mStreaming) {
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 |
--------------------------------------------------------------------------------
/app/src/main/java/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 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/streaming/audio/AudioStream.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 |
25 | import net.majorkernelpanic.streaming.MediaStream;
26 | import android.media.MediaRecorder;
27 | import android.util.Log;
28 |
29 | /**
30 | * Don't use this class directly.
31 | */
32 | public abstract class AudioStream extends MediaStream {
33 |
34 | protected int mAudioSource;
35 | protected int mOutputFormat;
36 | protected int mAudioEncoder;
37 | protected AudioQuality mRequestedQuality = AudioQuality.DEFAULT_AUDIO_QUALITY.clone();
38 | protected AudioQuality mQuality = mRequestedQuality.clone();
39 |
40 | public AudioStream() {
41 | setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
42 | }
43 |
44 | public void setAudioSource(int audioSource) {
45 | mAudioSource = audioSource;
46 | }
47 |
48 | public void setAudioQuality(AudioQuality quality) {
49 | mRequestedQuality = quality;
50 | }
51 |
52 | /**
53 | * Returns the quality of the stream.
54 | */
55 | public AudioQuality getAudioQuality() {
56 | return mQuality;
57 | }
58 |
59 | protected void setAudioEncoder(int audioEncoder) {
60 | mAudioEncoder = audioEncoder;
61 | }
62 |
63 | protected void setOutputFormat(int outputFormat) {
64 | mOutputFormat = outputFormat;
65 | }
66 |
67 | @Override
68 | protected void encodeWithMediaRecorder() throws IOException {
69 |
70 | // We need a local socket to forward data output by the camera to the packetizer
71 | createSockets();
72 |
73 | Log.v(TAG,"Requested audio with "+mQuality.bitRate/1000+"kbps"+" at "+mQuality.samplingRate/1000+"kHz");
74 |
75 | mMediaRecorder = new MediaRecorder();
76 | mMediaRecorder.setAudioSource(mAudioSource);
77 | mMediaRecorder.setOutputFormat(mOutputFormat);
78 | mMediaRecorder.setAudioEncoder(mAudioEncoder);
79 | mMediaRecorder.setAudioChannels(1);
80 | mMediaRecorder.setAudioSamplingRate(mQuality.samplingRate);
81 | mMediaRecorder.setAudioEncodingBitRate(mQuality.bitRate);
82 |
83 | // We write the ouput of the camera in a local socket instead of a file !
84 | // This one little trick makes streaming feasible quiet simply: data from the camera
85 | // can then be manipulated at the other end of the socket
86 | mMediaRecorder.setOutputFile(mSender.getFileDescriptor());
87 |
88 | mMediaRecorder.prepare();
89 | mMediaRecorder.start();
90 |
91 | try {
92 | // mReceiver.getInputStream contains the data from the camera
93 | // the mPacketizer encapsulates this stream in an RTP stream and send it over the network
94 | mPacketizer.setDestination(mDestination, mRtpPort, mRtcpPort);
95 | mPacketizer.setInputStream(mReceiver.getInputStream());
96 | mPacketizer.start();
97 | mStreaming = true;
98 | } catch (IOException e) {
99 | stop();
100 | throw new IOException("Something happened with the local sockets :/ Start failed !");
101 | }
102 |
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/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 |
--------------------------------------------------------------------------------
/app/src/main/java/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 |
--------------------------------------------------------------------------------
/app/src/main/java/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 |
--------------------------------------------------------------------------------
/app/src/main/java/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 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/streaming/gl/SurfaceView.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.gl;
22 |
23 | import java.util.concurrent.Semaphore;
24 |
25 | import android.content.Context;
26 | import android.graphics.SurfaceTexture;
27 | import android.graphics.SurfaceTexture.OnFrameAvailableListener;
28 | import android.util.AttributeSet;
29 | import android.util.Log;
30 | import android.view.Surface;
31 | import android.view.SurfaceHolder;
32 |
33 | public class SurfaceView extends android.view.SurfaceView implements Runnable, OnFrameAvailableListener, SurfaceHolder.Callback {
34 |
35 | public final static String TAG = "GLSurfaceView";
36 |
37 | private Thread mThread = null;
38 | private boolean mFrameAvailable = false;
39 | private boolean mRunning = true;
40 |
41 | private SurfaceManager mViewSurfaceManager = null;
42 | private SurfaceManager mCodecSurfaceManager = null;
43 | private TextureManager mTextureManager = null;
44 |
45 | private Semaphore mLock = new Semaphore(0);
46 | private Object mSyncObject = new Object();
47 |
48 | public SurfaceView(Context context, AttributeSet attrs) {
49 | super(context, attrs);
50 | getHolder().addCallback(this);
51 | }
52 |
53 | public SurfaceTexture getSurfaceTexture() {
54 | return mTextureManager.getSurfaceTexture();
55 | }
56 |
57 | public void addMediaCodecSurface(Surface surface) {
58 | synchronized (mSyncObject) {
59 | mCodecSurfaceManager = new SurfaceManager(surface,mViewSurfaceManager);
60 | }
61 | }
62 |
63 | public void removeMediaCodecSurface() {
64 | synchronized (mSyncObject) {
65 | if (mCodecSurfaceManager != null) {
66 | mCodecSurfaceManager.release();
67 | mCodecSurfaceManager = null;
68 | }
69 | }
70 | }
71 |
72 | public void startGLThread() {
73 | Log.d(TAG,"Thread started.");
74 | if (mTextureManager == null) {
75 | mTextureManager = new TextureManager();
76 | }
77 | if (mTextureManager.getSurfaceTexture() == null) {
78 | mThread = new Thread(SurfaceView.this);
79 | mRunning = true;
80 | mThread.start();
81 | mLock.acquireUninterruptibly();
82 | }
83 | }
84 |
85 | @Override
86 | public void run() {
87 |
88 | mViewSurfaceManager = new SurfaceManager(getHolder().getSurface());
89 | mViewSurfaceManager.makeCurrent();
90 | mTextureManager.createTexture().setOnFrameAvailableListener(this);
91 |
92 | mLock.release();
93 |
94 | try {
95 | long ts = 0, oldts = 0;
96 | while (mRunning) {
97 | synchronized (mSyncObject) {
98 | mSyncObject.wait(2500);
99 | if (mFrameAvailable) {
100 | mFrameAvailable = false;
101 |
102 | mViewSurfaceManager.makeCurrent();
103 | mTextureManager.updateFrame();
104 | mTextureManager.drawFrame();
105 | mViewSurfaceManager.swapBuffer();
106 |
107 | if (mCodecSurfaceManager != null) {
108 | mCodecSurfaceManager.makeCurrent();
109 | mTextureManager.drawFrame();
110 | oldts = ts;
111 | ts = mTextureManager.getSurfaceTexture().getTimestamp();
112 | //Log.d(TAG,"FPS: "+(1000000000/(ts-oldts)));
113 | mCodecSurfaceManager.setPresentationTime(ts);
114 | mCodecSurfaceManager.swapBuffer();
115 | }
116 |
117 | } else {
118 | Log.e(TAG,"No frame received !");
119 | }
120 | }
121 | }
122 | } catch (InterruptedException ignore) {
123 | } finally {
124 | mViewSurfaceManager.release();
125 | mTextureManager.release();
126 | }
127 | }
128 |
129 | @Override
130 | public void onFrameAvailable(SurfaceTexture surfaceTexture) {
131 | synchronized (mSyncObject) {
132 | mFrameAvailable = true;
133 | mSyncObject.notifyAll();
134 | }
135 | }
136 |
137 | @Override
138 | public void surfaceChanged(SurfaceHolder holder, int format, int width,
139 | int height) {
140 | }
141 |
142 | @Override
143 | public void surfaceCreated(SurfaceHolder holder) {
144 | }
145 |
146 | @Override
147 | public void surfaceDestroyed(SurfaceHolder holder) {
148 | if (mThread != null) {
149 | mThread.interrupt();
150 | }
151 | mRunning = false;
152 | }
153 |
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/streaming/hw/NV21Convertor.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.nio.ByteBuffer;
24 |
25 | import android.media.MediaCodecInfo;
26 |
27 | /**
28 | * Converts from NV21 to YUV420 semi planar or planar.
29 | */
30 | public class NV21Convertor {
31 |
32 | private int mSliceHeight, mHeight;
33 | private int mStride, mWidth;
34 | private int mSize;
35 | private boolean mPlanar, mPanesReversed = false;
36 | private int mYPadding;
37 | private byte[] mBuffer;
38 | ByteBuffer mCopy;
39 |
40 | public void setSize(int width, int height) {
41 | mHeight = height;
42 | mWidth = width;
43 | mSliceHeight = height;
44 | mStride = width;
45 | mSize = mWidth*mHeight;
46 | }
47 |
48 | public void setStride(int width) {
49 | mStride = width;
50 | }
51 |
52 | public void setSliceHeigth(int height) {
53 | mSliceHeight = height;
54 | }
55 |
56 | public void setPlanar(boolean planar) {
57 | mPlanar = planar;
58 | }
59 |
60 | public void setYPadding(int padding) {
61 | mYPadding = padding;
62 | }
63 |
64 | public int getBufferSize() {
65 | return 3*mSize/2;
66 | }
67 |
68 | public void setEncoderColorFormat(int colorFormat) {
69 | switch (colorFormat) {
70 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
71 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
72 | case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
73 | setPlanar(false);
74 | break;
75 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
76 | case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
77 | setPlanar(true);
78 | break;
79 | }
80 | }
81 |
82 | public void setColorPanesReversed(boolean b) {
83 | mPanesReversed = b;
84 | }
85 |
86 | public int getStride() {
87 | return mStride;
88 | }
89 |
90 | public int getSliceHeigth() {
91 | return mSliceHeight;
92 | }
93 |
94 | public int getYPadding() {
95 | return mYPadding;
96 | }
97 |
98 |
99 | public boolean getPlanar() {
100 | return mPlanar;
101 | }
102 |
103 | public boolean getUVPanesReversed() {
104 | return mPanesReversed;
105 | }
106 |
107 | public void convert(byte[] data, ByteBuffer buffer) {
108 | byte[] result = convert(data);
109 | buffer.put(result, 0, result.length);
110 | }
111 |
112 | public byte[] convert(byte[] data) {
113 |
114 | // A buffer large enough for every case
115 | if (mBuffer==null || mBuffer.length != 3*mSliceHeight*mStride/2+mYPadding) {
116 | mBuffer = new byte[3*mSliceHeight*mStride/2+mYPadding];
117 | }
118 |
119 | if (!mPlanar) {
120 | if (mSliceHeight==mHeight && mStride==mWidth) {
121 | // Swaps U and V
122 | if (!mPanesReversed) {
123 | for (int i = mSize; i < mSize+mSize/2; i += 2) {
124 | mBuffer[0] = data[i+1];
125 | data[i+1] = data[i];
126 | data[i] = mBuffer[0];
127 | }
128 | }
129 | if (mYPadding>0) {
130 | System.arraycopy(data, 0, mBuffer, 0, mSize);
131 | System.arraycopy(data, mSize, mBuffer, mSize+mYPadding, mSize/2);
132 | return mBuffer;
133 | }
134 | return data;
135 | }
136 | } else {
137 | if (mSliceHeight==mHeight && mStride==mWidth) {
138 | // De-interleave U and V
139 | if (!mPanesReversed) {
140 | for (int i = 0; i < mSize/4; i+=1) {
141 | mBuffer[i] = data[mSize+2*i+1];
142 | mBuffer[mSize/4+i] = data[mSize+2*i];
143 | }
144 | } else {
145 | for (int i = 0; i < mSize/4; i+=1) {
146 | mBuffer[i] = data[mSize+2*i];
147 | mBuffer[mSize/4+i] = data[mSize+2*i+1];
148 | }
149 | }
150 | if (mYPadding == 0) {
151 | System.arraycopy(mBuffer, 0, data, mSize, mSize/2);
152 | } else {
153 | System.arraycopy(data, 0, mBuffer, 0, mSize);
154 | System.arraycopy(mBuffer, 0, mBuffer, mSize+mYPadding, mSize/2);
155 | return mBuffer;
156 | }
157 | return data;
158 | }
159 | }
160 |
161 | return data;
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/app/src/main/java/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
67 | mp4Parser = new MP4Parser(path);
68 |
69 | // We parse it
70 | try {
71 | mp4Parser.parse();
72 | } catch (IOException ignore) {
73 | // Maybe enough of the file has been parsed and we can get the stsd box
74 | }
75 |
76 | // We find the stsdBox
77 | stsdBox = mp4Parser.getStsdBox();
78 | mPPS = stsdBox.getB64PPS();
79 | mSPS = stsdBox.getB64SPS();
80 | mProfilLevel = stsdBox.getProfileLevel();
81 |
82 | // We're done !
83 | mp4Parser.close();
84 |
85 | }
86 |
87 | public String getProfileLevel() {
88 | return mProfilLevel;
89 | }
90 |
91 | public String getB64PPS() {
92 | Log.d(TAG, "PPS: "+mPPS);
93 | return mPPS;
94 | }
95 |
96 | public String getB64SPS() {
97 | Log.d(TAG, "SPS: "+mSPS);
98 | return mSPS;
99 | }
100 |
101 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/streaming/rtcp/SenderReport.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.rtcp;
22 |
23 | import java.io.IOException;
24 | import java.net.DatagramPacket;
25 | import java.net.InetAddress;
26 | import java.net.MulticastSocket;
27 | import java.nio.channels.IllegalSelectorException;
28 |
29 | import android.os.SystemClock;
30 | import android.util.Log;
31 |
32 | /**
33 | * Implementation of Sender Report RTCP packets.
34 | */
35 | public class SenderReport {
36 |
37 | public static final int MTU = 1500;
38 |
39 | private MulticastSocket usock;
40 | private DatagramPacket upack;
41 |
42 | private byte[] buffer = new byte[MTU];
43 | private int ssrc, port = -1;
44 | private int octetCount = 0, packetCount = 0;
45 | private long interval, delta, now, oldnow;
46 |
47 | public SenderReport(int ssrc) throws IOException {
48 | super();
49 | this.ssrc = ssrc;
50 | }
51 |
52 | public SenderReport() {
53 |
54 | /* Version(2) Padding(0) */
55 | /* ^ ^ PT = 0 */
56 | /* | | ^ */
57 | /* | -------- | */
58 | /* | |--------------------- */
59 | /* | || */
60 | /* | || */
61 | buffer[0] = (byte) Integer.parseInt("10000000",2);
62 |
63 | /* Packet Type PT */
64 | buffer[1] = (byte) 200;
65 |
66 | /* Byte 2,3 -> Length */
67 | setLong(28/4-1, 2, 4);
68 |
69 | /* Byte 4,5,6,7 -> SSRC */
70 | /* Byte 8,9,10,11 -> NTP timestamp hb */
71 | /* Byte 12,13,14,15 -> NTP timestamp lb */
72 | /* Byte 16,17,18,19 -> RTP timestamp */
73 | /* Byte 20,21,22,23 -> packet count */
74 | /* Byte 24,25,26,27 -> octet count */
75 |
76 | try {
77 | usock = new MulticastSocket();
78 | } catch (IOException e) {
79 | throw new RuntimeException(e.getMessage());
80 | }
81 | upack = new DatagramPacket(buffer, 1);
82 |
83 | // By default we sent one report every 5 secconde
84 | interval = 3000;
85 |
86 | }
87 |
88 | public void close() {
89 | usock.close();
90 | }
91 |
92 | /**
93 | * Sets the temporal interval between two RTCP Sender Reports.
94 | * Default interval is set to 5 secondes.
95 | * Set 0 to disable RTCP.
96 | * @param interval The interval in milliseconds
97 | */
98 | public void setInterval(long interval) {
99 | this.interval = interval;
100 | }
101 |
102 | /**
103 | * Updates the number of packets sent, and the total amount of data sent.
104 | * @param length The length of the packet
105 | * @throws IOException
106 | **/
107 | public void update(int length, long ntpts, long rtpts) throws IOException {
108 | packetCount += 1;
109 | octetCount += length;
110 | setLong(packetCount, 20, 24);
111 | setLong(octetCount, 24, 28);
112 |
113 | now = SystemClock.elapsedRealtime();
114 | delta += oldnow != 0 ? now-oldnow : 0;
115 | oldnow = now;
116 | if (interval>0) {
117 | if (delta>=interval) {
118 | // We send a Sender Report
119 | send(ntpts,rtpts);
120 | delta = 0;
121 | }
122 | }
123 |
124 | }
125 |
126 | public void setSSRC(int ssrc) {
127 | this.ssrc = ssrc;
128 | setLong(ssrc,4,8);
129 | packetCount = 0;
130 | octetCount = 0;
131 | setLong(packetCount, 20, 24);
132 | setLong(octetCount, 24, 28);
133 | }
134 |
135 | public void setDestination(InetAddress dest, int dport) {
136 | port = dport;
137 | upack.setPort(dport);
138 | upack.setAddress(dest);
139 | }
140 |
141 | public int getPort() {
142 | return port;
143 | }
144 |
145 | public int getLocalPort() {
146 | return usock.getLocalPort();
147 | }
148 |
149 | public int getSSRC() {
150 | return ssrc;
151 | }
152 |
153 | /**
154 | * Resets the reports (total number of bytes sent, number of packets sent, etc.)
155 | */
156 | public void reset() {
157 | packetCount = 0;
158 | octetCount = 0;
159 | setLong(packetCount, 20, 24);
160 | setLong(octetCount, 24, 28);
161 | delta = now = oldnow = 0;
162 | }
163 |
164 | private void setLong(long n, int begin, int end) {
165 | for (end--; end >= begin; end--) {
166 | buffer[end] = (byte) (n % 256);
167 | n >>= 8;
168 | }
169 | }
170 |
171 | /** Sends the RTCP packet over the network. */
172 | private void send(long ntpts, long rtpts) throws IOException {
173 | long hb = ntpts/1000000000;
174 | long lb = ( ( ntpts - hb*1000000000 ) * 4294967296L )/1000000000;
175 | setLong(hb, 8, 12);
176 | setLong(lb, 12, 16);
177 | setLong(rtpts, 16, 20);
178 | upack.setLength(28);
179 | usock.send(upack);
180 | }
181 |
182 |
183 | }
184 |
--------------------------------------------------------------------------------
/app/src/main/java/net/majorkernelpanic/streaming/rtp/AACLATMPacketizer.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.annotation.SuppressLint;
26 | import android.media.MediaCodec.BufferInfo;
27 | import android.os.SystemClock;
28 | import android.util.Log;
29 |
30 | /**
31 | * RFC 3640.
32 | *
33 | * Encapsulates AAC Access Units in RTP packets as specified in the RFC 3640.
34 | * This packetizer is used by the AACStream class in conjunction with the
35 | * MediaCodec API introduced in Android 4.1 (API Level 16).
36 | *
37 | */
38 | @SuppressLint("NewApi")
39 | public class AACLATMPacketizer extends AbstractPacketizer implements Runnable {
40 |
41 | private final static String TAG = "AACLATMPacketizer";
42 |
43 | private Thread t;
44 |
45 | public AACLATMPacketizer() {
46 | super();
47 | socket.setCacheSize(0);
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 | socket.setClockFrequency(samplingRate);
72 | }
73 |
74 | @SuppressLint("NewApi")
75 | public void run() {
76 |
77 | Log.d(TAG,"AAC LATM packetizer started !");
78 |
79 | int length = 0;
80 | long oldts;
81 | BufferInfo bufferInfo;
82 |
83 | try {
84 | while (!Thread.interrupted()) {
85 | buffer = socket.requestBuffer();
86 | length = is.read(buffer, rtphl+4, MAXPACKETSIZE-(rtphl+4));
87 |
88 | if (length>0) {
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 |
--------------------------------------------------------------------------------
/app/src/main/java/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 (sum