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 Camera.Size mPreferredSize;
31 private MyServiceEventListener mListener;
32 public void setServiceEventListener(MyServiceEventListener listener){
36 public class LocalBinder extends Binder {
37 public MyService getServiceInstance(){
38 return MyService.this;
42 public Camera getCameraInstance() {
43 if (mCamera == null) {
44 CameraHandlerThread mThread = new CameraHandlerThread("ThreadCamera");
45 synchronized (mThread) {
52 private void openCameraOriginal() {
54 gLogger.error("openCameraOriginal");
55 Camera.CameraInfo info = new Camera.CameraInfo();
56 int numCameras = Camera.getNumberOfCameras();
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);
66 } else if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
67 gLogger.debug("CAMERA_FACING_FRONT id is " + i);
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());
81 private class CameraHandlerThread extends HandlerThread {
84 public CameraHandlerThread(String name) {
86 gLogger.error("CameraHandlerThread: " + name);
88 mHandler = new Handler(getLooper());
91 synchronized void notifyCameraOpened() {
93 gLogger.debug("notifyCameraOpened");
97 mHandler.post(new Runnable() {
100 openCameraOriginal();
101 notifyCameraOpened();
106 } catch (InterruptedException e) {
107 gLogger.error("wait was interrupted, " + e.getMessage());
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); //设置为前台服务
127 public IBinder onBind(Intent intent) {
132 public void onCreate() {
134 gLogger.error("onCreate ---> ");
135 createNotification();
136 Toast.makeText(this, "Started to Publish!", Toast.LENGTH_LONG).show();
137 mCamera = getCameraInstance();
139 configCamera(mCamera);
140 mCamera.setErrorCallback(this);
146 public void onDestroy() {
147 stopForeground(true);
149 gLogger.error( "onDestroy --------->");
150 Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
153 mCamera.stopPreview();
154 mCamera.setPreviewCallback(null);
157 } catch (Exception e){
158 gLogger.error("onDestroy - error " + e.getMessage());
166 public int onStartCommand(Intent intent, int flags, int startId) {
167 gLogger.error("onStartCommand");
172 public void onLowMemory(){
174 gLogger.error("onLowMemory");
177 // Camera.PreviewCallback
179 public void onPreviewFrame(final byte[] data, Camera camera){
180 if(frameCount % (15 * 60) == 0) {
181 gLogger.error("onPreviewFrame");
183 if(FfmpegHelper.processFrame(data) != 0){
184 gLogger.error("onPreviewFrame, processFrame close");
185 FfmpegHelper.close();
186 if(mListener != null) mListener.onEncoderError("processFrame");
188 if(frameCount % (15 * 60) == 0){
189 if(mListener != null) mListener.onPublishing("processFrame OK");
195 public void startPreview (SurfaceHolder holder){
196 gLogger.debug("startPreview");
198 gLogger.error("startPreview - error: camera is null");
202 mCamera.setPreviewCallback(this);
203 mCamera.setPreviewDisplay(holder);
204 mCamera.startPreview();
205 } catch (Exception e){
206 gLogger.error("startPreview - error: " + e.getMessage());
208 if(mListener != null) mListener.onCameraError("setPreviewDisplay - " + e.getMessage());
212 public void reopenCamera() {
213 gLogger.debug("reopenCamera");
215 mCamera.stopPreview();
219 openCameraOriginal();
222 public boolean setRtmpUrl (String url){
223 if(mCamera == null) {
224 gLogger.error("setRtmpUrl mCamera is null");
226 } else if (mPreferredSize == null) {
227 gLogger.error("setRtmpUrl mPreferredSize is null");
230 gLogger.error("setRtmpUrl - size: " + mPreferredSize.width + "x" + mPreferredSize.height + ". url: " + url);
231 int ret = FfmpegHelper.initEncoder(mPreferredSize.width, mPreferredSize.height, url);
233 gLogger.error("setRtmpUrl, initEncoder error");
238 private void configCamera(Camera camera){
239 Camera.Parameters paras = null;
241 paras = camera.getParameters();
242 } catch (RuntimeException e){
243 gLogger.error("configCamera - " + e.getMessage());
244 if(mListener != null) mListener.onCameraError("getParameters - " + e.getMessage());
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
252 gLogger.error(cc.width + "x" + cc.height);
254 gLogger.error("Supported Preview fps range:");
255 for(int[] i : paras.getSupportedPreviewFpsRange()){
256 gLogger.error("[" + i[0] + "," + i[1] + "]");
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);
267 public Camera.Size getBestPictureSize(){
268 return mPreferredSize;
271 // Camaer.onError callback
273 public void onError(int error, Camera camera){
274 gLogger.error("Camera.OnError, " + error);
276 case Camera.CAMERA_ERROR_SERVER_DIED:
277 gLogger.error("CAMERA_ERROR_SERVER_DIED");
279 case Camera.CAMERA_ERROR_UNKNOWN:
280 gLogger.error("CAMERA_ERROR_UNKNOWN");
284 if(mListener != null) mListener.onCameraError("OnError, " + error);
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;