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