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