/*
Copyright (c) 2013-2016 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
*/
package org.easydarwin.blogdemos;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;
import org.easydarwin.blogdemos.hw.EncoderDebugger;
import org.easydarwin.blogdemos.hw.NV21Convertor;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import static android.R.attr.data;
/**
* @CreadBy ï¼DramaScript
* @date 2017/8/25
*/
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, View.OnClickListener {
String path = Environment.getExternalStorageDirectory() + "/vv831.h264";
int width = 640, height = 480;
int framerate, bitrate;
int mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
MediaCodec mMediaCodec;
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
Camera mCamera;
NV21Convertor mConvertor;
Button btnSwitch;
boolean started = false;
private Socket socket;
private SurfaceView video_play;
private AvcDecode mPlayer = null;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
Toast.makeText(MainActivity.this, "å¼å¯ç´æå¤±è´¥", Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(MainActivity.this, "è¿æ¥æå¡å¨å¤±è´¥", Toast.LENGTH_SHORT).show();
break;
case 3:
if (!started) {
startPreview();
} else {
stopPreview();
}
break;
case 4:
Toast.makeText(MainActivity.this, "socketå
³éäºè¿æ¥", Toast.LENGTH_SHORT).show();
break;
case 5:
Toast.makeText(MainActivity.this, "socketæå¼äºè¿æ¥", Toast.LENGTH_SHORT).show();
break;
}
}
};
private Thread threadListener;
@Override
protected void onCreate(Bundle savedInstanceState) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnSwitch = (Button) findViewById(R.id.btn_switch);
btnSwitch.setOnClickListener(this);
initMediaCodec();
surfaceView = (SurfaceView) findViewById(R.id.sv_surfaceview);
video_play = (SurfaceView) findViewById(R.id.video_play);
surfaceView.getHolder().addCallback(this);
surfaceView.getHolder().setFixedSize(getResources().getDisplayMetrics().widthPixels,
getResources().getDisplayMetrics().heightPixels);
}
@Override
protected void onResume() {
super.onResume();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
//ç³è¯·WRITE_EXTERNAL_STORAGEæé
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
1);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
doNext(requestCode, grantResults);
}
private void doNext(int requestCode, int[] grantResults) {
if (requestCode == 1) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
} else {
// Permission Denied
// displayFrameworkBugMessageAndExit();
Toast.makeText(this, "请å¨åºç¨ç®¡ç䏿å¼âç¸æºâè®¿é®æéï¼", Toast.LENGTH_LONG).show();
finish();
}
}
}
private void initMediaCodec() {
int dgree = getDgree();
framerate = 15;
bitrate = 2 * width * height * framerate / 20;
EncoderDebugger debugger = EncoderDebugger.debug(getApplicationContext(), width, height);
mConvertor = debugger.getNV21Convertor();
try {
mMediaCodec = MediaCodec.createByCodecName(debugger.getEncoderName());
MediaFormat mediaFormat;
if (dgree == 0) {
mediaFormat = MediaFormat.createVideoFormat("video/avc", height, width);
} else {
mediaFormat = MediaFormat.createVideoFormat("video/avc", width, height);
}
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
debugger.getEncoderColorFormat());
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMediaCodec.start();
} catch (IOException e) {
e.printStackTrace();
}
}
public static int[] determineMaximumSupportedFramerate(Camera.Parameters parameters) {
int[] maxFps = new int[]{0, 0};
List supportedFpsRanges = parameters.getSupportedPreviewFpsRange();
for (Iterator it = supportedFpsRanges.iterator(); it.hasNext(); ) {
int[] interval = it.next();
if (interval[1] > maxFps[1] || (interval[0] > maxFps[0] && interval[1] == maxFps[1])) {
maxFps = interval;
}
}
return maxFps;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("çç´æ");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getTitle().equals("çç´æ")) {
Intent intent = new Intent(this, WatchMovieActivity.class);
startActivity(intent);
}
return super.onOptionsItemSelected(item);
}
private boolean ctreateCamera(SurfaceHolder surfaceHolder) {
try {
mCamera = Camera.open(mCameraId);
Camera.Parameters parameters = mCamera.getParameters();
int[] max = determineMaximumSupportedFramerate(parameters);
Camera.CameraInfo camInfo = new Camera.CameraInfo();
Camera.getCameraInfo(mCameraId, camInfo);
int cameraRotationOffset = camInfo.orientation;
int rotate = (360 + cameraRotationOffset - getDgree()) % 360;
parameters.setRotation(rotate);
parameters.setPreviewFormat(ImageFormat.NV21);
List sizes = parameters.getSupportedPreviewSizes();
parameters.setPreviewSize(width, height);
parameters.setPreviewFpsRange(max[0], max[1]);
mCamera.setParameters(parameters);
// mCamera.autoFocus(null);
int displayRotation;
displayRotation = (cameraRotationOffset - getDgree() + 360) % 360;
mCamera.setDisplayOrientation(displayRotation);
mCamera.setPreviewDisplay(surfaceHolder);
return true;
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String stack = sw.toString();
Toast.makeText(this, stack, Toast.LENGTH_LONG).show();
destroyCamera();
e.printStackTrace();
return false;
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceHolder = holder;
ctreateCamera(surfaceHolder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stopPreview();
destroyCamera();
}
Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
byte[] mPpsSps = new byte[0];
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (data == null) {
return;
}
ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mMediaCodec.getOutputBuffers();
byte[] dst = new byte[data.length];
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
if (getDgree() == 0) {
dst = Util.rotateNV21Degree90(data, previewSize.width, previewSize.height);
} else {
dst = data;
}
try {
int bufferIndex = mMediaCodec.dequeueInputBuffer(5000000);
if (bufferIndex >= 0) {
inputBuffers[bufferIndex].clear();
mConvertor.convert(dst, inputBuffers[bufferIndex]);
mMediaCodec.queueInputBuffer(bufferIndex, 0,
inputBuffers[bufferIndex].position(),
System.nanoTime() / 1000, 0);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
//è®°å½ppsåsps
if (outData[0] == 0 && outData[1] == 0 && outData[2] == 0 && outData[3] == 1 && outData[4] == 103) {
mPpsSps = outData;
} else if (outData[0] == 0 && outData[1] == 0 && outData[2] == 0 && outData[3] == 1 && outData[4] == 101) {
//å¨å
³é®å¸§åé¢å ä¸ppsåspsæ°æ®
byte[] iframeData = new byte[mPpsSps.length + outData.length];
System.arraycopy(mPpsSps, 0, iframeData, 0, mPpsSps.length);
System.arraycopy(outData, 0, iframeData, mPpsSps.length, outData.length);
outData = iframeData;
}
// å°æ°æ®ç¨socketä¼ è¾
writeData(outData);
mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mMediaCodec.dequeueOutputBuffer(bufferInfo, 0);
}
} else {
Log.e("easypusher", "No buffer available !");
}
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
String stack = sw.toString();
Log.e("save_log", stack);
e.printStackTrace();
} finally {
mCamera.addCallbackBuffer(dst);
}
}
};
// è¾åºæµå¯¹è±¡
OutputStream outputStream;
/**
* å°æ°æ®ä¼ è¾ç»æå¡å¨
*
* @param outData
*/
private void writeData(final byte[] outData) {
new Thread() {
@Override
public void run() {
try {
if (!socket.isClosed()) {
if (socket.isConnected()) {
// æ¥éª¤1ï¼ä»Socket è·å¾è¾åºæµå¯¹è±¡OutputStream
// 该对象ä½ç¨ï¼åéæ°æ®
outputStream = socket.getOutputStream();
// æ¥éª¤2ï¼åå
¥éè¦åéçæ°æ®å°è¾åºæµå¯¹è±¡ä¸
outputStream.write(outData);
// ç¹å«æ³¨æï¼æ°æ®çç»å°¾å 䏿¢è¡ç¬¦æå¯è®©æå¡å¨ç«¯çreadline()忢é»å¡
// æ¥éª¤3ï¼åéæ°æ®å°æå¡ç«¯
outputStream.flush();
byte[] temp = new byte[4];
System.arraycopy(outData, 0, temp, 0, 4);
Log.e("writeSteam", "æ£å¨åå
¥æ°æ®é¿åº¦ï¼" + outData.length + ",åå个åèçå¼ï¼" + bytesToInt(temp, 0));
} else {
Log.e("writeSteam", "åé失败ï¼socketæå¼äºè¿æ¥");
}
} else {
Log.e("writeSteam", "åé失败ï¼socketå
³é");
}
} catch (IOException e) {
e.printStackTrace();
Log.e("writeSteam", "åå
¥æ°æ®å¤±è´¥");
}
}
}.start();
}
private List byteList = new ArrayList<>();
private List index = new ArrayList<>();
private int flag = 0;
private void startSocketListener() {
byte[] head = {0x00, 0x00, 0x00, 0x01};
// å©ç¨çº¿ç¨æ± ç´æ¥å¼å¯ä¸ä¸ªçº¿ç¨ & æ§è¡è¯¥çº¿ç¨
// æ¥éª¤1ï¼å建è¾å
¥æµå¯¹è±¡InputStream
threadListener = new Thread() {
@Override
public void run() {
super.run();
while (true) {
if (!socket.isClosed()) {
if (socket.isConnected()) {
try {
// æ¥éª¤1ï¼å建è¾å
¥æµå¯¹è±¡InputStream
InputStream is = socket.getInputStream();
if (is != null) {
DataInputStream input = new DataInputStream(is);
byte[] bytes = new byte[10000];
int le = input.read(bytes);
byte[] out = new byte[le];
System.arraycopy(bytes, 0, out, 0, out.length);
Util.save(out, 0, out.length, path, true);
Log.e("readSteam", "æ¥æ¶çæ°æ®é¿åº¦ï¼" +out.length);
if (le != -1) {
for (Byte b:byteList){
Log.e("after","䏿¬¡å©ä½æ°æ®ï¼"+b.byteValue());
}
for (byte data : out) {
byteList.add(data);
Log.e("tag","æ£å¨å½å
¥æ°æ®ï¼"+data);
for (Byte b:byteList){
Log.e("tagfter","å½å
¥ä¹åçæ°æ°æ®ï¼"+b.byteValue());
}
}
for (int i = 0; i < byteList.size(); i++) {
if (i + 3 <= out.length) {
if (byteList.get(i).byteValue() == 0x00 && byteList.get(i + 1).byteValue() == 0x00 && byteList.get(i + 2).byteValue() == 0x00 && byteList.get(i + 3).byteValue() == 0x01) {
index.add(i);
}
}
}
Log.e("index","index="+index.size());
if (index.size()>=2){
//æªåå
¶ä¸çä¸å¸§
byte[] frameBy = new byte[index.get(1)-index.get(0)];
int a = 0;
for (int i = index.get(0); i <=index.get(1)-1; i++) {
frameBy[a] = byteList.get(i).byteValue();
++a;
}
//ä¼ ç»H264è§£ç å¨
for (byte b:frameBy){
Log.e("indecode","ä¼ å
¥è§£ç çæ°æ®ï¼"+b);
}
if (frameBy.length!=0){
mPlayer.decodeH264(frameBy);
}
Log.e("tag","frameByçé¿åº¦ï¼"+frameBy.length);
//ä»éåä¸å é¤ä¸ä¸å¸§ä¹åçæ°æ®
for (int i = index.get(1)-1; i >=0; i--) {
byteList.remove(i);
}
for (Byte b:byteList){
Log.e("after","å é¤ä¹åçå©ä½æ°æ®ï¼"+b.byteValue());
}
index.clear();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
// Log.e("readSteam", "æ¥å失败ï¼socketæå¼äºè¿æ¥");
}
} else {
// Log.e("readSteam", "æ¥å失败ï¼socketå
³é");
}
}
}
};
threadListener.start();
}
public byte[] subBytes(byte[] src, int begin, int count) {
byte[] bs = new byte[count];
for (int i = begin; i < begin + count; i++) bs[i - begin] = src[i];
return bs;
}
public static int bytesToInt(byte[] src, int offset) {
int value;
value = (int) ((src[offset] & 0xFF)
| ((src[offset + 1] & 0xFF) << 8)
| ((src[offset + 2] & 0xFF) << 16)
| ((src[offset + 3] & 0xFF) << 24));
return value;
}
/**
* å¼å¯é¢è§
*/
public synchronized void startPreview() {
if (mCamera != null && !started) {
mCamera.startPreview();
int previewFormat = mCamera.getParameters().getPreviewFormat();
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
int size = previewSize.width * previewSize.height
* ImageFormat.getBitsPerPixel(previewFormat)
/ 8;
mCamera.addCallbackBuffer(new byte[size]);
mCamera.setPreviewCallbackWithBuffer(previewCallback);
started = true;
btnSwitch.setText("忢");
mPlayer = new AvcDecode(width, height, video_play.getHolder().getSurface());
startSocketListener();
}
}
/**
* 忢é¢è§
*/
public synchronized void stopPreview() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallbackWithBuffer(null);
started = false;
btnSwitch.setText("å¼å§");
try {
if (socket != null) {
if (socket.isConnected()) {
socket.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
// threadListener.stop();
}
}
/**
* 鿝Camera
*/
protected synchronized void destroyCamera() {
if (mCamera != null) {
mCamera.stopPreview();
try {
mCamera.release();
} catch (Exception e) {
}
mCamera = null;
}
}
private int getDgree() {
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break; // Natural orientation
case Surface.ROTATION_90:
degrees = 90;
break; // Landscape left
case Surface.ROTATION_180:
degrees = 180;
break;// Upside down
case Surface.ROTATION_270:
degrees = 270;
break;// Landscape right
}
return degrees;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_switch:
new Thread(new Runnable() {
@Override
public void run() {
socket = App.getInstance().getSocket();
Message msg = Message.obtain();
if (socket == null) {
msg.what = 1;
handler.sendMessage(msg);
} else if (!socket.isConnected()) {
msg.what = 2;
handler.sendMessage(msg);
} else {
msg.what = 3;
handler.sendMessage(msg);
}
}
}).start();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
destroyCamera();
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
}