d448064fba0239631fc07ce0516922cc83100dc7
[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         }
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         try {
197             mCamera.setPreviewDisplay(holder);
198             mCamera.startPreview();
199         } catch (Exception e){
200             gLogger.error("startPreview - error: " + e.getMessage());
201             e.printStackTrace();
202             if(mListener != null) mListener.onCameraError("setPreviewDisplay - " + e.getMessage());
203         }
204     }
205
206     public void reopenCamera() {
207         gLogger.debug("reopenCamera");
208         if(mCamera != null){
209             mCamera.stopPreview();
210             mCamera.release();
211             mCamera = null;
212         }
213         openCameraOriginal();
214     }
215
216     public boolean setRtmpUrl (String url){
217         //this.rtmpUrl = url;
218         if(mCamera == null){
219             gLogger.error("setRtmpUrl, error mCamera is null");
220             return false;
221         }
222         gLogger.error("setRtmpUrl - size: " +  width + "x" + height + ". url: " + url);
223         int ret = FfmpegHelper.initEncoder(width, height, url);
224         if(ret != 0){
225             gLogger.error("setRtmpUrl, initEncoder error");
226         }
227         return ret == 0 ? true : false;
228     }
229
230     private void configCamera(Camera camera){
231         Camera.Parameters paras = null;
232         try {
233             paras = camera.getParameters();
234         } catch (RuntimeException e){
235             gLogger.error("configCamera - " + e.getMessage());
236             if(mListener != null) mListener.onCameraError("getParameters - " + e.getMessage());
237             return;
238         }
239         gLogger.error("Supported Picture Sizes:");
240         Camera.Size preferredSize = paras.getSupportedPictureSizes().get(0);
241         for (Camera.Size cc : paras.getSupportedPictureSizes()){
242             if (cc.width == 640) // chose 640 x 480 if exists
243                 preferredSize = cc;
244             gLogger.error(cc.width + "x" + cc.height);
245         }
246         gLogger.error("Supported Preview fps range:");
247         for(int[] i : paras.getSupportedPreviewFpsRange()){
248             gLogger.error("[" + i[0] + "," + i[1] + "]");
249         }
250         width = preferredSize.width;
251         height = preferredSize.height;
252         paras.setPictureSize(preferredSize.width, preferredSize.height); // use 640x480 preferred
253         camera.setParameters(paras);
254         camera.setDisplayOrientation(0);
255         gLogger.error("Preview Format: " + paras.getPreviewFormat() + ". Size: " + paras.getPreviewSize().width + "x" + paras.getPreviewSize().height);
256         gLogger.error("Picture Format: " + paras.getPictureFormat() + ". Size: " + paras.getPictureSize().width + "x" + paras.getPictureSize().height);
257         camera.setPreviewCallback(this);
258     }
259
260     // Camaer.onError callback
261     @Override
262     public void onError(int error, Camera camera){
263         gLogger.error("Camera.OnError, " + error);
264         switch (error) {
265             case Camera.CAMERA_ERROR_SERVER_DIED:
266                 gLogger.error("CAMERA_ERROR_SERVER_DIED");
267                 break;
268             case Camera.CAMERA_ERROR_UNKNOWN:
269                 gLogger.error("CAMERA_ERROR_UNKNOWN");
270                 break;
271         }
272
273         if(mListener != null) mListener.onCameraError("OnError, " + error);
274     }
275
276
277     public interface MyServiceEventListener {
278         void onCameraError(String err);
279         void onEncoderError(String msg);
280         //void onIsPreviewing(String msg); // notify mainActivity if preview is running well
281         void onPublishing(String msg); // notify main activity if encoder is running well;
282     }
283 }