import android.app.Service;
import android.content.Intent;
+import android.hardware.Camera;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Looper;
-import android.util.Log;
+import android.view.SurfaceHolder;
import android.widget.Toast;
import android.support.v4.app.NotificationCompat;
import android.graphics.BitmapFactory;
import android.app.Notification;
-import android.os.Message;
-
-public class MyService extends Service {
- private static final String TAG = "MyService";
- private Ffmpeg ffmpeg = Ffmpeg.getInstance();
- private Boolean isRunning = false;
-
- private Runnable runnable = new Runnable() {
- @Override
- public void run() {
- Log.e(TAG, "Run ffmpeg");
- isRunning = true;
- ffmpeg.push(null);
- }
- };
-
-
- /**
- * id不可设置为0,否则不能设置为前台service
- */
- private static final int NOTIFICATION_DOWNLOAD_PROGRESS_ID = 0x0001;
-
- //private boolean isRemove=false;//是否需要移除
-
- /**
- * Notification
- */
- public void createNotification(){
- Log.e(TAG, "create notification");
- //使用兼容版本
- NotificationCompat.Builder builder=new NotificationCompat.Builder(this);
- //设置状态栏的通知图标
- builder.setSmallIcon(R.mipmap.ic_launcher);
- //设置通知栏横条的图标
- builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background));
- //禁止用户点击删除按钮删除
- builder.setAutoCancel(false);
- //禁止滑动删除
- builder.setOngoing(true);
- //右上角的时间显示
- builder.setShowWhen(true);
- //设置通知栏的标题内容
- builder.setContentTitle("Rtmp Foreground Service!!!");
- //创建通知
- Notification notification = builder.build();
- //设置为前台服务
- startForeground(NOTIFICATION_DOWNLOAD_PROGRESS_ID,notification);
+import org.apache.log4j.Logger;
+import android.os.Binder;
+
+
+
+public class MyService extends Service implements Camera.PreviewCallback, Camera.ErrorCallback {
+
+ private static Logger gLogger = Logger.getLogger("MyService");
+ private Camera mCamera = null;
+ IBinder mBinder = new LocalBinder();
+ //private String rtmpUrl;
+ private long frameCount = 0;
+
+ // Preferred picture Size of the camera;
+ private Camera.Size mPreferredSize;
+
+
+ private MyServiceEventListener mListener;
+ public void setServiceEventListener(MyServiceEventListener listener){
+ mListener = listener;
+ }
+
+ public class LocalBinder extends Binder {
+ public MyService getServiceInstance(){
+ return MyService.this;
+ }
+ }
+
+ public Camera getCameraInstance() {
+ if (mCamera == null) {
+ CameraHandlerThread mThread = new CameraHandlerThread("ThreadCamera");
+ synchronized (mThread) {
+ mThread.openCamera();
+ }
+ }
+ return mCamera;
+ }
+
+ private void openCameraOriginal() {
+ try {
+ gLogger.error("openCameraOriginal");
+ Camera.CameraInfo info = new Camera.CameraInfo();
+ int numCameras = Camera.getNumberOfCameras();
+ int backId = -1;
+ int frontId = -1;
+ int camerId = 0;
+ gLogger.debug("Number of Cameras is " + numCameras);
+ for(int i = 0; i < numCameras; i++){
+ Camera.getCameraInfo(i, info);
+ if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK){
+ gLogger.debug("CAMERA_FACING_BACK id is " + i);
+ backId = i;
+ } else if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
+ gLogger.debug("CAMERA_FACING_FRONT id is " + i);
+ frontId = i;
+ }
+ }
+ if(frontId != -1) camerId = frontId;
+ else if(backId != -1) camerId = backId;
+ gLogger.debug("openCameraOriginal - camera id " + camerId);
+ mCamera = Camera.open(1);
+ } catch (Exception e) {
+ gLogger.error("openCameraOriginal - camera is not available. error: " + e.getMessage());
+ if(mListener != null) mListener.onCameraError("openCamera - error: " + e.getMessage());
+ }
}
+ private class CameraHandlerThread extends HandlerThread {
+ Handler mHandler;
+ public CameraHandlerThread(String name) {
+ super(name);
+ gLogger.error("CameraHandlerThread: " + name);
+ start();
+ mHandler = new Handler(getLooper());
+ }
+
+ synchronized void notifyCameraOpened() {
+ notify();
+ gLogger.debug("notifyCameraOpened");
+ }
+
+ void openCamera() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ openCameraOriginal();
+ notifyCameraOpened();
+ }
+ });
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ gLogger.error("wait was interrupted, " + e.getMessage());
+ }
+ }
+ }
+
+ private static final int NOTIFICATION_DOWNLOAD_PROGRESS_ID = 0x0001; //id不可设置为0,否则不能设置为前台service
+ private void createNotification(){
+ gLogger.debug("createNotification");
+ NotificationCompat.Builder builder=new NotificationCompat.Builder(this); //使用兼容版本
+ builder.setSmallIcon(R.mipmap.ic_launcher); //设置状态栏的通知图标
+ builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background)); //设置通知栏横条的图标
+ builder.setAutoCancel(false); //禁止用户点击删除按钮删除
+ builder.setOngoing(true); //禁止滑动删除
+ builder.setShowWhen(true); //右上角的时间显示
+ builder.setContentTitle("Rtmp Foreground Service!!!"); //设置通知栏的标题内容
+ Notification notification = builder.build(); //创建通知
+ startForeground(NOTIFICATION_DOWNLOAD_PROGRESS_ID,notification); //设置为前台服务
+ }
@Override
public IBinder onBind(Intent intent) {
- return null;
+ return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
- Log.e(TAG, "onCreate");
+ gLogger.error("onCreate ---> ");
+ createNotification();
+ Toast.makeText(this, "Started to Publish!", Toast.LENGTH_LONG).show();
+ mCamera = getCameraInstance();
+ if(mCamera != null){
+ configCamera(mCamera);
+ mCamera.setErrorCallback(this);
+ }
+
}
@Override
public void onDestroy() {
stopForeground(true);
- Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
- Log.e(TAG, "onDestroy");
super.onDestroy();
-
+ gLogger.error( "onDestroy --------->");
+ Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
+ if(mCamera != null){
+ try {
+ mCamera.stopPreview();
+ mCamera.setPreviewCallback(null);
+ mCamera.release();
+ mCamera = null;
+ } catch (Exception e){
+ gLogger.error("onDestroy - error " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
}
- @Override
- public void onStart(Intent intent, int startid){
- super.onStart(intent, startid);
- Log.e(TAG, "onStart");
-
- }
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- Log.e(TAG, "onStartCommand");
- if (!isRunning) {
- createNotification();
- Toast.makeText(this, "Ffmpeg started", Toast.LENGTH_LONG).show();
- new Thread(runnable).start();
- }
-
-// int i=intent.getExtras().getInt("cmd");
-// if(i==0){
-// if(!isRemove) {
-//
-// createNotification();
-// }
-// isRemove=true;
-// }else {
-// //移除前台服务
-// if (isRemove) {
-// stopForeground(true);
-// }
-// isRemove=false;
-// }
- //super.onStartCommand(intent, flags, startId);
+ gLogger.error("onStartCommand");
return START_STICKY;
}
@Override
public void onLowMemory(){
super.onLowMemory();
- Log.e(TAG, "onLowMemory");
+ gLogger.error("onLowMemory");
+ }
+
+ // Camera.PreviewCallback
+ @Override
+ public void onPreviewFrame(final byte[] data, Camera camera){
+ if(frameCount % (15 * 60) == 0) {
+ gLogger.error("onPreviewFrame");
+ }
+ if(FfmpegHelper.processFrame(data) != 0){
+ gLogger.error("onPreviewFrame, processFrame close");
+ FfmpegHelper.close();
+ if(mListener != null) mListener.onEncoderError("processFrame");
+ } else {
+ if(frameCount % (15 * 60) == 0){
+ if(mListener != null) mListener.onPublishing("processFrame OK");
+ }
+ }
+ frameCount++;
+ }
+
+ public void startPreview (SurfaceHolder holder){
+ gLogger.debug("startPreview");
+ if(mCamera == null){
+ gLogger.error("startPreview - error: camera is null");
+ return;
+ }
+ try {
+ mCamera.setPreviewDisplay(holder);
+ mCamera.startPreview();
+ } catch (Exception e){
+ gLogger.error("startPreview - error: " + e.getMessage());
+ e.printStackTrace();
+ if(mListener != null) mListener.onCameraError("setPreviewDisplay - " + e.getMessage());
+ }
+ }
+
+ public void reopenCamera() {
+ gLogger.debug("reopenCamera");
+ if(mCamera != null){
+ mCamera.stopPreview();
+ mCamera.release();
+ mCamera = null;
+ }
+ openCameraOriginal();
}
+ public boolean setRtmpUrl (String url){
+ //this.rtmpUrl = url;
+ if(mCamera == null || mPreferredSize == null){
+ gLogger.error("setRtmpUrl, error mCamera or PreferredSize is null");
+ return false;
+ }
+ gLogger.error("setRtmpUrl - size: " + mPreferredSize.width + "x" + mPreferredSize.height + ". url: " + url);
+ int ret = FfmpegHelper.initEncoder(mPreferredSize.width, mPreferredSize.height, url);
+ if(ret != 0){
+ gLogger.error("setRtmpUrl, initEncoder error");
+ }
+ return ret == 0;
+ }
+
+ private void configCamera(Camera camera){
+ Camera.Parameters paras = null;
+ try {
+ paras = camera.getParameters();
+ } catch (RuntimeException e){
+ gLogger.error("configCamera - " + e.getMessage());
+ if(mListener != null) mListener.onCameraError("getParameters - " + e.getMessage());
+ return;
+ }
+ gLogger.error("Supported Picture Sizes:");
+ Camera.Size preferredSize = paras.getSupportedPictureSizes().get(0);
+ for (Camera.Size cc : paras.getSupportedPictureSizes()){
+ if (cc.width == 640) // chose 640 x 480 if exists
+ preferredSize = cc;
+ gLogger.error(cc.width + "x" + cc.height);
+ }
+ gLogger.error("Supported Preview fps range:");
+ for(int[] i : paras.getSupportedPreviewFpsRange()){
+ gLogger.error("[" + i[0] + "," + i[1] + "]");
+ }
+ mPreferredSize = preferredSize;
+ paras.setPictureSize(preferredSize.width, preferredSize.height); // use 640x480 preferred
+ camera.setParameters(paras);
+ camera.setDisplayOrientation(0);
+ gLogger.error("Preview Format: " + paras.getPreviewFormat() + ". Size: " + paras.getPreviewSize().width + "x" + paras.getPreviewSize().height);
+ gLogger.error("Picture Format: " + paras.getPictureFormat() + ". Size: " + paras.getPictureSize().width + "x" + paras.getPictureSize().height);
+ camera.setPreviewCallback(this);
+ }
+
+ public Camera.Size getBestPictureSize(){
+ return mPreferredSize;
+ }
+
+ // Camaer.onError callback
+ @Override
+ public void onError(int error, Camera camera){
+ gLogger.error("Camera.OnError, " + error);
+ switch (error) {
+ case Camera.CAMERA_ERROR_SERVER_DIED:
+ gLogger.error("CAMERA_ERROR_SERVER_DIED");
+ break;
+ case Camera.CAMERA_ERROR_UNKNOWN:
+ gLogger.error("CAMERA_ERROR_UNKNOWN");
+ break;
+ }
+
+ if(mListener != null) mListener.onCameraError("OnError, " + error);
+ }
+
+
+ public interface MyServiceEventListener {
+ void onCameraError(String err);
+ void onEncoderError(String msg);
+ //void onIsPreviewing(String msg); // notify mainActivity if preview is running well
+ void onPublishing(String msg); // notify main activity if encoder is running well;
+ }
}