├── AndroidH264StreamDemo
├── ic_launcher-web.png
├── libs
│ └── android-support-v4.jar
├── res
│ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ ├── values
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-v11
│ │ └── styles.xml
│ ├── menu
│ │ └── main.xml
│ ├── values-w820dp
│ │ └── dimens.xml
│ ├── values-v14
│ │ └── styles.xml
│ └── layout
│ │ ├── stream_dlg_view.xml
│ │ └── activity_main.xml
├── .settings
│ └── org.eclipse.jdt.core.prefs
├── .classpath
├── project.properties
├── proguard-project.txt
├── .project
├── AndroidManifest.xml
└── src
│ └── com
│ └── jemi
│ └── androidh264streamdemo
│ ├── AvcEncoder.java
│ └── MainActivity.java
├── .gitignore
└── README.md
/AndroidH264StreamDemo/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytestar/android-h264-stream-demo/HEAD/AndroidH264StreamDemo/ic_launcher-web.png
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytestar/android-h264-stream-demo/HEAD/AndroidH264StreamDemo/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytestar/android-h264-stream-demo/HEAD/AndroidH264StreamDemo/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytestar/android-h264-stream-demo/HEAD/AndroidH264StreamDemo/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytestar/android-h264-stream-demo/HEAD/AndroidH264StreamDemo/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bytestar/android-h264-stream-demo/HEAD/AndroidH264StreamDemo/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AndroidH264StreamDemo
5 | Hello world!
6 | Settings
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | 64dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/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 edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-21
15 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/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 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | AndroidH264StreamDemo
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/layout/stream_dlg_view.xml:
--------------------------------------------------------------------------------
1 |
10 |
16 |
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear on external disk
35 | .Spotlight-V100
36 | .Trashes
37 |
38 | # Directories potentially created on remote AFP share
39 | .AppleDB
40 | .AppleDesktop
41 | Network Trash Folder
42 | Temporary Items
43 | .apdisk
44 |
45 | # generated files
46 | bin/
47 | gen/
48 | .metadata/
49 | RemoteSystemsTempFiles/
50 | .gitattributes
51 |
52 | # Built application files
53 | *.apk
54 | *.ap_
55 |
56 | # Files for the Dalvik VM
57 | *.dex
58 |
59 | # Java class files
60 | *.class
61 |
62 | # Gradle files
63 | .gradle/
64 | build/
65 |
66 | # Local configuration file (sdk path, etc)
67 | local.properties
68 |
69 | # Proguard folder generated by Eclipse
70 | proguard/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # android-h264-stream-demo
2 |
3 | ## What it does
4 | This is an example project to show how to streaming from android camera to
5 | VLC or gstreamer.
6 |
7 | This demo project use MediaCodec API to encode H.264 data
8 | and simply wrap with UDP packet then send these packets to VLC or gstreamer.
9 |
10 | ## How does it work
11 | The working flow as below
12 | ```
13 | camera preview data(YV12) -> YUV420sp -> MediaCodec -> H.264 data -> UDP
14 | ```
15 |
16 | ## Media Server
17 |
18 | By using VLC and gstreamer, we can ensure that the H.264 video streaming is working fine.
19 | You can use the following command to start a media server.
20 |
21 | ### VLC
22 | ```
23 | vlc -vvv udp://@:
24 | ```
25 |
26 | ### gstreamer
27 | ```
28 | gst-launch udpsrc port= ! video/x-h264,width=,height=,framerate=/1 ! ffdec_h264 ! autovideosink
29 | ```
30 |
31 | ## Requirement
32 | - Minimal API requirement: 16
33 |
34 | ## License
35 | ```
36 | Copyright (C) 2015 Ping-Chun Tseng
37 | Licensed under Apache 2.0
38 | ```
39 |
40 | ## Notes
41 | - In some network environment, UDP packet may be cut by MTU limitation
42 |
43 |
44 | ## Future works
45 | - Support H.264 over RTP (RFC3984)
46 | - Support NV21 and YUV420p image format
47 |
48 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
15 |
23 |
24 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/AndroidH264StreamDemo/src/com/jemi/androidh264streamdemo/AvcEncoder.java:
--------------------------------------------------------------------------------
1 | package com.jemi.androidh264streamdemo;
2 |
3 | import java.io.ByteArrayOutputStream;
4 | import java.io.IOException;
5 | import java.nio.ByteBuffer;
6 |
7 | import android.media.MediaCodec;
8 | import android.media.MediaCodecInfo;
9 | import android.media.MediaCodecList;
10 | import android.media.MediaFormat;
11 |
12 | public class AvcEncoder
13 | {
14 | private final static String TAG = AvcEncoder.class.getSimpleName();
15 | private final static String MIME_TYPE = "video/avc";
16 | private final static int I_FRAME_INTERVAL = 1;
17 |
18 | MediaCodec mediaCodec;
19 | int width;
20 | int height;
21 | int timeoutUSec = 10000;
22 | long frameIndex = 0;
23 | byte[] spsPpsInfo = null;
24 | byte[] yuv420 = null;
25 | int frameRate;
26 | int yStride;
27 | int cStride;
28 | int ySize;
29 | int cSize;
30 | int halfWidth;
31 | int halfHeight;
32 |
33 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
34 |
35 | public AvcEncoder()
36 | {
37 | }
38 |
39 | public boolean init(int width, int height, int framerate, int bitrate)
40 | {
41 | try
42 | {
43 | mediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
44 | }
45 | catch (IOException e)
46 | {
47 | return false;
48 | }
49 |
50 | boolean isSupport = false;
51 | int colorFormat = 0;
52 | MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
53 | MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(MIME_TYPE);
54 | for (int i = 0; i < capabilities.colorFormats.length && colorFormat == 0; i++)
55 | {
56 | int format = capabilities.colorFormats[i];
57 | if (format == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar)
58 | {
59 | isSupport = true;
60 | break;
61 | }
62 | }
63 | if (!isSupport)
64 | return false;
65 |
66 | this.width = width;
67 | this.height = height;
68 | this.halfWidth = width / 2;
69 | this.halfHeight = height / 2;
70 | this.frameRate = framerate;
71 |
72 | this.yStride = (int) Math.ceil(width/16.0f) * 16;
73 | this.cStride = (int) Math.ceil(width/32.0f) * 16;
74 | this.ySize = yStride * height;
75 | this.cSize = cStride * height / 2;
76 |
77 | this.yuv420 = new byte[width*height*3/2];
78 |
79 | MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE, width, height);
80 | mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
81 | mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
82 | mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
83 | MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
84 | mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL);
85 |
86 | mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
87 | mediaCodec.start();
88 | return true;
89 | }
90 |
91 | public void close()
92 | {
93 | try
94 | {
95 | mediaCodec.stop();
96 | mediaCodec.release();
97 | }
98 | catch (Exception e)
99 | {
100 | e.printStackTrace();
101 | }
102 | }
103 |
104 | public byte[] offerEncoder(byte[] input)
105 | {
106 | YV12toYUV420PackedSemiPlanar(input, yuv420, width, height);
107 | try {
108 | ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
109 | ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
110 | int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
111 | if (inputBufferIndex >= 0)
112 | {
113 | long pts = computePresentationTime(frameIndex, frameRate);
114 | ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
115 | inputBuffer.clear();
116 | inputBuffer.put(yuv420, 0, yuv420.length);
117 | mediaCodec.queueInputBuffer(inputBufferIndex, 0, yuv420.length, pts, 0);
118 | frameIndex++;
119 | }
120 |
121 | MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
122 | int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, timeoutUSec);
123 |
124 | while (outputBufferIndex >= 0)
125 | {
126 | ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
127 | byte[] outData = new byte[bufferInfo.size];
128 | outputBuffer.get(outData);
129 |
130 | if (spsPpsInfo == null)
131 | {
132 | ByteBuffer spsPpsBuffer = ByteBuffer.wrap(outData);
133 | if (spsPpsBuffer.getInt() == 0x00000001)
134 | {
135 | spsPpsInfo = new byte[outData.length];
136 | System.arraycopy(outData, 0, spsPpsInfo, 0, outData.length);
137 | }
138 | else
139 | {
140 | return null;
141 | }
142 | }
143 | else
144 | {
145 | outputStream.write(outData);
146 | }
147 |
148 | mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
149 | outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, timeoutUSec);
150 | }
151 | byte[] ret = outputStream.toByteArray();
152 | if (ret.length > 5 && ret[4] == 0x65) //key frame need to add sps pps
153 | {
154 | outputStream.reset();
155 | outputStream.write(spsPpsInfo);
156 | outputStream.write(ret);
157 | }
158 |
159 | }
160 | catch (Throwable t)
161 | {
162 | t.printStackTrace();
163 | }
164 | byte[] ret = outputStream.toByteArray();
165 | outputStream.reset();
166 | return ret;
167 | }
168 |
169 | public byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height)
170 | {
171 | for (int i=0; i encDataList = new ArrayList();
55 | ArrayList encDataLengthList = new ArrayList();
56 |
57 | Runnable senderRun = new Runnable() {
58 | @Override
59 | public void run()
60 | {
61 | while (isStreaming)
62 | {
63 | boolean empty = false;
64 | byte[] encData = null;
65 |
66 | synchronized(encDataList)
67 | {
68 | if (encDataList.size() == 0)
69 | {
70 | empty = true;
71 | }
72 | else
73 | encData = encDataList.remove(0);
74 | }
75 | if (empty)
76 | {
77 | try {
78 | Thread.sleep(10);
79 | } catch (InterruptedException e) {
80 | e.printStackTrace();
81 | }
82 | continue;
83 | }
84 | try
85 | {
86 | DatagramPacket packet = new DatagramPacket(encData, encData.length, address, port);
87 | udpSocket.send(packet);
88 | }
89 | catch (IOException e)
90 | {
91 | e.printStackTrace();
92 | }
93 | }
94 | //TODO:
95 | }
96 | };
97 |
98 | @Override
99 | protected void onCreate(Bundle savedInstanceState)
100 | {
101 | super.onCreate(savedInstanceState);
102 | this.requestWindowFeature(Window.FEATURE_NO_TITLE);
103 | this.setContentView(R.layout.activity_main);
104 |
105 | this.findViewById(R.id.btnCamSize).setOnClickListener(
106 | new View.OnClickListener()
107 | {
108 | @Override
109 | public void onClick(View v)
110 | {
111 | showSettingsDlg();
112 | }
113 | });
114 |
115 | this.findViewById(R.id.btnStream).setOnClickListener(
116 | new View.OnClickListener()
117 | {
118 | @Override
119 | public void onClick(View v)
120 | {
121 | if (isStreaming)
122 | {
123 | ((Button)v).setText("Stream");
124 | stopStream();
125 | }
126 | else
127 | {
128 | showStreamDlg();
129 | }
130 | }
131 | });
132 |
133 | SurfaceView svCameraPreview = (SurfaceView) this.findViewById(R.id.svCameraPreview);
134 | this.previewHolder = svCameraPreview.getHolder();
135 | this.previewHolder.addCallback(this);
136 | }
137 |
138 | @Override
139 | protected void onPause()
140 | {
141 | this.stopStream();
142 |
143 |
144 | if (encoder != null)
145 | encoder.close();
146 |
147 | super.onPause();
148 | }
149 |
150 | @Override
151 | public boolean onCreateOptionsMenu(Menu menu)
152 | {
153 | getMenuInflater().inflate(R.menu.main, menu);
154 | return true;
155 | }
156 |
157 | @Override
158 | public boolean onOptionsItemSelected(MenuItem item)
159 | {
160 | int id = item.getItemId();
161 | if (id == R.id.action_settings)
162 | return true;
163 | return super.onOptionsItemSelected(item);
164 | }
165 |
166 | @Override
167 | public void onPreviewFrame(byte[] data, Camera camera)
168 | {
169 | this.camera.addCallbackBuffer(this.previewBuffer);
170 |
171 | if (this.isStreaming)
172 | {
173 | if (this.encDataLengthList.size() > 100)
174 | {
175 | Log.e(TAG, "OUT OF BUFFER");
176 | return;
177 | }
178 |
179 | byte[] encData = this.encoder.offerEncoder(data);
180 | if (encData.length > 0)
181 | {
182 | synchronized(this.encDataList)
183 | {
184 | this.encDataList.add(encData);
185 | }
186 | }
187 | }
188 | }
189 |
190 | @Override
191 | public void surfaceCreated(SurfaceHolder holder)
192 | {
193 | startCamera();
194 | }
195 |
196 | @Override
197 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
198 | {
199 | }
200 |
201 | @Override
202 | public void surfaceDestroyed(SurfaceHolder holder)
203 | {
204 | stopCamera();
205 | }
206 |
207 | private void startStream(String ip, int port)
208 | {
209 | SharedPreferences sp = this.getPreferences(Context.MODE_PRIVATE);
210 | int width = sp.getInt(SP_CAM_WIDTH, 0);
211 | int height = sp.getInt(SP_CAM_HEIGHT, 0);
212 |
213 | this.encoder = new AvcEncoder();
214 | this.encoder.init(width, height, DEFAULT_FRAME_RATE, DEFAULT_BIT_RATE);
215 |
216 | try
217 | {
218 | this.udpSocket = new DatagramSocket();
219 | this.address = InetAddress.getByName(ip);
220 | this.port = port;
221 | }
222 | catch (SocketException e)
223 | {
224 | // TODO Auto-generated catch block
225 | e.printStackTrace();
226 | return;
227 | }
228 | catch (UnknownHostException e)
229 | {
230 | // TODO Auto-generated catch block
231 | e.printStackTrace();
232 | return;
233 | }
234 | sp.edit().putString(SP_DEST_IP, ip).commit();
235 | sp.edit().putInt(SP_DEST_PORT, port).commit();
236 |
237 | this.isStreaming = true;
238 | Thread thrd = new Thread(senderRun);
239 | thrd.start();
240 |
241 | ((Button)this.findViewById(R.id.btnStream)).setText("Stop");
242 | this.findViewById(R.id.btnCamSize).setEnabled(false);
243 | }
244 |
245 | private void stopStream()
246 | {
247 | this.isStreaming = false;
248 |
249 | if (this.encoder != null)
250 | this.encoder.close();
251 | this.encoder = null;
252 |
253 | this.findViewById(R.id.btnCamSize).setEnabled(true);
254 | }
255 |
256 | private void startCamera()
257 | {
258 | SharedPreferences sp = this.getPreferences(Context.MODE_PRIVATE);
259 | int width = sp.getInt(SP_CAM_WIDTH, 0);
260 | int height = sp.getInt(SP_CAM_HEIGHT, 0);
261 | if (width == 0)
262 | {
263 | Camera tmpCam = Camera.open();
264 | Camera.Parameters params = tmpCam.getParameters();
265 | final List prevSizes = params.getSupportedPreviewSizes();
266 | int i = prevSizes.size()-1;
267 | width = prevSizes.get(i).width;
268 | height = prevSizes.get(i).height;
269 | sp.edit().putInt(SP_CAM_WIDTH, width).commit();
270 | sp.edit().putInt(SP_CAM_HEIGHT, height).commit();
271 | tmpCam.release();
272 | tmpCam = null;
273 | }
274 |
275 | this.previewHolder.setFixedSize(width, height);
276 |
277 | int stride = (int) Math.ceil(width/16.0f) * 16;
278 | int cStride = (int) Math.ceil(width/32.0f) * 16;
279 | final int frameSize = stride * height;
280 | final int qFrameSize = cStride * height / 2;
281 |
282 | this.previewBuffer = new byte[frameSize + qFrameSize * 2];
283 |
284 | try
285 | {
286 | camera = Camera.open();
287 | camera.setPreviewDisplay(this.previewHolder);
288 | Camera.Parameters params = camera.getParameters();
289 | params.setPreviewSize(width, height);
290 | params.setPreviewFormat(ImageFormat.YV12);
291 | camera.setParameters(params);
292 | camera.addCallbackBuffer(previewBuffer);
293 | camera.setPreviewCallbackWithBuffer(this);
294 | camera.startPreview();
295 | }
296 | catch (IOException e)
297 | {
298 | //TODO:
299 | }
300 | catch (RuntimeException e)
301 | {
302 | //TODO:
303 | }
304 | }
305 |
306 | private void stopCamera()
307 | {
308 | if (camera != null)
309 | {
310 | camera.setPreviewCallback(null);
311 | camera.stopPreview();
312 | camera.release();
313 | camera = null;
314 | }
315 | }
316 |
317 | private void showStreamDlg()
318 | {
319 | LayoutInflater inflater = this.getLayoutInflater();
320 | View content = inflater.inflate(R.layout.stream_dlg_view, null);
321 |
322 | SharedPreferences sp = this.getPreferences(Context.MODE_PRIVATE);
323 | String ip = sp.getString(SP_DEST_IP, "");
324 | int port = sp.getInt(SP_DEST_PORT, -1);
325 | if (ip.length() > 0)
326 | {
327 | EditText etIP = (EditText)content.findViewById(R.id.etIP);
328 | etIP.setText(ip);
329 | EditText etPort = (EditText)content.findViewById(R.id.etPort);
330 | etPort.setText(String.valueOf(port));
331 | }
332 |
333 | AlertDialog.Builder dlgBld = new AlertDialog.Builder(this);
334 | dlgBld.setTitle(R.string.app_name);
335 | dlgBld.setView(content);
336 | dlgBld.setPositiveButton(android.R.string.ok,
337 | new DialogInterface.OnClickListener()
338 | {
339 | @Override
340 | public void onClick(DialogInterface dialog, int which)
341 | {
342 | EditText etIP = (EditText) ((AlertDialog)dialog).findViewById(R.id.etIP);
343 | EditText etPort = (EditText) ((AlertDialog)dialog).findViewById(R.id.etPort);
344 | String ip = etIP.getText().toString();
345 | int port = Integer.valueOf(etPort.getText().toString());
346 | if (ip.length() > 0 && (port >=0 && port <= 65535))
347 | {
348 | startStream(ip, port);
349 | }
350 | else
351 | {
352 | //TODO:
353 | }
354 | }
355 | });
356 | dlgBld.setNegativeButton(android.R.string.cancel, null);
357 | dlgBld.show();
358 | }
359 |
360 | private void showSettingsDlg()
361 | {
362 | Camera.Parameters params = camera.getParameters();
363 | final List prevSizes = params.getSupportedPreviewSizes();
364 | String[] choiceStrItems = new String[prevSizes.size()];
365 | ArrayList choiceItems = new ArrayList();
366 | for (Size s : prevSizes)
367 | {
368 | choiceItems.add(s.width + "x" + s.height);
369 | }
370 | choiceItems.toArray(choiceStrItems);
371 |
372 | AlertDialog.Builder dlgBld = new AlertDialog.Builder(this);
373 | dlgBld.setTitle(R.string.app_name);
374 | dlgBld.setSingleChoiceItems(choiceStrItems, 0, null);
375 | dlgBld.setPositiveButton(android.R.string.ok,
376 | new DialogInterface.OnClickListener()
377 | {
378 | @Override
379 | public void onClick(DialogInterface dialog, int which)
380 | {
381 | int pos = ((AlertDialog)dialog).getListView().getCheckedItemPosition();
382 | Size s = prevSizes.get(pos);
383 | SharedPreferences sp = MainActivity.this.getPreferences(Context.MODE_PRIVATE);
384 | sp.edit().putInt(SP_CAM_WIDTH, s.width).commit();
385 | sp.edit().putInt(SP_CAM_HEIGHT, s.height).commit();
386 |
387 | stopCamera();
388 | startCamera();
389 | }
390 | });
391 | dlgBld.setNegativeButton(android.R.string.cancel, null);
392 | dlgBld.show();
393 | }
394 | }
395 |
--------------------------------------------------------------------------------