Fix memory leak, and all work well except startup and jni log
[rtmpclient.git] / app / src / main / java / ai / suanzi / rtmpclient / MyService.java
index 1d9dc60..2637d60 100644 (file)
@@ -29,15 +29,18 @@ import android.view.Gravity;
 
 public class MyService extends Service  implements Camera.PreviewCallback {
 
-    private Logger gLogger = Logger.getLogger("MyService");
+    private static Logger gLogger = Logger.getLogger("MyService");
+    private static String TAG = "MyService";
 
     private Ffmpeg ffmpeg = Ffmpeg.getInstance();
     private  Boolean isRunning = false;
-    private FfmpegRunnable  runnable;
+    //private FfmpegRunnable  runnable;
     private Camera mCamera = null;
     IBinder mBinder = new LocalBinder();
-    private WindowManager mWindowManager;
-    private SurfaceView mOutComeVideoView;
+    private String rtmpUrl;
+    //private WindowManager mWindowManager;
+    //private SurfaceView mOutComeVideoView;
+    private long frameCount = 0;
 
 
     public class LocalBinder extends Binder {
@@ -46,43 +49,61 @@ public class MyService extends Service  implements Camera.PreviewCallback {
         }
     }
 
-    private class FfmpegRunnable implements Runnable {
-        private String url;
-        private Camera.PreviewCallback cb;
-        public FfmpegRunnable(String _url, Camera.PreviewCallback _cb){
-            this.url = _url;
-            this.cb = _cb;
+    public Camera getCameraInstance() {
+        if (mCamera == null) {
+            CameraHandlerThread mThread = new CameraHandlerThread("camera thread");
+            synchronized (mThread) {
+                mThread.openCamera();
+            }
+        }
+        if (mCamera == null){
+            gLogger.error("getCameraInstance, camera is null");
+        }
+        return mCamera;
+    }
+
+    private void openCameraOriginal() {
+        try {
+            gLogger.error("openCameraOriginal");
+            mCamera = Camera.open(1);
+        } catch (Exception e) {
+            gLogger.error("camera is not available. 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();
         }
-        @Override
-        public void run(){
-            gLogger.error("Run Ffmpeg url: " + url);
-            isRunning = true;
-            //ffmpeg.push(null, this.url);
-            if (mCamera == null) {
-                gLogger.error("open camea");
-                try {
-                    mCamera = Camera.open(1);
-                }catch (Exception e){
-                    e.printStackTrace();
+
+        void openCamera() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    openCameraOriginal();
+                    notifyCameraOpened();
                 }
-            }
-            /*
-            SurfaceTexture st = new SurfaceTexture(0);
+            });
             try {
-                mCamera.setPreviewTexture(st);
-            }catch (Exception e){
-                e.printStackTrace();
-            }*/
-            gLogger.error("start preview");
-            mCamera.setPreviewCallback(this.cb);
-            mCamera.startPreview();
-
+                wait();
+            } catch (InterruptedException e) {
+                gLogger.error("wait was interrupted");
+            }
         }
     }
 
     private static final int NOTIFICATION_DOWNLOAD_PROGRESS_ID = 0x0001;                                        //id不可设置为0,否则不能设置为前台service
     private void createNotification(){
-        gLogger.error("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));   //设置通知栏横条的图标
@@ -102,57 +123,31 @@ public class MyService extends Service  implements Camera.PreviewCallback {
     @Override
     public void onCreate() {
         super.onCreate();
-        gLogger.error("onCreate");
-
-
-//        mWindowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
-//        mOutComeVideoView = new SurfaceView(this);
-//
-//
-//        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(1, 1, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
-//                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT);
-//        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
-//        mWindowManager.addView(mOutComeVideoView, layoutParams);
-//        mOutComeVideoView.getHolder().addCallback(this);
-//
-//
+        gLogger.error("onCreate ---> ");
         createNotification();
-        runnable = new FfmpegRunnable("xxxxxx", this);
-        new Thread(runnable).start();
-
-
-
-
+        Toast.makeText(this, "Video stream pushed to " + this.rtmpUrl, Toast.LENGTH_LONG).show();
+        mCamera = getCameraInstance();
+        configCamera(mCamera);
     }
 
     @Override
     public void onDestroy() {
         stopForeground(true);
-        Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
-        gLogger.error( "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);
-        gLogger.error("onStart");
-
-    }
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        String url = "hahahahhah"; //intent.getExtras().getString("url");
-
-
-        gLogger.error("onStartCommand: url is:" + url + ". isRunning: " + isRunning);
-        runnable = new FfmpegRunnable(url, this);
-        if (!isRunning) {
-            createNotification();
-            Toast.makeText(this, "Video stream pushed to " + url, Toast.LENGTH_LONG).show();
-            new Thread(runnable).start();
-        }
+        gLogger.error("onStartCommand");
+        //nrunnable = new FfmpegRunnable("xxx", this);
+        //new Thread(runnable).start();
         return START_STICKY;
     }
 
@@ -165,49 +160,82 @@ public class MyService extends Service  implements Camera.PreviewCallback {
     // Camera.PreviewCallback
     @Override
     public void  onPreviewFrame(final byte[] data, Camera camera){
-        gLogger.error("onPreviewFrame");
+        if(frameCount % (15 * 60) == 0) {
+            gLogger.error("onPreviewFrame");
+        }
+        frameCount++;
         ffmpeg.process(data);
     }
 
-    public void onChange (SurfaceHolder holder){
-        gLogger.error("onChange");
+    public void startPreview (SurfaceHolder holder){
+        gLogger.error("startPreview");
+        if (mCamera == null){
+            gLogger.error("startPreview - error: camera is null");
+            return;
+        }
         try {
-            if (holder == null){
-                gLogger.error("xxxx holder is null xxxxxx");
-            }
-            if (mCamera == null) {
-                gLogger.error("xxxx camera is null xxxx");
-            }
-
             mCamera.setPreviewDisplay(holder);
             mCamera.startPreview();
         } catch (Exception e){
+            gLogger.error("startPreview - error: " + e.getMessage());
             e.printStackTrace();
         }
     }
 
+    public boolean setRtmpUrl (String url){
+        this.rtmpUrl = url;
+        Camera.Parameters param = mCamera.getParameters();
+        int width = param.getPictureSize().width;
+        int height = param.getPictureSize().height;
+        gLogger.error("setRtmpUrl - size: " +  width + "x" + height + ". url: " + url);
+        int ret = ffmpeg.initnew(width, height, url);
+        return ret == 0 ? true : false;
+    }
 
+    private void configCamera(Camera camera){
+        if(mCamera == null){
+            gLogger.error("configCamera - camera is null");
+            return;
+        }
+        Camera.Parameters paras = camera.getParameters();
+        gLogger.error("Supported Picture Sizes:");
+        Camera.Size preferredSize = paras.getSupportedPictureSizes().get(0);
+        for (Camera.Size cc : paras.getSupportedPictureSizes()){
+            if (cc.width == 640)
+                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] + "]");
+        }
+        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);
+        //camera.startPreview();
+    }
+}
 
-//    // SurfaceHolder.Callback implementation
-//    @Override
-//    public void surfaceCreated(final SurfaceHolder holder){
-//        gLogger.error("SurfacedCreated");
-//    }
-//
-//    @Override
-//    public void surfaceChanged(SurfaceHolder holder, int format, int widht, int height){
-//        gLogger.error("surfaceChanged");
-//        try {
-//            mCamera.setPreviewDisplay(holder);
-//        } catch (Exception e){
-//            e.printStackTrace();
+//    private class FfmpegRunnable implements Runnable {
+//        private String url;
+//        private Camera.PreviewCallback cb;
+//        public FfmpegRunnable(String _url, Camera.PreviewCallback _cb){
+//            this.url = _url;
+//            this.cb = _cb;
+//        }
+//        @Override
+//        public void run(){
+//            gLogger.error("Run Ffmpeg url: " + url);
+//            isRunning = true;
+//            gLogger.error("Open camera");
+//            mCamera = getCameraInstance();
+//            if(mCamera == null) {
+//                gLogger.error("Open camera, camera is null");
+//            }
+//            configCamera(mCamera);
+//            mCamera.setPreviewCallback(this.cb);
 //        }
-//        mCamera.startPreview();
-//
-//    }
-//
-//    @Override
-//    public void surfaceDestroyed(SurfaceHolder holder){
-//        gLogger.error("surfaceDestroyed");
 //    }
-}