/*
* BackgroudService.java
* BackgroudService
*
* Github: https://github.com/daniulive/SmarterStreaming
*
* Created by DaniuLive on 2016/12/12.
* Copyright © 2014~2016 DaniuLive. All rights reserved.
*/
package com.daniulive.smartpublisher;
import java.nio.ByteBuffer;
import java.util.List;
import com.eventhandle.SmartEventCallback;
import com.voiceengine.NTAudioRecord;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
@SuppressLint({ "ClickableViewAccessibility", "NewApi" })
public class BackgroudService extends Service implements
SurfaceHolder.Callback, PreviewCallback {
private boolean mPreviewRunning = false;
private boolean is_running = false;
/**
* 窗口管理者
*/
private WindowManager mWindowManager;
// desk capture
private int mScreenDensity;
private int sreenWindowWidth;
private int screenWindowHeight;
private VirtualDisplay mVirtualDisplay;
private ImageReader mImageReader;
private MediaProjectionManager mMediaProjectionManager;
private MediaProjection mMediaProjection;
private static final String TAG = "SmartServicePublisher";
private static final int FRONT = 1; // 前置摄像头标记
private static final int BACK = 2; // 后置摄像头标记
private int currentCameraType = BACK; // 当前打开的摄像头标记
private static final int PORTRAIT = 1; // 竖屏
private static final int LANDSCAPE = 2; // 横屏
private int currentOrigentation = PORTRAIT;
private int curCameraIndex = -1;
@SuppressWarnings("deprecation")
private Camera mCamera = null;
private AutoFocusCallback myAutoFocusCallback = null;
private int videoWidth = 640;
private int videoHight = 480;
private int frameCount = 0;
NTAudioRecord audioRecord_ = null; // for audio capture
Notification notification = null;
private SurfaceView bgSurfaceView;
private SmartPublisherJni libPublisher = null;
private String txt = "当前状态";
private int audio_opt = 1;
private int video_opt = 1;
private final int PUSH_TYPE_SCREEN = 0;
private final int PUSH_TYPE_CAMERA = 1;
private int pushType = PUSH_TYPE_SCREEN;
private final int SCREEN_RESOLUTION_STANDARD = 0;
private final int SCREEN_RESOLUTION_LOW = 1;
private int screenResolution = SCREEN_RESOLUTION_STANDARD;
private String recDir = "/sdcard/daniulive/rec"; // for recorder path
private boolean is_need_local_recorder = false; // do not enable recorder in
// default
static {
System.load("libSmartPublisher.so");
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate..");
}
@SuppressWarnings("deprecation")
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.i(TAG, "onStart..");
pushType = intent.getExtras().getInt("PUSHTYPE");
videoWidth = intent.getExtras().getInt("CAMERAWIDTH");
videoHight = intent.getExtras().getInt("CAMERAHEIGHT");
screenResolution = intent.getExtras().getInt("SCREENRESOLUTION");
boolean isCameraFaceFront = intent.getExtras().getBoolean(
"SWITCHCAMERA");
if (isCameraFaceFront) {
currentCameraType = FRONT;
} else {
currentCameraType = BACK;
}
if (!is_running) {
// 对于6.0以上的设备
/*
* if (Build.VERSION.SDK_INT >= 23) { //如果支持悬浮窗功能 if
* (Settings.canDrawOverlays(getApplicationContext())) {
* showWindow(); } else { //手动去开启悬浮窗 Intent intent = new
* Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
* intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
* getApplicationContext().startActivity(intent); } } else {
* //6.0以下的设备直接开启 showWindow(); }
*/
mWindowManager = (WindowManager) getSystemService(Service.WINDOW_SERVICE);
if (pushType == PUSH_TYPE_CAMERA) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this,
0, intent, 0);
Intent bIntent = new Intent(this, BackgroudService.class);
PendingIntent deleteIntent = PendingIntent.getService(this, 0,
bIntent, 0);
notification = new Notification.Builder(this)
.setContentTitle("后台采集中。。").setAutoCancel(true)
.setDeleteIntent(deleteIntent)
.setContentIntent(contentIntent).build();
startForeground(android.os.Process.myPid(), notification);
bgSurfaceView = new SurfaceView(this);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
1, 1, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mWindowManager.addView(bgSurfaceView, layoutParams);
bgSurfaceView.getHolder().addCallback(this);
} else {
// 窗口管理者
createScreenEnvironment();
startRecorderScreen();
}
CheckInitAudioRecorder();
libPublisher = new SmartPublisherJni();
if (libPublisher == null)
return;
if (pushType == PUSH_TYPE_CAMERA) {
libPublisher.SmartPublisherInit(this.getApplicationContext(),
audio_opt, video_opt, videoWidth, videoHight);
} else {
libPublisher.SmartPublisherInit(this.getApplicationContext(),
audio_opt, video_opt, sreenWindowWidth,
screenWindowHeight);
}
libPublisher.SetSmartPublisherEventCallback(new EventHande());
boolean isHwEncoder = intent.getExtras().getBoolean("HWENCODER");
if (isHwEncoder) {
int kbps = setHardwareEncoderKbps(sreenWindowWidth,
screenWindowHeight);
Log.i(TAG, "hwHWKbps: " + kbps);
int isSupportHWEncoder = libPublisher
.SetSmartPublisherVideoHWEncoder(kbps);
if (isSupportHWEncoder == 0) {
Log.i(TAG, "Great, it supports hardware encoder!");
}
}
is_need_local_recorder = intent.getExtras().getBoolean("RECORDER");
ConfigRecorderFuntion();
String publishURL = intent.getStringExtra("PUBLISHURL");
Log.i(TAG, "publishURL: " + publishURL);
// IF not set url or url is empty, it will not publish stream
// if ( libPublisher.SmartPublisherSetURL("") != 0 )
if (libPublisher.SmartPublisherSetURL(publishURL) != 0) {
Log.e(TAG, "Failed to set publish stream URL..");
}
int isStarted = libPublisher.SmartPublisherStart();
if (isStarted != 0) {
Log.e(TAG, "Failed to publish stream..");
}
is_running = true;
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.i(TAG, "Service stopped..");
StopPublisher();
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind..");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind..");
return super.onUnbind(intent);
}
private void StopPublisher() {
if (is_running) {
StopPublish();
stopScreenCapture();
is_running = false;
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private boolean startScreenCapture() {
Log.i(TAG, "startScreenCapture..");
setupMediaProjection();
setupVirtualDisplay();
return true;
}
private int align(int d, int a) {
return (((d) + (a - 1)) & ~(a - 1));
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private void createScreenEnvironment() {
sreenWindowWidth = mWindowManager.getDefaultDisplay().getWidth();
screenWindowHeight = mWindowManager.getDefaultDisplay().getHeight();
if (sreenWindowWidth > 800) {
if (screenResolution == SCREEN_RESOLUTION_STANDARD) {
sreenWindowWidth = align(sreenWindowWidth / 2, 16);
screenWindowHeight = align(screenWindowHeight / 2, 16);
} else {
sreenWindowWidth = align(sreenWindowWidth * 2 / 5, 16);
screenWindowHeight = align(screenWindowHeight * 2 / 5, 16);
}
}
Log.i(TAG, "mWindowWidth : " + sreenWindowWidth + ",mWindowHeight : "
+ screenWindowHeight);
DisplayMetrics displayMetrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);
mScreenDensity = displayMetrics.densityDpi;
mImageReader = ImageReader.newInstance(sreenWindowWidth,
screenWindowHeight, 0x1, 2);
mMediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
}
@SuppressLint("NewApi")
private void setupMediaProjection() {
mMediaProjection = mMediaProjectionManager.getMediaProjection(
MainActivity.mResultCode, MainActivity.mResultData);
}
@SuppressLint("NewApi")
private void setupVirtualDisplay() {
mVirtualDisplay = mMediaProjection.createVirtualDisplay(
"ScreenCapture", sreenWindowWidth, screenWindowHeight,
mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mImageReader.getSurface(), null, null);
mImageReader.setOnImageAvailableListener(
new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = mImageReader.acquireLatestImage();
if (image != null) {
processScreenImage(image);
image.close();
}
}
}, null);
}
private void startRecorderScreen() {
Log.i(TAG, "record start..");
if (startScreenCapture()) {
new Thread() {
@Override
public void run() {
Log.i(TAG, "start record..");
}
}.start();
}
}
/**
* Process image data as desired.
*/
@SuppressLint("NewApi")
private void processScreenImage(Image image) {
int width = image.getWidth();
int height = image.getHeight();
final Image.Plane[] planes = image.getPlanes();
final ByteBuffer buffer = planes[0].getBuffer();
int rowStride = planes[0].getRowStride();
libPublisher.SmartPublisherOnCaptureVideoRGBAData(buffer, rowStride,
width, height);
}
@SuppressLint("NewApi")
private void stopScreenCapture() {
if (pushType == PUSH_TYPE_CAMERA) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
}
class EventHande implements SmartEventCallback {
@Override
public void onCallback(int code, long param1, long param2,
String param3, String param4, Object param5) {
switch (code) {
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:
txt = "开始。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:
txt = "连接中。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:
txt = "连接失败。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:
txt = "连接成功。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:
txt = "连接断开。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:
txt = "关闭。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
Log.i(TAG, "开始一个新的录像文件 : " + param3);
txt = "开始一个新的录像文件。。";
break;
case EVENTID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
Log.i(TAG, "已生成一个录像文件 : " + param3);
txt = "已生成一个录像文件。。";
break;
}
String str = "当前回调状态:" + txt;
Log.i(TAG, str);
}
}
void CheckInitAudioRecorder() {
if (audioRecord_ == null) {
audioRecord_ = new NTAudioRecord(this, 1);
}
if (audioRecord_ != null) {
Log.i(TAG, "onCreate, call executeAudioRecordMethod..");
audioRecord_.executeAudioRecordMethod();
}
}
private void StopPublish() {
if (audioRecord_ != null) {
Log.i(TAG, "surfaceDestroyed, call StopRecording..");
audioRecord_.StopRecording();
audioRecord_ = null;
}
if (libPublisher != null) {
libPublisher.SmartPublisherStop();
}
}
private int setHardwareEncoderKbps(int width, int height) {
int hwEncoderKpbs = 0;
if (width < 200) {
hwEncoderKpbs = 300;
} else if (width < 400) {
hwEncoderKpbs = 500;
} else if (width < 600) {
hwEncoderKpbs = 800;
} else if (width < 800) {
hwEncoderKpbs = 1100;
} else if (width < 1000) {
hwEncoderKpbs = 1500;
} else if (width < 1300) {
hwEncoderKpbs = 2000;
} else {
hwEncoderKpbs = 1000;
}
return hwEncoderKpbs;
}
// Configure recorder related function.
void ConfigRecorderFuntion() {
if (libPublisher != null) {
if (is_need_local_recorder) {
if (recDir != null && !recDir.isEmpty()) {
int ret = libPublisher
.SmartPublisherCreateFileDirectory(recDir);
if (0 == ret) {
if (0 != libPublisher
.SmartPublisherSetRecorderDirectory(recDir)) {
Log.e(TAG, "Set recoder dir failed , path:"
+ recDir);
return;
}
if (0 != libPublisher.SmartPublisherSetRecorder(1)) {
Log.e(TAG, "SmartPublisherSetRecoder failed.");
return;
}
if (0 != libPublisher
.SmartPublisherSetRecorderFileMaxSize(200)) {
Log.e(TAG,
"SmartPublisherSetRecoderFileMaxSize failed.");
return;
}
} else {
Log.e(TAG, "Create recoder dir failed, path:" + recDir);
}
}
} else {
if (0 != libPublisher.SmartPublisherSetRecorder(0)) {
Log.e(TAG, "SmartPublisherSetRecoder failed.");
return;
}
}
}
}
/* it will call when surfaceChanged */
@SuppressWarnings("deprecation")
private void initCamera(SurfaceHolder holder) {
Log.i(TAG, "initCamera..");
if (mPreviewRunning)
mCamera.stopPreview();
Camera.Parameters parameters;
try {
parameters = mCamera.getParameters();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
parameters.setPictureSize(videoWidth, videoHight);
parameters.setPreviewSize(videoWidth, videoHight);
parameters.setPictureFormat(PixelFormat.JPEG);
parameters.setPreviewFormat(PixelFormat.YCbCr_420_SP);
SetCameraFPS(parameters);
mCamera.setParameters(parameters);
int bufferSize = (((videoWidth | 0xf) + 1) * videoHight * ImageFormat
.getBitsPerPixel(parameters.getPreviewFormat())) / 8;
mCamera.addCallbackBuffer(new byte[bufferSize]);
mCamera.setPreviewCallbackWithBuffer(this);
try {
mCamera.setPreviewDisplay(holder);
} catch (Exception ex) {
// TODO Auto-generated catch block
if (null != mCamera) {
mCamera.release();
mCamera = null;
}
ex.printStackTrace();
}
mCamera.startPreview();
mCamera.autoFocus(myAutoFocusCallback);
mPreviewRunning = true;
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private Camera openCamera(int type) {
int frontIndex = -1;
int backIndex = -1;
int cameraCount = Camera.getNumberOfCameras();
Log.i(TAG, "cameraCount: " + cameraCount);
CameraInfo info = new CameraInfo();
for (int cameraIndex = 0; cameraIndex < cameraCount; cameraIndex++) {
Camera.getCameraInfo(cameraIndex, info);
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
frontIndex = cameraIndex;
} else if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
backIndex = cameraIndex;
}
}
currentCameraType = type;
if (type == FRONT && frontIndex != -1) {
curCameraIndex = frontIndex;
return Camera.open(frontIndex);
} else if (type == BACK && backIndex != -1) {
curCameraIndex = backIndex;
return Camera.open(backIndex);
}
return null;
}
@SuppressWarnings("deprecation")
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
frameCount++;
if (frameCount % 3000 == 0) {
Log.i("OnPre", "gc+");
System.gc();
Log.i("OnPre", "gc-");
}
if (data == null) {
@SuppressWarnings("deprecation")
Parameters params = camera.getParameters();
Size size = params.getPreviewSize();
int bufferSize = (((size.width | 0x1f) + 1) * size.height * ImageFormat
.getBitsPerPixel(params.getPreviewFormat())) / 8;
camera.addCallbackBuffer(new byte[bufferSize]);
} else {
if (is_running) {
// Log.i(TAG, "callback data length: " + data.length);
libPublisher.SmartPublisherOnCaptureVideoData(data,
data.length, currentCameraType, currentOrigentation);
}
camera.addCallbackBuffer(data);
}
}
@SuppressWarnings("deprecation")
private void SetCameraFPS(Camera.Parameters parameters) {
if (parameters == null)
return;
int[] findRange = null;
int defFPS = 20 * 1000;
List<int[]> fpsList = parameters.getSupportedPreviewFpsRange();
if (fpsList != null && fpsList.size() > 0) {
for (int i = 0; i < fpsList.size(); ++i) {
int[] range = fpsList.get(i);
if (range != null
&& Camera.Parameters.PREVIEW_FPS_MIN_INDEX < range.length
&& Camera.Parameters.PREVIEW_FPS_MAX_INDEX < range.length) {
Log.i(TAG, "Camera index:" + i + " support min fps:"
+ range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);
Log.i(TAG, "Camera index:" + i + " support max fps:"
+ range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
if (findRange == null) {
if (defFPS <= range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]) {
findRange = range;
Log.i(TAG,
"Camera found appropriate fps, min fps:"
+ range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]
+ " ,max fps:"
+ range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
}
}
if (findRange != null) {
parameters.setPreviewFpsRange(
findRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
findRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
// Check if it has front camera
private int findFrontCamera() {
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras();
for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
return camIdx;
}
}
return -1;
}
// Check if it has back camera
private int findBackCamera() {
int cameraCount = 0;
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
cameraCount = Camera.getNumberOfCameras();
for (int camIdx = 0; camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
return camIdx;
}
}
return -1;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated");
if (pushType == PUSH_TYPE_CAMERA) {
try {
// User could check if the device has back/front camera here..
/*
* int CammeraIndex = findBackCamera(); Log.i(TAG,
* "BackCamera: " + CammeraIndex);
*
* if (CammeraIndex == -1) { CammeraIndex = findFrontCamera();
* currentCameraType = FRONT; if (CammeraIndex == -1) {
* Log.i(TAG, "NO camera!!"); return; } } else {
* currentCameraType = BACK; }
*/
if (mCamera == null) {
mCamera = openCamera(currentCameraType);
}
} catch (Exception e) {
e.printStackTrace();
}
initCamera(holder);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.i(TAG, "surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(TAG, "surfaceDestroyed");
if (pushType == PUSH_TYPE_CAMERA) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
}
if (libPublisher != null) {
libPublisher.SmartPublisherStop();
}
}
}