1 package ai.suanzi.rtmpclient;
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;
19 public class MyService extends Service implements Camera.PreviewCallback, Camera.ErrorCallback {
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;
27 // Preferred picture Size of the camera;
28 private int width = 0;
29 private int height = 0;
32 private MyServiceEventListener mListener;
33 public void setServiceEventListener(MyServiceEventListener listener){
37 public class LocalBinder extends Binder {
38 public MyService getServiceInstance(){
39 return MyService.this;
43 public Camera getCameraInstance() {
44 if (mCamera == null) {
45 CameraHandlerThread mThread = new CameraHandlerThread("camera thread");
46 synchronized (mThread) {
53 private void openCameraOriginal() {
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());
63 private class CameraHandlerThread extends HandlerThread {
66 public CameraHandlerThread(String name) {
68 gLogger.error("CameraHandlerThread: " + name);
70 mHandler = new Handler(getLooper());
73 synchronized void notifyCameraOpened() {
78 mHandler.post(new Runnable() {
87 } catch (InterruptedException e) {
88 gLogger.error("wait was interrupted");
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); //设置为前台服务
108 public IBinder onBind(Intent intent) {
113 public void onCreate() {
115 gLogger.error("onCreate ---> ");
116 createNotification();
117 //Toast.makeText(this, "Started to Publish!", Toast.LENGTH_LONG).show();
118 mCamera = getCameraInstance();
120 configCamera(mCamera);
121 mCamera.setErrorCallback(this);
127 public void onDestroy() {
128 stopForeground(true);
130 gLogger.error( "onDestroy --------->");
131 Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
133 mCamera.stopPreview();
140 public int onStartCommand(Intent intent, int flags, int startId) {
141 gLogger.error("onStartCommand");
146 public void onLowMemory(){
148 gLogger.error("onLowMemory");
151 // Camera.PreviewCallback
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 ");
159 if(FfmpegHelper.processFrame(data) != 0){
160 gLogger.error("FfmpegHelper.processFrame error, close");
161 FfmpegHelper.close();
162 if(mListener != null) mListener.onEncoderError("processFrame");
166 public void startPreview (SurfaceHolder holder){
167 gLogger.error("startPreview");
169 mCamera.setPreviewDisplay(holder);
170 mCamera.startPreview();
171 } catch (Exception e){
172 gLogger.error("startPreview - error: " + e.getMessage());
174 if(mListener != null) mListener.onCameraError("setPreviewDisplay - " + e.getMessage());
178 public boolean setRtmpUrl (String 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;
186 private void configCamera(Camera camera){
187 Camera.Parameters paras = null;
189 paras = camera.getParameters();
190 } catch (RuntimeException e){
191 gLogger.error("configCamera - " + e.getMessage());
192 if(mListener != null) mListener.onCameraError("getParameters - " + e.getMessage());
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
200 gLogger.error(cc.width + "x" + cc.height);
202 gLogger.error("Supported Preview fps range:");
203 for(int[] i : paras.getSupportedPreviewFpsRange()){
204 gLogger.error("[" + i[0] + "," + i[1] + "]");
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);
216 // Camaer.onError callback
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);
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;