提交 4d59186e 编写于 作者: jinlong0603's avatar jinlong0603

更新最新的推送库,Android增加软编码及同屏功能

上级 f216f439
*.iml
.gradle
/.gradle
/.idea
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
Android
\ No newline at end of file
......@@ -3,9 +3,8 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="F:\01Tools\Android\android-studio\gradle\gradle-2.10" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
......
......@@ -7,9 +7,9 @@ android {
defaultConfig {
applicationId "org.easydarwin.easyipcamera"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0.16.0929"
targetSdkVersion 21
versionCode 4
versionName "1.2.17.0219"
}
buildTypes {
release {
......
......@@ -11,6 +11,11 @@
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".activity.EasyApplication"
android:allowBackup="true"
......@@ -37,7 +42,11 @@
android:name=".activity.StreameActivity"
android:screenOrientation="portrait" />
<receiver android:name="org.easydarwin.easyipcamera.updatemgr.CompleteReceiver" >
<service
android:name=".activity.RecordService"
android:enabled="true" />
<receiver android:name=".updatemgr.CompleteReceiver" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" />
......
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......@@ -30,7 +30,7 @@ public class EasyApplication extends Application {
mApplication = this;
if (Util.getSupportResolution(this).size() == 0) {
StringBuilder stringBuilder = new StringBuilder();
Camera camera = Camera.open();
Camera camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
List<Camera.Size> supportedPreviewSizes = camera.getParameters().getSupportedPreviewSizes();
for (Camera.Size str : supportedPreviewSizes) {
stringBuilder.append(str.width + "x" + str.height).append(";");
......
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
*/
package org.easydarwin.easyipcamera.activity;
import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.Process;
import android.support.annotation.Nullable;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.WindowManager;
import org.easydarwin.easyipcamera.camera.EasyIPCamera;
import org.easydarwin.easyipcamera.hw.EncoderDebugger;
import org.easydarwin.easyipcamera.util.Util;
import org.easydarwin.easyipcamera.view.StatusInfoView;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class RecordService extends Service implements EasyIPCamera.IPCameraCallBack{
private static final String TAG = "RecordService";
private String mVideoPath;
private MediaProjectionManager mMpmngr;
private MediaProjection mMpj;
private VirtualDisplay mVirtualDisplay;
private int windowWidth;
private int windowHeight;
private int screenDensity;
private Surface mSurface;
private MediaCodec mMediaCodec;
private WindowManager wm;
private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
static EasyIPCamera mEasyIPCamera;
private Thread mPushThread;
private byte[] mPpsSps;
// private static AudioStream mAudioStream;
private int mChannelId = 1;
private int mChannelState = 0;
private int mFrameRate = 25;
private int mBitRate;
private Context mApplicationContext;
private boolean codecAvailable = false;
private byte[] mVps = new byte[255];
private byte[] mSps = new byte[255];
private byte[] mPps = new byte[128];
private byte[] mMei = new byte[128];
private byte[] mH264Buffer;
private long timeStamp = System.currentTimeMillis();
private boolean mIsRunning = false;
private boolean mStartingService = false;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
mMpmngr = (MediaProjectionManager) getApplicationContext().getSystemService(MEDIA_PROJECTION_SERVICE);
// mAudioStream = new AudioStream(mEasyIPCamera);
mApplicationContext = getApplicationContext();
codecAvailable = false;
createEnvironment();
}
private void createEnvironment() {
mVideoPath = Environment.getExternalStorageDirectory().getPath() + "/";
wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
windowWidth = 720;//wm.getDefaultDisplay().getWidth();
windowHeight = 1280;//wm.getDefaultDisplay().getHeight();
DisplayMetrics displayMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(displayMetrics);
screenDensity = displayMetrics.densityDpi;
// while (windowWidth > 1080){
// windowWidth /= 2;
// windowHeight /=2;
// }
Log.d(TAG, String.format("kim createEnvironment Size=%dx%d", windowWidth, windowHeight));
EncoderDebugger debugger = EncoderDebugger.debug(mApplicationContext, windowWidth, windowHeight);
mSps = Base64.decode(debugger.getB64SPS(), Base64.NO_WRAP);
mPps = Base64.decode(debugger.getB64PPS(), Base64.NO_WRAP);
mH264Buffer = new byte[(int) (windowWidth*windowHeight*1.5)];
}
/**
* 初始化编码器
*/
private void initMediaCodec() {
mFrameRate = 25;
mBitRate = 1200000;
EncoderDebugger debugger = EncoderDebugger.debug(mApplicationContext, windowWidth, windowHeight);
mSps = Base64.decode(debugger.getB64SPS(), Base64.NO_WRAP);
mPps = Base64.decode(debugger.getB64PPS(), Base64.NO_WRAP);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private void startMediaCodec() {
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", windowWidth, windowHeight);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
mediaFormat.setInteger(MediaFormat.KEY_CAPTURE_RATE, mFrameRate);
mediaFormat.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / mFrameRate);
try {
mMediaCodec = MediaCodec.createEncoderByType("video/avc");
} catch (IOException e) {
e.printStackTrace();
}
mMediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface = mMediaCodec.createInputSurface();
codecAvailable = true;
mMediaCodec.start();
startPush();
}
/**
* 停止编码并释放编码资源占用
*/
private void stopMediaCodec() {
if (mMediaCodec != null) {
codecAvailable = false;
// mMediaCodec.stop();
// mMediaCodec.release();
// mMediaCodec = null;
}
stopPush();
}
private void startPush() {
if (mPushThread != null) return;
mPushThread = new Thread(){
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
// mAudioStream.startRecord();
while (mPushThread != null && codecAvailable) {
try {
int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 10000);
if ((EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_REQUEST_MEDIA_INFO != mChannelState) &&
(EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_REQUEST_PLAY_STREAM != mChannelState)) {
Log.e(TAG, "RecordService startPush state error! mChannelState=" + mChannelState);
continue;
}
if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {//请求超时
try {
// wait 10ms
Thread.sleep(10);
} catch (InterruptedException e) {
}
} else if (index >= 0) {//有效输出
ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(index);
//记录pps和sps
int type = outputBuffer.get(4) & 0x07;
if (type == 7 || type == 8) {
byte[] outData = new byte[mBufferInfo.size];
outputBuffer.get(outData);
mPpsSps = outData;
} else if (type == 5) {
//在关键帧前面加上pps和sps数据
//在关键帧前面加上pps和sps数据
System.arraycopy(mPpsSps, 0, mH264Buffer, 0, mPpsSps.length);
outputBuffer.get(mH264Buffer, mPpsSps.length, mBufferInfo.size);
mEasyIPCamera.pushFrame(mChannelId, EasyIPCamera.FrameFlag.EASY_SDK_VIDEO_FRAME_FLAG, System.currentTimeMillis(), mH264Buffer, 0,mPpsSps.length+mBufferInfo.size);
} else {
outputBuffer.get(mH264Buffer, 0, mBufferInfo.size);
if (System.currentTimeMillis() - timeStamp >= 3000) {
timeStamp = System.currentTimeMillis();
if (Build.VERSION.SDK_INT >= 23) {
Bundle params = new Bundle();
params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
mMediaCodec.setParameters(params);
}
}
mEasyIPCamera.pushFrame(mChannelId, EasyIPCamera.FrameFlag.EASY_SDK_VIDEO_FRAME_FLAG, System.currentTimeMillis(), mH264Buffer, 0,mPpsSps.length+mBufferInfo.size);
}
mMediaCodec.releaseOutputBuffer(index, false);
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
if(!codecAvailable){
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
}
};
mPushThread.start();
startVirtualDisplay();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void stopPush(){
Thread t = mPushThread;
if (t != null){
mPushThread = null;
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// mAudioStream.stop();
if (mMediaCodec != null) {
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void startVirtualDisplay() {
if (mMpj == null) {
mMpj = mMpmngr.getMediaProjection(StreameActivity.mResultCode, StreameActivity.mResultIntent);
StreameActivity.mResultCode = 0;
StreameActivity.mResultIntent = null;
}
mVirtualDisplay = mMpj.createVirtualDisplay("record_screen", windowWidth, windowHeight, screenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR| DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC| DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, mSurface, null, null);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private void release() {
Log.i(TAG, " release() ");
if (mSurface != null){
mSurface.release();
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mIsRunning = true;
mStartingService = true;
String strPort = EasyApplication.getEasyApplication().getPort();
final String strId = EasyApplication.getEasyApplication().getId();
if(strPort == null|| strPort.isEmpty() || strId == null || strId.isEmpty()) {
mStartingService = false;
return START_STICKY;
}
final int iport = Integer.parseInt(strPort);
if(mEasyIPCamera == null) {
mStartingService = false;
return START_STICKY;
}
mChannelId = mEasyIPCamera.registerCallback(this);
// mAudioStream.setChannelId(mChannelId);
// mAudioStream.startRecord();
new Thread(new Runnable() {
@Override
public void run() {
int result = -1;
while(mIsRunning && result < 0) {
result = mEasyIPCamera.startup(iport, EasyIPCamera.AuthType.AUTHENTICATION_TYPE_BASIC, "", "", "", 0, mChannelId, strId.getBytes());
if(result < 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.d(TAG, "kim startup result="+result);
}
initMediaCodec();
mStartingService = false;
}
}).start();
int ret = super.onStartCommand(intent, flags, startId);
return ret;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onDestroy() {
new Thread(new Runnable() {
@Override
public void run() {
mIsRunning = false;
while (mStartingService){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
stopPush();
setChannelState(0);
mEasyIPCamera.resetChannel(mChannelId);
int result = mEasyIPCamera.shutdown();
Log.d(TAG, "kim shutdown result="+result);
mEasyIPCamera.unrigisterCallback(RecordService.this);
mEasyIPCamera = null;
release();
if (mMpj != null) {
mMpj.stop();
}
//super.onDestroy();
}
}).start();
}
private void setChannelState(int state){
if(state <= 2) {
mChannelState = state;
// mAudioStream.setChannelState(state);
}
}
@Override
public void onIPCameraCallBack(int channelId, int channelState, byte[] mediaInfo, int userPtr) {
// Log.d(TAG, "kim onIPCameraCallBack, channelId="+channelId+", mChannelId="+mChannelId+", channelState="+channelState);
if(channelId != mChannelId)
return;
setChannelState(channelState);
switch(channelState){
case EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_ERROR:
Log.d(TAG, "Screen Record EASY_IPCAMERA_STATE_ERROR");
Util.showDbgMsg(StatusInfoView.DbgLevel.DBG_LEVEL_WARN, "Screen Record EASY_IPCAMERA_STATE_ERROR");
break;
case EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_REQUEST_MEDIA_INFO:
Util.showDbgMsg(StatusInfoView.DbgLevel.DBG_LEVEL_INFO, "Screen Record EASY_IPCAMERA_STATE_REQUEST_MEDIA_INFO");
ByteBuffer buffer = ByteBuffer.wrap(mediaInfo);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(EasyIPCamera.VideoCodec.EASY_SDK_VIDEO_CODEC_H264);
buffer.putInt(mFrameRate);
// buffer.putInt(mAudioStream.getAudioEncCodec());
// buffer.putInt(mAudioStream.getSamplingRate());
// buffer.putInt(mAudioStream.getChannelNum());
// buffer.putInt(mAudioStream.getBitsPerSample());
buffer.putInt(0);
buffer.putInt(0);
buffer.putInt(0);
buffer.putInt(0);
buffer.putInt(0);//vps length
buffer.putInt(mSps.length);
buffer.putInt(mPps.length);
buffer.putInt(0);
buffer.put(mVps);
buffer.put(mSps,0,mSps.length);
if(mSps.length < 255) {
buffer.put(mVps, 0, 255 - mSps.length);
}
buffer.put(mPps,0,mPps.length);
if(mPps.length < 128) {
buffer.put(mVps, 0, 128 - mPps.length);
}
buffer.put(mMei);
break;
case EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_REQUEST_PLAY_STREAM:
Util.showDbgMsg(StatusInfoView.DbgLevel.DBG_LEVEL_INFO, "Screen Record EASY_IPCAMERA_STATE_REQUEST_PLAY_STREAM");
startMediaCodec();
//mAudioStream.startPush();
break;
case EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_REQUEST_STOP_STREAM:
Util.showDbgMsg(StatusInfoView.DbgLevel.DBG_LEVEL_INFO, "Screen Record EASY_IPCAMERA_STATE_REQUEST_STOP_STREAM");
stopMediaCodec();
//mAudioStream.stopPush();
break;
default:
break;
}
}
}
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......@@ -8,15 +8,19 @@
package org.easydarwin.easyipcamera.activity;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.easydarwin.easyipcamera.R;
import org.easydarwin.easyipcamera.camera.MediaStream;
import org.easydarwin.easyipcamera.config.Config;
import org.easydarwin.easyipcamera.util.Util;
......@@ -38,6 +42,15 @@ public class SettingActivity extends AppCompatActivity {
txtPort.setText(port);
txtId.setText(id);
CheckBox x264enc = (CheckBox) findViewById(R.id.use_x264_encode);
x264enc.setChecked(PreferenceManager.getDefaultSharedPreferences(this).getBoolean("key-sw-codec", MediaStream.useSWCodec()));
x264enc.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
PreferenceManager.getDefaultSharedPreferences(SettingActivity.this).edit().putBoolean("key-sw-codec", isChecked).apply();
}
});
Button btnSave = (Button) findViewById(R.id.btn_save);
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
......
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
*/
package org.easydarwin.easyipcamera.activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Surface;
......@@ -25,6 +28,7 @@ import android.widget.Spinner;
import android.widget.TextView;
import org.easydarwin.easyipcamera.R;
import org.easydarwin.easyipcamera.camera.EasyIPCamera;
import org.easydarwin.easyipcamera.camera.MediaStream;
import org.easydarwin.easyipcamera.updatemgr.UpdateMgr;
import org.easydarwin.easyipcamera.util.Util;
......@@ -37,18 +41,21 @@ import java.util.List;
public class StreameActivity extends AppCompatActivity implements SurfaceHolder.Callback, View.OnClickListener {
static final String TAG = "StreameActivity";
public static final int REQUEST_MEDIA_PROJECTION = 1002;
//默认分辨率
int width = 640, height = 480;
Button btnSwitch;
Button btnSetting;
Button btnPushScreen;
TextView txtStreamAddress;
Button btnSwitchCemera;
Spinner spnResolution;
List<String> listResolution;
MediaStream mMediaStream;
private StatusInfoView mDbgInfoPrint;
private boolean mIsStarted = false;
static Intent mResultIntent;
static int mResultCode;
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -67,6 +74,8 @@ public class StreameActivity extends AppCompatActivity implements SurfaceHolder.
btnSetting.setOnClickListener(this);
btnSwitchCemera = (Button) findViewById(R.id.btn_switchCamera);
btnSwitchCemera.setOnClickListener(this);
btnPushScreen = (Button) findViewById(R.id.push_screen);
btnPushScreen.setOnClickListener(this);
txtStreamAddress = (TextView) findViewById(R.id.txt_stream_address);
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.sv_surfaceview);
surfaceView.getHolder().addCallback(this);
......@@ -89,7 +98,11 @@ public class StreameActivity extends AppCompatActivity implements SurfaceHolder.
mMediaStream.setDgree(getDgree());
initSpninner();
mIsStarted = false;
if (RecordService.mEasyIPCamera != null){
btnPushScreen.setText("停止推送屏幕");
TextView viewById = (TextView) findViewById(R.id.txt_stream_address);
viewById.setText(getRTSPAddr());
}
UpdateMgr update = new UpdateMgr(this);
update.checkUpdate();
......@@ -111,7 +124,7 @@ public class StreameActivity extends AppCompatActivity implements SurfaceHolder.
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spnResolution.setAdapter(adapter);
int position = listResolution.indexOf(String.format("%dx%d", width, height));
spnResolution.setSelection(position);
spnResolution.setSelection(position,false);
spnResolution.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
......@@ -144,8 +157,10 @@ public class StreameActivity extends AppCompatActivity implements SurfaceHolder.
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mMediaStream.stopPreview();
mMediaStream.stopStream();
mMediaStream.destroyCamera();
if(mMediaStream.isOpen()){
mMediaStream.stopChannel();
}
}
......@@ -169,27 +184,40 @@ public class StreameActivity extends AppCompatActivity implements SurfaceHolder.
return degrees;
}
private String getRTSPAddr(){
String ip = Util.getLocalIpAddress();
String port = EasyApplication.getEasyApplication().getPort();
String id = EasyApplication.getEasyApplication().getId();
return String.format("rtsp://%s:%s/%s", ip, port, id);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_switch:
StatusInfoView.getInstence().clearMsg();
if (!mIsStarted) {
if (!mMediaStream.isOpen()) {
btnSetting.setEnabled(false);
btnPushScreen.setEnabled(false);
btnSwitchCemera.setEnabled(false);
String ip = Util.getLocalIpAddress();
String port = EasyApplication.getEasyApplication().getPort();
String id = EasyApplication.getEasyApplication().getId();
mMediaStream.startStream(ip, port, id);
mMediaStream.startChannel(ip, port, id);
btnSwitch.setText("停止");
txtStreamAddress.setVisibility(View.VISIBLE);
StatusInfoView.getInstence().setVisibility(View.VISIBLE);
txtStreamAddress.setText(String.format("rtsp://%s:%s/%s", ip, port, id));
mIsStarted = true;
txtStreamAddress.setText(String.format(getRTSPAddr()));
} else {
txtStreamAddress.setVisibility(View.INVISIBLE);
StatusInfoView.getInstence().setVisibility(View.INVISIBLE);
mMediaStream.stopStream();
btnSwitch.setText("开始");
mIsStarted = false;
mMediaStream.stopChannel();
btnSwitch.setText("推送摄像头");
btnSetting.setEnabled(true);
btnPushScreen.setEnabled(true);
btnSwitchCemera.setEnabled(true);
}
break;
case R.id.btn_setting:
......@@ -204,14 +232,130 @@ public class StreameActivity extends AppCompatActivity implements SurfaceHolder.
case R.id.btn_switchCamera: {
mMediaStream.setDgree(getDgree());
mMediaStream.switchCamera();
break;
}
case R.id.push_screen:
if (!mMediaStream.isOpen()) {
spnResolution.setEnabled(true);
}
//sendMessage("");
boolean SWcodec = PreferenceManager.getDefaultSharedPreferences(EasyApplication.getEasyApplication()).getBoolean("key-sw-codec", false);
if(SWcodec){
new AlertDialog.Builder(this).setMessage("推送屏幕暂时只支持硬编码,请使用硬件编码。").setTitle("抱歉").show();
} else {
onPushScreen();
}
break;
default:
break;
}
}
private void startScreenPushIntent() {
if (StreameActivity.mResultIntent != null && StreameActivity.mResultCode != 0) {
RecordService.mEasyIPCamera = new EasyIPCamera();
RecordService.mEasyIPCamera.active(getApplicationContext());
Intent intent = new Intent(getApplicationContext(), RecordService.class);
startService(intent);
txtStreamAddress.setVisibility(View.VISIBLE);
txtStreamAddress.setText(String.format(getRTSPAddr()));
btnPushScreen.setText("停止推送屏幕");
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
MediaProjectionManager mMpMngr = (MediaProjectionManager) getApplicationContext().getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mMpMngr.createScreenCaptureIntent(), StreameActivity.REQUEST_MEDIA_PROJECTION);
}
}
}
public void onPushScreen() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
new AlertDialog.Builder(this).setMessage("推送屏幕需要安卓5.0以上,您当前系统版本过低,不支持该功能。").setTitle("抱歉").show();
return;
}
if (!PreferenceManager.getDefaultSharedPreferences(this).getBoolean("alert_screen_background_pushing", false)){
new AlertDialog.Builder(this).setTitle("提醒").setMessage("屏幕直播将要开始,直播过程中您可以切换到其它屏幕。不过记得直播结束后,再进来停止直播哦!").setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
PreferenceManager.getDefaultSharedPreferences(StreameActivity.this).edit().putBoolean("alert_screen_background_pushing", true).apply();
onPushScreen();
}
}).show();
return;
}
if (RecordService.mEasyIPCamera != null) {
Intent intent = new Intent(getApplicationContext(), RecordService.class);
stopService(intent);
TextView viewById = (TextView) findViewById(R.id.txt_stream_address);
viewById.setText(null);
btnPushScreen.setText("推送屏幕");
btnSwitch.setEnabled(true);
btnSwitchCemera.setEnabled(true);
btnSetting.setEnabled(true);
}else{
btnSwitch.setEnabled(false);
btnSwitchCemera.setEnabled(false);
btnSetting.setEnabled(false);
startScreenPushIntent();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_MEDIA_PROJECTION) {
if (resultCode == RESULT_OK) {
Log.e(TAG,"get capture permission success!");
mResultCode = resultCode;
mResultIntent = data;
startScreenPushIntent();
}
break;
}
}
@Override
protected void onDestroy() {
mMediaStream.destroyChannel();
StatusInfoView.getInstence().uninit();
super.onDestroy();
mMediaStream.destroyStream();
}
@Override
protected void onStop() {
StatusInfoView.getInstence().uninit();
super.onStop();
}
/**
* Take care of popping the fragment back stack or finishing the activity
* as appropriate.
*/
@Override
public void onBackPressed() {
boolean isStreaming = mMediaStream != null && mMediaStream.isOpen();
if (isStreaming && PreferenceManager.getDefaultSharedPreferences(this).getBoolean("key_enable_background_camera", true)){
if (!PreferenceManager.getDefaultSharedPreferences(this).getBoolean("background_camera_alert", false)){
new AlertDialog.Builder(this).setTitle("提醒").setMessage("您设置了使能摄像头后台采集,因此摄像头将会继续在后台采集并上传视频。记得直播结束后,再回来这里关闭直播。").setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
PreferenceManager.getDefaultSharedPreferences(StreameActivity.this).edit().putBoolean("background_camera_alert", true).apply();
StreameActivity.super.onBackPressed();
}
}).show();
return;
}
super.onBackPressed();
}else{
super.onBackPressed();
}
}
}
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......@@ -40,7 +40,8 @@ public class AudioStream {
private Thread encodeThread = null;
String TAG = "AudioStream";
//final String path = Environment.getExternalStorageDirectory() + "/123450001.aac";
boolean stoped = false;
private boolean stoped = false;
private boolean mPushAudio = false;
private int mChannelState = 0;
protected MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
......@@ -81,6 +82,7 @@ public class AudioStream {
private void init() {
try {
stoped=false;
mPushAudio = false;
mAudioEncCodec = EasyIPCamera.AudioCodec.EASY_SDK_AUDIO_CODEC_AAC;
mBitsPerSample = 16;//AudioFormat.ENCODING_PCM_16BIT;
mChannelNum = 1;
......@@ -154,20 +156,20 @@ public class AudioStream {
}
}
}
int size = mBufferInfo.size + 7;
byte[] buffer = new byte[size];
mBuffer.get(buffer);
Log.d(TAG, "kim mChannelState="+mChannelState+", mChannelId="+mChannelId+", length="+buffer.length);
if(mChannelState == EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_REQUEST_PLAY_STREAM) {
mEasyIPCamera.pushFrame(mChannelId, EasyIPCamera.FrameFlag.EASY_SDK_AUDIO_FRAME_FLAG, System.currentTimeMillis(), buffer);
int size = mBufferInfo.size + 7;
//Log.d(TAG, "kim mChannelState="+mChannelState+", mChannelId="+mChannelId+", length="+buffer.length);
if(mPushAudio && mChannelState == EasyIPCamera.ChannelState.EASY_IPCAMERA_STATE_REQUEST_PLAY_STREAM) {
// byte[] buffer = new byte[size];
// mBuffer.get(buffer, 0, size);
mEasyIPCamera.pushFrame(mChannelId, EasyIPCamera.FrameFlag.EASY_SDK_AUDIO_FRAME_FLAG, System.currentTimeMillis(), mBuffer.array(), 0, size);
}
if (mBuffer.position() >= size) { // read complete
mMediaCodec.releaseOutputBuffer(mIndex, false);
mBuffer = null;
}
} catch (RuntimeException e) {
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
......@@ -210,7 +212,7 @@ public class AudioStream {
}
}
}
} catch (RuntimeException e) {
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
......@@ -265,6 +267,14 @@ public class AudioStream {
stoped = true;
}
public void startPush(){
mPushAudio = true;
}
public void stopPush(){
mPushAudio = false;
}
public int getAudioEncCodec(){
return mAudioEncCodec;
}
......
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......@@ -7,6 +7,7 @@
package org.easydarwin.easyipcamera.camera;
import android.content.Context;
import android.util.Log;
import android.util.SparseArray;
......@@ -58,14 +59,14 @@ public class EasyIPCamera {
public static final int EASY_SDK_AUDIO_CODEC_G726 = 0x1100B; /* G726 */
}
int registerCallback(IPCameraCallBack cb) {
public int registerCallback(IPCameraCallBack cb) {
synchronized (sCallbacks) {
sCallbacks.put(++sKey, cb);
return sKey;
}
}
void unrigisterCallback(IPCameraCallBack cb) {
public void unrigisterCallback(IPCameraCallBack cb) {
synchronized (sCallbacks) {
int idx = sCallbacks.indexOfValue(cb);
if (idx != -1) {
......@@ -89,6 +90,8 @@ public class EasyIPCamera {
return;
}
public native int active(String key, Context context);
/* 启动 Rtsp Server */
/*设置监听端口, 回调函数及自定义数据 */
public native int startup(int listenport, int authType, String realm, String username, String password, int userptr,int channelid, byte[] channelinfo);
......@@ -97,11 +100,24 @@ public class EasyIPCamera {
public native int shutdown();
/* frame: 具体发送的帧数据 */
public native int pushFrame(int channelId, int avFrameFlag, long timestamp, byte[] pBuffer);
public native int pushFrame(int channelId, int avFrameFlag, long timestamp, byte[] pBuffer, int offset, int length);
public native int resetChannel(int channelId);
public interface IPCameraCallBack {
void onIPCameraCallBack(int channelId, int channelState, byte[] mediaInfo, int userPtr);
}
public int active(Context context){
String key = "6D72754B7A4A36526D343041344B68597031636670655276636D63755A57467A65575268636E64706269356C59584E356158426A5957316C636D4658444661672F365867523246326157346D516D466962334E68514449774D545A4659584E355247467964326C75564756686257566863336B3D";
int iRet = active(key, context);
if(iRet != 0){
Log.e(TAG, "Key invalid! active failed!!! return : " + iRet);
}
return iRet;
}
public int pushFrame(int channelId, int avFrameFlag, long timestamp, byte[] pBuffer){
return pushFrame(channelId, avFrameFlag, timestamp, pBuffer, 0, pBuffer.length);
}
}
\ No newline at end of file
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......
/*
Copyright (c) 2012-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2012-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......
package org.easydarwin.easyipcamera.sw;
/**
*/
public class JNIUtil {
static {
System.loadLibrary("Utils");
}
/**
* 都是Y:U:V = 4:1:1但 U与 V顺序相反。变换可逆
*
* @param buffer
* @param width
* @param height
*/
public static void yV12ToYUV420P(byte[] buffer, int width, int height) {
callMethod("YV12ToYUV420P", null, buffer, width, height);
}
/**
* 都是Y:U+V = 4:2,但是这两者U、V方向相反。变换可逆
*
* @param buffer
* @param width
* @param height
*/
public static void nV21To420SP(byte[] buffer, int width, int height) {
callMethod("NV21To420SP", null, buffer, width, height);
}
/**
* 旋转1个字节为单位的矩阵
*
* @param data 要旋转的矩阵
* @param offset 偏移量
* @param width 宽度
* @param height 高度
* @param degree 旋转度数
*/
public static void rotateMatrix(byte[] data, int offset, int width, int height, int degree) {
callMethod("RotateByteMatrix", null, data, offset, width, height, degree);
}
/**
* 旋转2个字节为单位的矩阵
*
* @param data 要旋转的矩阵
* @param offset 偏移量
* @param width 宽度
* @param height 高度
* @param degree 旋转度数
*/
public static void rotateShortMatrix(byte[] data, int offset, int width, int height, int degree) {
callMethod("RotateShortMatrix", null, data, offset, width, height, degree);
}
private static native void callMethod(String methodName, Object[] returnValue, Object... params);
}
package org.easydarwin.easyipcamera.sw;
public class X264Encoder {
static {
System.loadLibrary("x264enc");
}
private long mHandle;
/**
* 创建编码器
*
* @param w 要编码的视频的宽度
* @param h 要编码的视频的高度
* @param bitrate 要编码的码率
*/
public void create(int w, int h, int frameRate, int bitrate) {
long[] handle = new long[1];
create(w, h, frameRate, bitrate, handle);
mHandle = handle[0];
}
/**
* 获取头信息
*
* @param sps yv12格式的视频数据(数据长度应该为w*h*1.5)
* @param spslen 视频数据的偏移(即在yv12里的起始位)
* @param pps 编码后的数据。
* @param ppslen 编码后的视频数据的偏移(即在out里的起始位)
* @return returns negative on error, zero if success.
*/
public int getSpsPps(byte[] sps, int[] spslen, byte[] pps, int[] ppslen) {
return getSpsPps(mHandle, sps, spslen, pps, ppslen);
}
/**
* 编码
*
* @param yv12 yv12格式的视频数据(数据长度应该为w*h*1.5)
* @param offset 视频数据的偏移(即在yv12里的起始位)
* @param out 编码后的数据。
* @param outOffset 编码后的视频数据的偏移(即在out里的起始位)
* @param outLen outLen[0]为编码后的视频数据的长度
* @param keyFrame keyFrame[0]为编码后的视频帧的关键帧标识
* @return returns negative on error, zero if no NAL units returned.
*/
public int encode(byte[] yv12, int offset, byte[] out, int outOffset, int[] outLen, byte[] keyFrame) {
return encode(mHandle, yv12, offset, out, outOffset, outLen, keyFrame);
}
/**
* 关闭编码器
*/
public void close() {
close(mHandle);
}
private static native void create(int width, int height, int frameRate, int bitRate, long[] handle);
private static native int getSpsPps(long handle, byte[] sps, int[] spslen, byte[] pps, int[] ppslen);
private static native int encode(long handle, byte[] buffer, int offset, byte[] out, int outOffset, int[] outLen, byte[] keyFrame);
private static native void close(long handle);
}
/*
Copyright (c) 2013-2016 EasyDarwin.ORG. All rights reserved.
Copyright (c) 2013-2017 EasyDarwin.ORG. All rights reserved.
Github: https://github.com/EasyDarwin
WEChat: EasyDarwin
Website: http://www.easydarwin.org
......@@ -8,10 +8,18 @@
package org.easydarwin.easyipcamera.util;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.easydarwin.easyipcamera.activity.EasyApplication;
import org.easydarwin.easyipcamera.config.Config;
import org.easydarwin.easyipcamera.sw.JNIUtil;
import org.easydarwin.easyipcamera.view.StatusInfoView;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
......@@ -152,6 +160,30 @@ public class Util {
return dst;
}
/**
* 旋转YUV格式数据
*
* @param src YUV数据
* @param format 0,420P;1,420SP
* @param width 宽度
* @param height 高度
* @param degree 旋转度数
*/
public static void yuvRotate(byte[] src, int format, int width, int height, int degree) {
int offset = 0;
if (format == 0) {
JNIUtil.rotateMatrix(src, offset, width, height, degree);
offset += (width * height);
JNIUtil.rotateMatrix(src, offset, width / 2, height / 2, degree);
offset += width * height / 4;
JNIUtil.rotateMatrix(src, offset, width / 2, height / 2, degree);
} else if (format == 1) {
JNIUtil.rotateMatrix(src, offset, width, height, degree);
offset += width * height;
JNIUtil.rotateShortMatrix(src, offset, width / 2, height / 2, degree);
}
}
/**
* 保存数据到本地
*
......@@ -211,24 +243,50 @@ public class Util {
sharedPreferences.edit().putString(Config.K_RESOLUTION, value).commit();
}
private static String intToIp(int i) {
return (i & 0xFF ) + "." +
((i >> 8 ) & 0xFF) + "." +
((i >> 16 ) & 0xFF) + "." +
( i >> 24 & 0xFF) ;
}
/**
* 获取IP地址
*/
public static String getLocalIpAddress() {
String localIP = "";
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
localIP = inetAddress.getHostAddress().toString();
//获取wifi服务
WifiManager wifiManager = (WifiManager) EasyApplication.getEasyApplication().getSystemService(Context.WIFI_SERVICE);
//判断wifi是否开启
if (wifiManager.isWifiEnabled()) {
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();
String ip = intToIp(ipAddress);
return ip;
} else {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
return inetAddress.getHostAddress().toString();
}
}
}
} catch (SocketException ex) {
Log.e("getLocalIpAddress", ex.toString());
}
} catch (SocketException ex) {
ex.printStackTrace();
return null;
}
return localIP;
}
/**
* 显示视频debug信息
*/
public static void showDbgMsg(String level, String data){
Intent intent = new Intent(StatusInfoView.DBG_MSG);
intent.putExtra(StatusInfoView.DBG_LEVEL, level);
intent.putExtra(StatusInfoView.DBG_DATA, data);
LocalBroadcastManager.getInstance(EasyApplication.getEasyApplication()).sendBroadcast(intent);
}
}
package org.easydarwin.easyipcamera.view;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.support.v4.content.LocalBroadcastManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
......@@ -24,14 +28,12 @@ public class StatusInfoView extends View {
public StatusInfoView(Context context) {
super(context);
mContext = context;
mInfoList = new ArrayList<StatusInfo>();
mInfoList.clear();
init();
}
public StatusInfoView(Context context, AttributeSet attr) {
super(context,attr);
mContext = context;
mInfoList = new ArrayList<StatusInfo>();
mInfoList.clear();
init();
}
public static StatusInfoView getInstence(){
......@@ -43,6 +45,18 @@ public class StatusInfoView extends View {
handler.postDelayed(runnable, 1000);
}
private void init(){
mInfoList = new ArrayList<StatusInfo>();
mInfoList.clear();
final IntentFilter inf = new IntentFilter(DBG_MSG);
LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver, inf);
}
public void uninit(){
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
}
public void addInfoMsg(StatusInfo info){
mInfoList.add(info);
}
......@@ -86,6 +100,29 @@ public class StatusInfoView extends View {
}
}
//BroadCast
public static final String DBG_MSG = "dbg-msg";
public static final String DBG_LEVEL = "dbg-level";
public static final String DBG_DATA = "dbg-datas";
public static class DbgLevel {
public static final String DBG_LEVEL_INFO = "Info";
public static final String DBG_LEVEL_WARN = "Warn";
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DBG_MSG.equals(intent.getAction())) {
final String msg = intent.getStringExtra(DBG_DATA);
final String level = intent.getStringExtra(DBG_LEVEL);
if(!level.isEmpty() && !msg.isEmpty()) {
StatusInfo info = new StatusInfo(level, msg);
addInfoMsg(info);
}
}
}
};
public static class StatusInfo{
String level;
String values;
......
......@@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="org.easydarwin.easyipcamera.activity.StreameActivity">
tools:context=".activity.StreameActivity">
<SurfaceView
android:id="@+id/sv_surfaceview"
......@@ -28,63 +28,63 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:id="@+id/option_bar_container"
android:orientation="vertical"
android:layout_alignParentBottom="true"
android:text="开始"
android:background="@drawable/button_selector"
android:textColor="#ffffff" />
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_switch"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:background="@drawable/button_selector"
android:text="设置"
android:visibility="visible"
android:textColor="#ffffff" />
<Button
android:id="@+id/btn_record"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_setting"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:background="@drawable/button_selector"
android:text="录像"
android:textColor="#ffffff" />
<Button
android:id="@+id/btn_audio"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_record"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:background="@drawable/button_selector"
android:text="静音"
android:textColor="#ffffff" />
<Button
android:id="@+id/btn_switchCamera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_audio"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:background="@drawable/button_selector"
android:text="切换"
android:textColor="#ffffff" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/btn_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="推送摄像头"
android:background="@drawable/button_selector"
android:textColor="#ffffff" />
<Button
android:id="@+id/btn_switchCamera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_audio"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:background="@drawable/button_selector"
android:text="切换"
android:textColor="#ffffff" />
<Button
android:id="@+id/push_screen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_alignParentBottom="true"
android:text="推送屏幕"
android:background="@drawable/button_selector"
android:textColor="#ffffff" />
<Button
android:id="@+id/btn_setting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/btn_switch"
android:layout_alignParentBottom="true"
android:layout_marginLeft="5dp"
android:background="@drawable/button_selector"
android:text="设置"
android:visibility="visible"
android:textColor="#ffffff" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/txt_stream_address"
android:textColor="#ff0000"
android:layout_above="@+id/btn_switch"
android:layout_above="@+id/option_bar_container"
android:padding="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
......
......@@ -5,7 +5,7 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp"
tools:context="org.easydarwin.easyipcamera.activity.SettingActivity">
tools:context=".activity.SettingActivity">
<TextView
......@@ -19,8 +19,8 @@
android:id="@+id/edt_server_port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="EasyDarwin端口(默认554)"
android:text="554"
android:hint="EasyDarwin端口(默认8554)"
android:text="8554"
android:textColorHint="#80000000" />
<EditText
......@@ -30,6 +30,15 @@
android:hint="直播流ID"
android:textColorHint="#80000000" />
<CheckBox
android:id="@+id/use_x264_encode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="false"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:text="使用软编码" />
<Button
android:id="@+id/btn_save"
android:layout_width="match_parent"
......
......@@ -29,6 +29,6 @@
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:gravity="center"
android:text="Copyright 2012-2016 www.EasyDarwin.org" />
android:text="Copyright 2012-2017 www.EasyDarwin.org" />
</RelativeLayout>
\ No newline at end of file
......@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
......
......@@ -5,13 +5,13 @@
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Mon Aug 08 19:43:17 CST 2016
#Fri Feb 10 23:01:49 CST 2017
systemProp.http.proxyHost=mirrors.neusoft.edu.cn
systemProp.http.proxyPort=80
#Mon Dec 28 10:00:20 PST 2015
#Mon Dec 26 23:04:21 CST 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
......@@ -28,7 +28,11 @@ extern "C"
Easy_API int Easy_APICALL EasyRTSP_GetErrCode(Easy_RTSP_Handle handle);
/* 激活 */
#ifdef ANDROID
Easy_API int Easy_APICALL EasyRTSP_Activate(char *license, char* userPtr);
#else
Easy_API int Easy_APICALL EasyRTSP_Activate(char *license);
#endif
/* 创建RTSPClient句柄 返回0表示成功,返回非0表示失败 */
Easy_API int Easy_APICALL EasyRTSP_Init(Easy_RTSP_Handle *handle);
......@@ -40,7 +44,7 @@ extern "C"
Easy_API int Easy_APICALL EasyRTSP_SetCallback(Easy_RTSP_Handle handle, RTSPSourceCallBack _callback);
/* 打开网络流 */
Easy_API int Easy_APICALL EasyRTSP_OpenStream(Easy_RTSP_Handle handle, int _channelid, char *_url, RTP_CONNECT_TYPE _connType, unsigned int _mediaType, char *_username, char *_password, void *userPtr, int _reconn/*1000表示长连接,即如果网络断开自动重连, 其它值为连接次数*/, int outRtpPacket/*默认为0,即回调输出完整的帧, 如果为1,则输出RTP包*/, int heartbeatType/*0x00:不发送心跳 0x01:OPTIONS 0x02:GET_PARAMETER*/, int _verbosity/*日志打印输出等级,0表示不输出*/);
Easy_API int Easy_APICALL EasyRTSP_OpenStream(Easy_RTSP_Handle handle, int _channelid, char *_url, EASY_RTP_CONNECT_TYPE _connType, unsigned int _mediaType, char *_username, char *_password, void *userPtr, int _reconn/*1000表示长连接,即如果网络断开自动重连, 其它值为连接次数*/, int outRtpPacket/*默认为0,即回调输出完整的帧, 如果为1,则输出RTP包*/, int heartbeatType/*0x00:不发送心跳 0x01:OPTIONS 0x02:GET_PARAMETER*/, int _verbosity/*日志打印输出等级,0表示不输出*/);
/* 关闭网络流 */
Easy_API int Easy_APICALL EasyRTSP_CloseStream(Easy_RTSP_Handle handle);
......
......@@ -12,6 +12,20 @@
#include "EasyRTSPClientAPI.h"
#include "GetVPSSPSPPS.h"
#ifdef _WIN32
#define KEY_EASYIPCAMERA "6D72754B7A4969576B5A7341344B6859703163667065704659584E35535642445957316C636D4666556C52545543356C65475658444661672F365867523246326157346D516D466962334E68514449774D545A4659584E355247467964326C75564756686257566863336B3D"
#define KEY_EASYRTSPCLIENT "79393674363469576B5A7341344B6859703163667065704659584E35535642445957316C636D4666556C52545543356C65475658444661672F365867523246326157346D516D466962334E68514449774D545A4659584E355247467964326C75564756686257566863336B3D"
#elif def _ARM
#define KEY_EASYIPCAMERA "6D72754B7A502B2B72624941344B6859703163667065356C59584E356158426A5957316C636D4666636E527A6346634D5671442F7065424859585A7062695A4359574A76633246414D6A41784E6B566863336C4559584A33615735555A5746745A57467A65513D3D"
#define KEY_EASYRTSPCLIENT "79393674362F2B2B72624941344B6859703163667065356C59584E356158426A5957316C636D4666636E527A6346634D5671442F7065424859585A7062695A4359574A76633246414D6A41784E6B566863336C4559584A33615735555A5746745A57467A65513D3D"
#else //x86 linux
#define KEY_EASYIPCAMERA "6D72754B7A4A4F576B596F41344B6859703163667065356C59584E356158426A5957316C636D4666636E527A6346634D5671442F7065424859585A7062695A4359574A76633246414D6A41784E6B566863336C4559584A33615735555A5746745A57467A65513D3D"
#define KEY_EASYRTSPCLIENT "7939367436354F576B596F41344B6859703163667065356C59584E356158426A5957316C636D4666636E527A6346634D5671442F7065424859585A7062695A4359574A76633246414D6A41784E6B566863336C4559584A33615735555A5746745A57467A65513D3D"
#endif
#define RTSP_SOURCE1 "rtsp://admin:admin@192.168.2.100/11"
#define RTSP_SOURCE2 "rtsp://admin:admin@192.168.2.100/33"
typedef enum __SOURCE_TYPE_ENUM_T
{
SOURCE_TYPE_FILE = 0x01,
......@@ -166,7 +180,7 @@ Easy_I32 __EasyIPCamera_Callback(Easy_I32 channelId, EASY_IPCAMERA_STATE_T chann
EasyRTSP_Init(&pChannel[channelId].rtspHandle);
EasyRTSP_SetCallback(pChannel[channelId].rtspHandle, __RTSPSourceCallBack);
EasyRTSP_OpenStream(pChannel[channelId].rtspHandle, channelId, pChannel[channelId].source_uri, RTP_OVER_TCP, 1, pChannel[channelId].username, pChannel[channelId].password, (void *)&pChannel[channelId], 1000, 0, 0, 1);
EasyRTSP_OpenStream(pChannel[channelId].rtspHandle, channelId, pChannel[channelId].source_uri, EASY_RTP_OVER_TCP, 1, pChannel[channelId].username, pChannel[channelId].password, (void *)&pChannel[channelId], 1000, 0, 0, 1);
}
printf("[channel %d] Get media info...\n", channelId);
......@@ -236,14 +250,14 @@ int main()
if (i==0)
{
channel[i].sourceType = SOURCE_TYPE_RTSP;
strcpy(channel[i].source_uri, "rtsp://admin:admin@192.168.66.222/11");
strcpy(channel[i].source_uri, RTSP_SOURCE1);
strcpy(channel[i].username, "admin");
strcpy(channel[i].password, "admin");
}
else if (i==1)
{
channel[i].sourceType = SOURCE_TYPE_RTSP;
strcpy(channel[i].source_uri, "rtsp://admin:admin@192.168.66.222/33");
strcpy(channel[i].source_uri, RTSP_SOURCE2);
strcpy(channel[i].username, "admin");
strcpy(channel[i].password, "admin");
}
......@@ -252,7 +266,7 @@ int main()
}
//进程名: EasyIPCamera_RTSP.exe
int activate_ret = EasyRTSP_Activate("6A59754D6A3469576B5A754134703958714741634575704659584E35535642445957316C636D4666556C52545543356C65475658444661672F704C67523246326157346D516D466962334E68514449774D545A4659584E355247467964326C75564756686257566863336B3D");
int activate_ret = EasyRTSP_Activate(KEY_EASYRTSPCLIENT);
if (activate_ret < 0)
{
printf("激活libEasyRTSPClient失败: %d\n", activate_ret);
......@@ -266,7 +280,7 @@ int main()
strcpy(liveChannel[i].name, channel[i].name);
}
activate_ret = EasyIPCamera_Activate("6D7061506E4969576B5A734132786459702B676A6B75704659584E35535642445957316C636D4666556C52545543356C65475658444661672F704C67523246326157346D516D466962334E68514449774D545A4659584E355247467964326C75564756686257566863336B3D");
activate_ret = EasyIPCamera_Activate(KEY_EASYIPCAMERA);
if (activate_ret < 0)
{
printf("激活libEasyIPCamera失败: %d\n", activate_ret);
......
......@@ -430,7 +430,7 @@ int __EasyIPCamera_Callback(Easy_I32 channelId, EASY_IPCAMERA_STATE_T channelSta
//开始RTSP服务
int CServerManager::StartServer(int listenport, char *username, char *password, LIVE_CHANNEL_INFO_T *channelInfo, Easy_U32 channelNum)
{
EasyIPCamera_Activate("6D7061506E4969576B5A734132786459702B676A6B75744659584E35535642445957316C636D466656326C754C6D56345A536C58444661672F704C67523246326157346D516D466962334E68514449774D545A4659584E355247467964326C75564756686257566863336B3D");
EasyIPCamera_Activate("6D72754B7A4969576B5A7341344B6859703163667065744659584E35535642445957316C636D466656326C754C6D56345A577858444661672F365867523246326157346D516D466962334E68514449774D545A4659584E355247467964326C75564756686257566863336B3D");
//EasyIPCamera_Callback callback, void *userptr,
int ret = EasyIPCamera_Startup(listenport, AUTHENTICATION_TYPE_BASIC, (char*)"", (Easy_U8*)username, (Easy_U8*)password, __EasyIPCamera_Callback, this, channelInfo, channelNum);
......
......@@ -62,96 +62,96 @@ typedef int Easy_Error;
typedef enum __EASY_ACTIVATE_ERR_CODE_ENUM
{
EASY_ACTIVATE_INVALID_KEY = -1, //无效Key
EASY_ACTIVATE_TIME_ERR = -2, //时间错误
EASY_ACTIVATE_PROCESS_NAME_LEN_ERR = -3, //进程名称长度不匹配
EASY_ACTIVATE_PROCESS_NAME_ERR = -4, //进程名称不匹配
EASY_ACTIVATE_VALIDITY_PERIOD_ERR= -5, //有效期校验不一致
EASY_ACTIVATE_PLATFORM_ERR = -6, //平台不匹配
EASY_ACTIVATE_COMPANY_ID_LEN_ERR= -7, //授权使用商不匹配
EASY_ACTIVATE_SUCCESS = 0, //激活成功
EASY_ACTIVATE_INVALID_KEY = -1, //ÎÞЧKey
EASY_ACTIVATE_TIME_ERR = -2, //ʱ¼ä´íÎó
EASY_ACTIVATE_PROCESS_NAME_LEN_ERR = -3, //½ø³ÌÃû³Æ³¤¶È²»Æ¥Åä
EASY_ACTIVATE_PROCESS_NAME_ERR = -4, //½ø³ÌÃû³Æ²»Æ¥Åä
EASY_ACTIVATE_VALIDITY_PERIOD_ERR= -5, //ÓÐЧÆÚУÑé²»Ò»ÖÂ
EASY_ACTIVATE_PLATFORM_ERR = -6, //ƽ̨²»Æ¥Åä
EASY_ACTIVATE_COMPANY_ID_LEN_ERR= -7, //ÊÚȨʹÓÃÉ̲»Æ¥Åä
EASY_ACTIVATE_SUCCESS = 0, //¼¤»î³É¹¦
}EASY_ACTIVATE_ERR_CODE_ENUM;
/* 视频编码 */
/* ÊÓƵ±àÂë */
#define EASY_SDK_VIDEO_CODEC_H264 0x1C /* H264 */
#define EASY_SDK_VIDEO_CODEC_H265 0x48323635 /* 1211250229 */
#define EASY_SDK_VIDEO_CODEC_MJPEG 0x08 /* MJPEG */
#define EASY_SDK_VIDEO_CODEC_MPEG4 0x0D /* MPEG4 */
/* 音频编码 */
/* ÒôƵ±àÂë */
#define EASY_SDK_AUDIO_CODEC_AAC 0x15002 /* AAC */
#define EASY_SDK_AUDIO_CODEC_G711U 0x10006 /* G711 ulaw*/
#define EASY_SDK_AUDIO_CODEC_G711A 0x10007 /* G711 alaw*/
#define EASY_SDK_AUDIO_CODEC_G726 0x1100B /* G726 */
/* 音视频帧标识 */
#define EASY_SDK_VIDEO_FRAME_FLAG 0x00000001 /* 视频帧标志 */
#define EASY_SDK_AUDIO_FRAME_FLAG 0x00000002 /* 音频帧标志 */
#define EASY_SDK_EVENT_FRAME_FLAG 0x00000004 /* 事件帧标志 */
#define EASY_SDK_RTP_FRAME_FLAG 0x00000008 /* RTP帧标志 */
#define EASY_SDK_SDP_FRAME_FLAG 0x00000010 /* SDP帧标志 */
#define EASY_SDK_MEDIA_INFO_FLAG 0x00000020 /* 媒体类型标志*/
/* ÒôÊÓƵ֡±êʶ */
#define EASY_SDK_VIDEO_FRAME_FLAG 0x00000001 /* ÊÓƵ֡±êÖ¾ */
#define EASY_SDK_AUDIO_FRAME_FLAG 0x00000002 /* ÒôƵ֡±êÖ¾ */
#define EASY_SDK_EVENT_FRAME_FLAG 0x00000004 /* ʼþÖ¡±êÖ¾ */
#define EASY_SDK_RTP_FRAME_FLAG 0x00000008 /* RTPÖ¡±êÖ¾ */
#define EASY_SDK_SDP_FRAME_FLAG 0x00000010 /* SDPÖ¡±êÖ¾ */
#define EASY_SDK_MEDIA_INFO_FLAG 0x00000020 /* ýÌåÀàÐͱêÖ¾*/
/* 视频关键字标识 */
#define EASY_SDK_VIDEO_FRAME_I 0x01 /* I */
#define EASY_SDK_VIDEO_FRAME_P 0x02 /* P */
#define EASY_SDK_VIDEO_FRAME_B 0x03 /* B */
/* ÊÓƵ¹Ø¼ü×Ö±êʶ */
#define EASY_SDK_VIDEO_FRAME_I 0x01 /* IÖ¡ */
#define EASY_SDK_VIDEO_FRAME_P 0x02 /* PÖ¡ */
#define EASY_SDK_VIDEO_FRAME_B 0x03 /* BÖ¡ */
#define EASY_SDK_VIDEO_FRAME_J 0x04 /* JPEG */
/* 连接类型 */
typedef enum __RTP_CONNECT_TYPE
/* 连接类型 */
typedef enum __EASY_RTP_CONNECT_TYPE
{
RTP_OVER_TCP = 0x01, /* RTP Over TCP */
RTP_OVER_UDP /* RTP Over UDP */
}RTP_CONNECT_TYPE;
EASY_RTP_OVER_TCP = 0x01, /* RTP Over TCP */
EASY_RTP_OVER_UDP /* RTP Over UDP */
}EASY_RTP_CONNECT_TYPE;
/* 媒体信息 */
/* ýÌåÐÅÏ¢ */
typedef struct __EASY_MEDIA_INFO_T
{
Easy_U32 u32VideoCodec; /* 视频编码类型 */
Easy_U32 u32VideoFps; /* 视频帧率 */
Easy_U32 u32AudioCodec; /* 音频编码类型 */
Easy_U32 u32AudioSamplerate; /* 音频采样率 */
Easy_U32 u32AudioChannel; /* 音频通道数 */
Easy_U32 u32AudioBitsPerSample; /* 音频采样精度 */
Easy_U32 u32VpsLength; /* 视频vps帧长度 */
Easy_U32 u32SpsLength; /* 视频sps帧长度 */
Easy_U32 u32PpsLength; /* 视频pps帧长度 */
Easy_U32 u32SeiLength; /* 视频sei帧长度 */
Easy_U8 u8Vps[128]; /* 视频vps帧内容 */
Easy_U8 u8Sps[128]; /* 视频sps帧内容 */
Easy_U8 u8Pps[36]; /* 视频sps帧内容 */
Easy_U8 u8Sei[36]; /* 视频sei帧内容 */
Easy_U32 u32VideoCodec; /* ÊÓƵ±àÂëÀàÐÍ */
Easy_U32 u32VideoFps; /* ÊÓƵ֡ÂÊ */
Easy_U32 u32AudioCodec; /* ÒôƵ±àÂëÀàÐÍ */
Easy_U32 u32AudioSamplerate; /* ÒôƵ²ÉÑùÂÊ */
Easy_U32 u32AudioChannel; /* ÒôƵͨµÀÊý */
Easy_U32 u32AudioBitsPerSample; /* ÒôƵ²ÉÑù¾«¶È */
Easy_U32 u32VpsLength; /* ÊÓƵvpsÖ¡³¤¶È */
Easy_U32 u32SpsLength; /* ÊÓƵspsÖ¡³¤¶È */
Easy_U32 u32PpsLength; /* ÊÓƵppsÖ¡³¤¶È */
Easy_U32 u32SeiLength; /* ÊÓƵseiÖ¡³¤¶È */
Easy_U8 u8Vps[255]; /* ÊÓƵvpsÖ¡ÄÚÈÝ */
Easy_U8 u8Sps[255]; /* ÊÓƵspsÖ¡ÄÚÈÝ */
Easy_U8 u8Pps[128]; /* ÊÓƵspsÖ¡ÄÚÈÝ */
Easy_U8 u8Sei[128]; /* ÊÓƵseiÖ¡ÄÚÈÝ */
}EASY_MEDIA_INFO_T;
/* 帧信息 */
/* Ö¡ÐÅÏ¢ */
typedef struct
{
unsigned int codec; /* 音视频格式 */
unsigned int codec; /* ÒôÊÓƵ¸ñʽ */
unsigned int type; /* 视频帧类型 */
unsigned char fps; /* 视频帧率 */
unsigned short width; /* 视频宽 */
unsigned short height; /* 视频高 */
unsigned int type; /* ÊÓƵ֡ÀàÐÍ */
unsigned char fps; /* ÊÓƵ֡ÂÊ */
unsigned short width; /* ÊÓƵ¿í */
unsigned short height; /* ÊÓƵ¸ß */
unsigned int reserved1; /* 保留参数1 */
unsigned int reserved2; /* 保留参数2 */
unsigned int reserved1; /* ±£Áô²ÎÊý1 */
unsigned int reserved2; /* ±£Áô²ÎÊý2 */
unsigned int sample_rate; /* 音频采样率 */
unsigned int channels; /* 音频声道数 */
unsigned int bits_per_sample; /* 音频采样精度 */
unsigned int sample_rate; /* ÒôƵ²ÉÑùÂÊ */
unsigned int channels; /* ÒôƵÉùµÀÊý */
unsigned int bits_per_sample; /* ÒôƵ²ÉÑù¾«¶È */
unsigned int length; /* 音视频帧大小 */
unsigned int timestamp_usec; /* 时间戳,微妙 */
unsigned int timestamp_sec; /* 时间戳 秒 */
unsigned int length; /* ÒôÊÓƵ֡´óС */
unsigned int timestamp_usec; /* ʱ¼ä´Á,΢Ãî */
unsigned int timestamp_sec; /* ʱ¼ä´Á Ãë */
float bitrate; /* 比特率 */
float losspacket; /* 丢包率 */
float bitrate; /* ±ÈÌØÂÊ */
float losspacket; /* ¶ª°üÂÊ */
}RTSP_FRAME_INFO;
#endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册