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