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