clean the code, and fix bug service stopped when back pressed
[rtmpclient.git] / app / src / main / java / ai / suanzi / rtmpclient / MyService.java
index c5ec172..2d49e9c 100644 (file)
@@ -2,124 +2,270 @@ package ai.suanzi.rtmpclient;
 
 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 = new Ffmpeg();
-    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 int width = 0;
+    private int height = 0;
+
+
+    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("camera thread");
+            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;
+            mCamera = Camera.open(camerId);
+        } 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();
+        }
 
+        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){
+            mCamera.stopPreview();
+            mCamera.release();
+        }
     }
 
-    @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");
+        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() {
+        if(mCamera != null){
+            mCamera.stopPreview();
+            mCamera.release();
+        }
+        openCameraOriginal();
+    }
+
+    public boolean setRtmpUrl (String url){
+        //this.rtmpUrl = url;
+        if(mCamera == null) return false;
+        gLogger.error("setRtmpUrl - size: " +  width + "x" + height + ". url: " + url);
+        int ret = FfmpegHelper.initEncoder(width, height, url);
+        if(ret != 0){
+            gLogger.error("setRtmpUrl, initEncoder error");
+        }
+        return ret == 0 ? true : false;
+    }
+
+    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] + "]");
+        }
+        width = preferredSize.width;
+        height = preferredSize.height;
+        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);
+    }
+
+    // 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;
+    }
 }