clean the code, and fix bug service stopped when back pressed
[rtmpclient.git] / app / src / main / java / ai / suanzi / rtmpclient / MyService.java
1 package ai.suanzi.rtmpclient;
2
3 import android.app.Service;
4 import android.content.Intent;
5 import android.hardware.Camera;
6 import android.os.Handler;
7 import android.os.HandlerThread;
8 import android.os.IBinder;
9 import android.view.SurfaceHolder;
10 import android.widget.Toast;
11 import android.support.v4.app.NotificationCompat;
12 import android.graphics.BitmapFactory;
13 import android.app.Notification;
14 import org.apache.log4j.Logger;
15 import android.os.Binder;
16
17
18
19 public class MyService extends Service  implements Camera.PreviewCallback, Camera.ErrorCallback {
20
21     private static Logger gLogger = Logger.getLogger("MyService");
22     private Camera mCamera = null;
23     IBinder mBinder = new LocalBinder();
24     //private String rtmpUrl;
25     private long frameCount = 0;
26
27     // Preferred picture Size of the camera;
28     private int width = 0;
29     private int height = 0;
30
31
32     private MyServiceEventListener mListener;
33     public void setServiceEventListener(MyServiceEventListener listener){
34         mListener = listener;
35     }
36
37     public class LocalBinder extends Binder {
38         public MyService getServiceInstance(){
39             return MyService.this;
40         }
41     }
42
43     public Camera getCameraInstance() {
44         if (mCamera == null) {
45             CameraHandlerThread mThread = new CameraHandlerThread("camera thread");
46             synchronized (mThread) {
47                 mThread.openCamera();
48             }
49         }
50         return mCamera;
51     }
52
53     private void openCameraOriginal() {
54         try {
55             gLogger.error("openCameraOriginal");
56             Camera.CameraInfo info = new Camera.CameraInfo();
57             int numCameras = Camera.getNumberOfCameras();
58             int backId = -1;
59             int frontId = -1;
60             int camerId = 0;
61             gLogger.debug("Number of Cameras is " + numCameras);
62             for(int i = 0; i < numCameras; i++){
63                 Camera.getCameraInfo(i, info);
64                 if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK){
65                     gLogger.debug("CAMERA_FACING_BACK id is " + i);
66                     backId = i;
67                 } else if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
68                     gLogger.debug("CAMERA_FACING_FRONT id is " + i);
69                     frontId = i;
70                 }
71             }
72             if(frontId != -1) camerId = frontId;
73             else if(backId != -1) camerId = backId;
74             mCamera = Camera.open(camerId);
75         } catch (Exception e) {
76             gLogger.error("openCameraOriginal - camera is not available. error: " + e.getMessage());
77             if(mListener != null) mListener.onCameraError("openCamera - error: " + e.getMessage());
78         }
79     }
80
81     private class CameraHandlerThread extends HandlerThread {
82         Handler mHandler;
83
84         public CameraHandlerThread(String name) {
85             super(name);
86             gLogger.error("CameraHandlerThread: " + name);
87             start();
88             mHandler = new Handler(getLooper());
89         }
90
91         synchronized void notifyCameraOpened() {
92             notify();
93         }
94
95         void openCamera() {
96             mHandler.post(new Runnable() {
97                 @Override
98                 public void run() {
99                     openCameraOriginal();
100                     notifyCameraOpened();
101                 }
102             });
103             try {
104                 wait();
105             } catch (InterruptedException e) {
106                 gLogger.error("wait was interrupted, " + e.getMessage());
107             }
108         }
109     }
110
111     private static final int NOTIFICATION_DOWNLOAD_PROGRESS_ID = 0x0001;                                        //id不可设置为0,否则不能设置为前台service
112     private void createNotification(){
113         gLogger.debug("createNotification");
114         NotificationCompat.Builder builder=new NotificationCompat.Builder(this);                        //使用兼容版本
115         builder.setSmallIcon(R.mipmap.ic_launcher);                                                             //设置状态栏的通知图标
116         builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background));   //设置通知栏横条的图标
117         builder.setAutoCancel(false);                                                                           //禁止用户点击删除按钮删除
118         builder.setOngoing(true);                                                                               //禁止滑动删除
119         builder.setShowWhen(true);                                                                              //右上角的时间显示
120         builder.setContentTitle("Rtmp Foreground Service!!!");                                                  //设置通知栏的标题内容
121         Notification notification = builder.build();                                                            //创建通知
122         startForeground(NOTIFICATION_DOWNLOAD_PROGRESS_ID,notification);                                        //设置为前台服务
123     }
124
125     @Override
126     public IBinder onBind(Intent intent) {
127         return mBinder;
128     }
129
130     @Override
131     public void onCreate() {
132         super.onCreate();
133         gLogger.error("onCreate ---> ");
134         createNotification();
135         Toast.makeText(this, "Started to Publish!", Toast.LENGTH_LONG).show();
136         mCamera = getCameraInstance();
137         if(mCamera != null){
138             configCamera(mCamera);
139             mCamera.setErrorCallback(this);
140         }
141
142     }
143
144     @Override
145     public void onDestroy() {
146         stopForeground(true);
147         super.onDestroy();
148         gLogger.error( "onDestroy --------->");
149         Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
150         if(mCamera != null){
151             mCamera.stopPreview();
152             mCamera.release();
153         }
154     }
155
156
157     @Override
158     public int onStartCommand(Intent intent, int flags, int startId) {
159         gLogger.error("onStartCommand");
160         return START_STICKY;
161     }
162
163     @Override
164     public void onLowMemory(){
165         super.onLowMemory();
166         gLogger.error("onLowMemory");
167     }
168
169     // Camera.PreviewCallback
170     @Override
171     public void  onPreviewFrame(final byte[] data, Camera camera){
172         if(frameCount % (15 * 60) == 0) {
173             gLogger.error("onPreviewFrame");
174         }
175         if(FfmpegHelper.processFrame(data) != 0){
176             gLogger.error("onPreviewFrame, processFrame close");
177             FfmpegHelper.close();
178             if(mListener != null) mListener.onEncoderError("processFrame");
179         } else {
180             if(frameCount % (15 * 60) == 0){
181                 if(mListener != null) mListener.onPublishing("processFrame OK");
182             }
183         }
184         frameCount++;
185     }
186
187     public void startPreview (SurfaceHolder holder){
188         gLogger.debug("startPreview");
189         try {
190             mCamera.setPreviewDisplay(holder);
191             mCamera.startPreview();
192         } catch (Exception e){
193             gLogger.error("startPreview - error: " + e.getMessage());
194             e.printStackTrace();
195             if(mListener != null) mListener.onCameraError("setPreviewDisplay - " + e.getMessage());
196         }
197     }
198
199     public void reopenCamera() {
200         if(mCamera != null){
201             mCamera.stopPreview();
202             mCamera.release();
203         }
204         openCameraOriginal();
205     }
206
207     public boolean setRtmpUrl (String url){
208         //this.rtmpUrl = url;
209         if(mCamera == null) return false;
210         gLogger.error("setRtmpUrl - size: " +  width + "x" + height + ". url: " + url);
211         int ret = FfmpegHelper.initEncoder(width, height, url);
212         if(ret != 0){
213             gLogger.error("setRtmpUrl, initEncoder error");
214         }
215         return ret == 0 ? true : false;
216     }
217
218     private void configCamera(Camera camera){
219         Camera.Parameters paras = null;
220         try {
221             paras = camera.getParameters();
222         } catch (RuntimeException e){
223             gLogger.error("configCamera - " + e.getMessage());
224             if(mListener != null) mListener.onCameraError("getParameters - " + e.getMessage());
225             return;
226         }
227         gLogger.error("Supported Picture Sizes:");
228         Camera.Size preferredSize = paras.getSupportedPictureSizes().get(0);
229         for (Camera.Size cc : paras.getSupportedPictureSizes()){
230             if (cc.width == 640) // chose 640 x 480 if exists
231                 preferredSize = cc;
232             gLogger.error(cc.width + "x" + cc.height);
233         }
234         gLogger.error("Supported Preview fps range:");
235         for(int[] i : paras.getSupportedPreviewFpsRange()){
236             gLogger.error("[" + i[0] + "," + i[1] + "]");
237         }
238         width = preferredSize.width;
239         height = preferredSize.height;
240         paras.setPictureSize(preferredSize.width, preferredSize.height); // use 640x480 preferred
241         camera.setParameters(paras);
242         camera.setDisplayOrientation(0);
243         gLogger.error("Preview Format: " + paras.getPreviewFormat() + ". Size: " + paras.getPreviewSize().width + "x" + paras.getPreviewSize().height);
244         gLogger.error("Picture Format: " + paras.getPictureFormat() + ". Size: " + paras.getPictureSize().width + "x" + paras.getPictureSize().height);
245         camera.setPreviewCallback(this);
246     }
247
248     // Camaer.onError callback
249     @Override
250     public void onError(int error, Camera camera){
251         gLogger.error("Camera.OnError, " + error);
252         switch (error) {
253             case Camera.CAMERA_ERROR_SERVER_DIED:
254                 gLogger.error("CAMERA_ERROR_SERVER_DIED");
255                 break;
256             case Camera.CAMERA_ERROR_UNKNOWN:
257                 gLogger.error("CAMERA_ERROR_UNKNOWN");
258                 break;
259         }
260
261         if(mListener != null) mListener.onCameraError("OnError, " + error);
262     }
263
264
265     public interface MyServiceEventListener {
266         void onCameraError(String err);
267         void onEncoderError(String msg);
268         //void onIsPreviewing(String msg); // notify mainActivity if preview is running well
269         void onPublishing(String msg); // notify main activity if encoder is running well;
270     }
271 }