From: Peng Li Date: Thu, 17 May 2018 18:42:01 +0000 (+0800) Subject: Fix memory leak, and all work well except startup and jni log X-Git-Tag: v0.2.0^2~2 X-Git-Url: http://47.100.26.94:8080/?a=commitdiff_plain;h=8af8da070650175d3a35a840fbd339ee572d144f;p=rtmpclient.git Fix memory leak, and all work well except startup and jni log --- diff --git a/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java b/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java index d0556a7..7141810 100644 --- a/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java +++ b/app/src/main/java/ai/suanzi/rtmpclient/Ffmpeg.java @@ -29,7 +29,7 @@ public class Ffmpeg { private Ffmpeg(){ Log.e("Ffmpeg", "init"); //init(); - inithaha(640, 480); + //inithaha(640, 480); //getPerfectDevice(); } @@ -37,6 +37,8 @@ public class Ffmpeg { public native String getVersion(); public native void init(); public native int inithaha(int width, int height); + public native int initnew (int width, int height, String url); + //public native init inithahurl(ini) public native int flush(); public native int close(); public native int process(byte[] data); @@ -45,4 +47,5 @@ public class Ffmpeg { public native int preview(Object surface); public native String getPerfectDevice(); public native int test(int fd); + public native void setRtmpUrl(String url); } diff --git a/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java b/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java index 6531b9f..708ed3c 100644 --- a/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java +++ b/app/src/main/java/ai/suanzi/rtmpclient/MainActivity.java @@ -53,18 +53,18 @@ import android.os.IBinder; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Camera.PreviewCallback{ private static final String TAG = "PENG"; - private Ffmpeg ffmpeg; - private Camera mCamera ; - private StreamTask mStreamTask; + //private Ffmpeg ffmpeg; + //private Camera mCamera ; + //private StreamTask mStreamTask; private SurfaceHolder mHolder; private SurfaceView mSufaceView; //private UVCCamera uvcCamera; - ExecutorService mExecutor = Executors.newSingleThreadExecutor(); + //ExecutorService mExecutor = Executors.newSingleThreadExecutor(); //Intent it = new Intent(getApplicationContext(), MyService.class); Intent intent = new Intent(); - private UsbManager usbManager; - private UsbDevice usbCamera; + //private UsbManager usbManager; + //private UsbDevice usbCamera; private Logger gLogger; @@ -73,7 +73,12 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal private TextInputEditText mTextUser; private TextInputEditText mTextCamera; private String mMacAddr = ""; - private CameraView mCameraView; + //private CameraView mCameraView; + private String mRtmpUrl; + + boolean mBounded; + MyService mServer; + Intent mIntent; private void configLog(){ try { @@ -94,42 +99,29 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal private void init(){ configLog(); gLogger.debug("#######################################"); + // set config file UserInfo.setConfigPath(getExternalFilesDir(null) + File.separator + "config"); - this.mMacAddr = getMacAddr(); - - ffmpeg = Ffmpeg.getInstance(); - //uvcCamera = new UVCCamera(); + this.mMacAddr = getMacAddr(); mBtnStart = findViewById(R.id.button); mTextServer = findViewById(R.id.textServer); mTextUser = findViewById(R.id.textUser); mTextCamera = findViewById(R.id.textCamera); - loadConfig(); - - // init service - intent.setPackage(this.getPackageName()); - intent.setAction("ai.suanzi.rtmpclient.service"); - - // init surface view mSufaceView = findViewById(R.id.surfaceView); mHolder = mSufaceView.getHolder(); mHolder.addCallback(this); - // camera - //mCamera = getCameraInstance(); - //configCamera(mCamera); + //intent.setPackage(this.getPackageName()); // init service + //intent.setAction("ai.suanzi.rtmpclient.service"); } - boolean mBounded; - MyService mServer; ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(MainActivity.this, "Service is disconnected", 1000).show(); - gLogger.error("onServiceDisconnected"); - + gLogger.error("onServiceDisconnected ---------->"); mBounded = false; mServer = null; } @@ -137,11 +129,14 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal @Override public void onServiceConnected(ComponentName name, IBinder service) { Toast.makeText(MainActivity.this, "Service is connected", 1000).show(); - gLogger.error("onServiceConnected"); + gLogger.error("onServiceConnected ---------->"); mBounded = true; LocalBinder mLocalBinder = (LocalBinder)service; mServer = mLocalBinder.getServiceInstance(); - mServer.onChange(mHolder); + if(mServer.setRtmpUrl(UserInfo.getConfig().toUrl())){ + mServer.startPreview(mHolder); + } + } }; @@ -149,27 +144,23 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); + init(); - //mCameraView = new CameraView(this); - //intent.putExtra("url", "xxxxxxxxxxxxxxxxxx"); - //intent.putExtra("view", mCameraView); + loadConfig(); //startService(intent); - Intent mIntent = new Intent(this, MyService.class); + mIntent = new Intent(this, MyService.class); bindService(mIntent, mConnection, BIND_AUTO_CREATE); - - - + mBtnStart.setText("start"); mBtnStart.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view){ - String url = mTextServer.getText().toString() + "/" + mTextUser.getText().toString() + "_" + mMacAddr + "_" + mTextCamera.getText().toString(); - gLogger.error("----------> onClick, Url is: " + url); - intent.putExtra("url", url); - startService(intent); + gLogger.error("----------> onClick"); saveConfig(); + unbindService(mConnection); + bindService(mIntent, mConnection, BIND_AUTO_CREATE); } }); } @@ -177,57 +168,37 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal @Override protected void onPause(){ super.onPause(); - gLogger.debug("OnPause"); + gLogger.error("OnPause --------->"); } @Override protected void onResume() { super.onResume(); - gLogger.debug("OnResume"); + gLogger.error("OnResume ---------> "); } @Override protected void onStop() { super.onStop(); - gLogger.debug("onStop"); + gLogger.debug("onStop --------->"); } @Override protected void onStart(){ super.onStart(); - gLogger.debug("onStart"); + gLogger.debug("onStart --------->"); } @Override protected void onDestroy(){ super.onDestroy(); - gLogger.debug("onDestroy"); + gLogger.debug("onDestroy --------->"); } @Override protected void onRestart(){ - super.onStart(); - gLogger.debug("onRestart"); - } - - - // class StreamTask AsyncTask - private class StreamTask extends AsyncTask{ - private byte[] data; - - StreamTask(byte[] data){ - this.data = data; - } - - @Override - protected Void doInBackground(Void... params) { - - if (this.data != null){ - Log.e(TAG, "fps: " + mCamera.getParameters().getPreviewFrameRate()); - ffmpeg.process(this.data); - } - return null; - } + super.onRestart(); + gLogger.debug("onRestart ---------->"); } @@ -243,82 +214,69 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal public void surfaceChanged(SurfaceHolder holder, int format, int widht, int height){ gLogger.error("surfaceChanged"); mHolder = holder; - if (mServer != null) { - mServer.onChange(holder); - } - - /* final SurfaceHolder hh = holder; - Thread tr = new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(3000); - } catch (Exception e){ - e.printStackTrace(); - } - if (mServer != null) { - gLogger.error("onChange"); - mServer.onChange(hh); - }else { - gLogger.error("service XXXXXXXXXXXXXXX"); - } - - } - }); - tr.run();*/ - - - /* - try { - mCamera.setPreviewDisplay(holder); - mCamera.startPreview(); - } catch (Exception e){ - gLogger.error("Error starting camera previewe: " + e.getMessage()); - Toast.makeText(this, "Camera Error: " + e.getMessage(), Toast.LENGTH_LONG).show(); + mServer.startPreview(holder); } - */ } @Override - public void surfaceDestroyed(SurfaceHolder holder){ - Log.e(TAG, "surfaceDestroyed"); + public void surfaceDestroyed(SurfaceHolder holder){ gLogger.debug("surfaceDestroyed"); } @Override public void onPreviewFrame(final byte[] data, Camera camera){ gLogger.error("onPreviewFrame"); - /*if (null != mStreamTask){ - switch (mStreamTask.getStatus()){ - case RUNNING: - Log.e(TAG, "onPreviewFrame Running"); - return; - case PENDING: - Log.e(TAG,"OnPreviewFrame Pending"); - mStreamTask.cancel(false); - break; - } + + } + + + private String getMacAddr() { + WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); + WifiInfo info = manager.getConnectionInfo(); + return info.getMacAddress().replace(":", ""); //02:00:00:00:00:00 - 020000000000 + } + + private void loadConfig() { + UserInfo info = UserInfo.getConfig(); + mTextServer.setText(info.server); + mTextUser.setText(info.user); + mTextCamera.setText(info.cameraId); + //mMacAddr = info.macAddr.equals("") ? this.mMacAddr : info.macAddr; + gLogger.error("loadConfig - url is :" + info.toUrl()); + } + + private void saveConfig() { + UserInfo info = UserInfo.getConfig(); + info.update(mTextServer.getText().toString(), mTextUser.getText().toString(), mMacAddr, mTextCamera.getText().toString()); + if(info.saveConfig()) { + Toast.makeText(getApplicationContext(), "Config saved", Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(getApplicationContext(), "Error: config saved", Toast.LENGTH_LONG).show(); } - mStreamTask = new StreamTask(data); - mStreamTask.execute((Void)null); -*/ + gLogger.error("saveConfig - url: " + info.toUrl()); + } + + private void changePermission(){ + try { + Log.e(TAG, "change permission"); + //Process sh = Runtime.getRuntime().exec(new String[]{"su", "-c", "chmod 666 /dev/video0"}); + + Process sh = Runtime.getRuntime().exec("/system/xbin/su", null,null); + //Process sh = Runtime.getRuntime().exec("su", null,null); + + OutputStream os = sh.getOutputStream(); + os.write(("/system/bin/chmod 666 /dev/video0").getBytes("ASCII")); + //os.write(("/system/bin/echo 'wowo' >> /data/local/test").getBytes("ASCII")); + os.flush(); + os.close(); + sh.waitFor(); -// ong endTime = System.currentTimeMillis(); -// mExecutor.execute(new Runnable() { -// @Override -// public void run() { -// //long encodeTime = System.currentTimeMillis(); -// ffmpeg.process(data); - //Log.e(TAG, "编码第:" + (encodeCount++) + "帧,耗时:" + (System.currentTimeMillis() - encodeTime)); -// } -// }); - //Log.e(TAG, "采集第:" + (++count) + "帧,距上一帧间隔时间:" -// + (endTime - previewTime) + " " + Thread.currentThread().getName()); -// previewTime = endTime;*/ + }catch (Exception e){ + e.printStackTrace(); + } } - // private private boolean checkCameraHardware(Context context) { return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); } @@ -365,50 +323,54 @@ public class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal this.startActivity(i); } - private String getMacAddr() { - WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); - WifiInfo info = manager.getConnectionInfo(); - return info.getMacAddress().replace(":", ""); //02:00:00:00:00:00 - 020000000000 - } - - private void loadConfig() { - UserInfo info = UserInfo.getConfig(); - mTextServer.setText(info.server.equals("") ? "rtmp://gpussh.suanzi.ai:1935/myapp" : info.server); - mTextUser.setText(info.user.equals("") ? "suanzi" : info.user ); - mTextCamera.setText(info.cameraId.equals("") ? "001" : info.cameraId); - mMacAddr = info.macAddr.equals("") ? this.mMacAddr : info.macAddr; - gLogger.debug("loadConfig " + info.toString()); - } +// +// // class StreamTask AsyncTask +// private class StreamTask extends AsyncTask{ +// private byte[] data; +// +// StreamTask(byte[] data){ +// this.data = data; +// } +// +// @Override +// protected Void doInBackground(Void... params) { +// +// if (this.data != null){ +// Log.e(TAG, "fps: " + mCamera.getParameters().getPreviewFrameRate()); +// ffmpeg.process(this.data); +// } +// return null; +// } +// } - private void saveConfig() { - UserInfo info = UserInfo.getConfig(); - info.update(mTextServer.getText().toString(), mTextUser.getText().toString(), mMacAddr, mTextCamera.getText().toString()); - if(info.saveConfig()) { - Toast.makeText(getApplicationContext(), "Config saved", Toast.LENGTH_LONG).show(); - } else { - Toast.makeText(getApplicationContext(), "Error: config saved", Toast.LENGTH_LONG).show(); + /*if (null != mStreamTask){ + switch (mStreamTask.getStatus()){ + case RUNNING: + Log.e(TAG, "onPreviewFrame Running"); + return; + case PENDING: + Log.e(TAG,"OnPreviewFrame Pending"); + mStreamTask.cancel(false); + break; + } } - gLogger.error("saveConfig: " + info.toString()); - } - - private void changePermission(){ - try { - Log.e(TAG, "change permission"); - //Process sh = Runtime.getRuntime().exec(new String[]{"su", "-c", "chmod 666 /dev/video0"}); + mStreamTask = new StreamTask(data); + mStreamTask.execute((Void)null); +*/ - Process sh = Runtime.getRuntime().exec("/system/xbin/su", null,null); - //Process sh = Runtime.getRuntime().exec("su", null,null); +// ong endTime = System.currentTimeMillis(); +// mExecutor.execute(new Runnable() { +// @Override +// public void run() { +// //long encodeTime = System.currentTimeMillis(); +// ffmpeg.process(data); + //Log.e(TAG, "编码第:" + (encodeCount++) + "帧,耗时:" + (System.currentTimeMillis() - encodeTime)); +// } +// }); + //Log.e(TAG, "采集第:" + (++count) + "帧,距上一帧间隔时间:" +// + (endTime - previewTime) + " " + Thread.currentThread().getName()); +// previewTime = endTime;*/ - OutputStream os = sh.getOutputStream(); - os.write(("/system/bin/chmod 666 /dev/video0").getBytes("ASCII")); - //os.write(("/system/bin/echo 'wowo' >> /data/local/test").getBytes("ASCII")); - os.flush(); - os.close(); - sh.waitFor(); - }catch (Exception e){ - e.printStackTrace(); - } - } } diff --git a/app/src/main/java/ai/suanzi/rtmpclient/MyLogConfigure.java b/app/src/main/java/ai/suanzi/rtmpclient/MyLogConfigure.java index 45a445c..3c63e39 100644 --- a/app/src/main/java/ai/suanzi/rtmpclient/MyLogConfigure.java +++ b/app/src/main/java/ai/suanzi/rtmpclient/MyLogConfigure.java @@ -35,32 +35,4 @@ public class MyLogConfigure { instance = new MyLogConfigure(); return Logger.getLogger(str); } -} -/* - -//设置文件名 - logConfigurator.setFileName(fileName); - //设置root日志输出级别 默认为DEBUG - logConfigurator.setRootLevel(Level.DEBUG); - // 设置日志输出级别 - logConfigurator.setLevel("org.apache", Level.INFO); - //设置 输出到日志文件的文字格式 默认 %d %-5p [%c{2}]-[%L] %m%n - logConfigurator.setFilePattern("%d %-5p [%c{2}]-[%L] %m%n"); - //设置输出到控制台的文字格式 默认%m%n - logConfigurator.setLogCatPattern("%m%n"); - //设置总文件大小 - logConfigurator.setMaxFileSize(1024 * 1024 * 5); - //设置最大产生的文件个数 - logConfigurator.setMaxBackupSize(1); - //设置所有消息是否被立刻输出 默认为true,false 不输出 - logConfigurator.setImmediateFlush(true); - //是否本地控制台打印输出 默认为true ,false不输出 - logConfigurator.setUseLogCatAppender(true); - //设置是否启用文件附加,默认为true。false为覆盖文件 - logConfigurator.setUseFileAppender(true); - //设置是否重置配置文件,默认为true - logConfigurator.setResetConfiguration(true); - //是否显示内部初始化日志,默认为false - logConfigurator.setInternalDebugging(false); - - */ \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/ai/suanzi/rtmpclient/MyService.java b/app/src/main/java/ai/suanzi/rtmpclient/MyService.java index 1d9dc60..2637d60 100644 --- a/app/src/main/java/ai/suanzi/rtmpclient/MyService.java +++ b/app/src/main/java/ai/suanzi/rtmpclient/MyService.java @@ -29,15 +29,18 @@ import android.view.Gravity; public class MyService extends Service implements Camera.PreviewCallback { - private Logger gLogger = Logger.getLogger("MyService"); + private static Logger gLogger = Logger.getLogger("MyService"); + private static String TAG = "MyService"; private Ffmpeg ffmpeg = Ffmpeg.getInstance(); private Boolean isRunning = false; - private FfmpegRunnable runnable; + //private FfmpegRunnable runnable; private Camera mCamera = null; IBinder mBinder = new LocalBinder(); - private WindowManager mWindowManager; - private SurfaceView mOutComeVideoView; + private String rtmpUrl; + //private WindowManager mWindowManager; + //private SurfaceView mOutComeVideoView; + private long frameCount = 0; public class LocalBinder extends Binder { @@ -46,43 +49,61 @@ public class MyService extends Service implements Camera.PreviewCallback { } } - private class FfmpegRunnable implements Runnable { - private String url; - private Camera.PreviewCallback cb; - public FfmpegRunnable(String _url, Camera.PreviewCallback _cb){ - this.url = _url; - this.cb = _cb; + public Camera getCameraInstance() { + if (mCamera == null) { + CameraHandlerThread mThread = new CameraHandlerThread("camera thread"); + synchronized (mThread) { + mThread.openCamera(); + } + } + if (mCamera == null){ + gLogger.error("getCameraInstance, camera is null"); + } + return mCamera; + } + + private void openCameraOriginal() { + try { + gLogger.error("openCameraOriginal"); + mCamera = Camera.open(1); + } catch (Exception e) { + gLogger.error("camera is not available. error: " + e.getMessage()); + } + } + + private class CameraHandlerThread extends HandlerThread { + Handler mHandler; + + public CameraHandlerThread(String name) { + super(name); + gLogger.error("CameraHandlerThread: " + name); + start(); + mHandler = new Handler(getLooper()); + } + + synchronized void notifyCameraOpened() { + notify(); } - @Override - public void run(){ - gLogger.error("Run Ffmpeg url: " + url); - isRunning = true; - //ffmpeg.push(null, this.url); - if (mCamera == null) { - gLogger.error("open camea"); - try { - mCamera = Camera.open(1); - }catch (Exception e){ - e.printStackTrace(); + + void openCamera() { + mHandler.post(new Runnable() { + @Override + public void run() { + openCameraOriginal(); + notifyCameraOpened(); } - } - /* - SurfaceTexture st = new SurfaceTexture(0); + }); try { - mCamera.setPreviewTexture(st); - }catch (Exception e){ - e.printStackTrace(); - }*/ - gLogger.error("start preview"); - mCamera.setPreviewCallback(this.cb); - mCamera.startPreview(); - + wait(); + } catch (InterruptedException e) { + gLogger.error("wait was interrupted"); + } } } private static final int NOTIFICATION_DOWNLOAD_PROGRESS_ID = 0x0001; //id不可设置为0,否则不能设置为前台service private void createNotification(){ - gLogger.error("createNotification"); + gLogger.debug("createNotification"); NotificationCompat.Builder builder=new NotificationCompat.Builder(this); //使用兼容版本 builder.setSmallIcon(R.mipmap.ic_launcher); //设置状态栏的通知图标 builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background)); //设置通知栏横条的图标 @@ -102,57 +123,31 @@ public class MyService extends Service implements Camera.PreviewCallback { @Override public void onCreate() { super.onCreate(); - gLogger.error("onCreate"); - - -// mWindowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE); -// mOutComeVideoView = new SurfaceView(this); -// -// -// WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(1, 1, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, -// WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, PixelFormat.TRANSLUCENT); -// layoutParams.gravity = Gravity.LEFT | Gravity.TOP; -// mWindowManager.addView(mOutComeVideoView, layoutParams); -// mOutComeVideoView.getHolder().addCallback(this); -// -// + gLogger.error("onCreate ---> "); createNotification(); - runnable = new FfmpegRunnable("xxxxxx", this); - new Thread(runnable).start(); - - - - + Toast.makeText(this, "Video stream pushed to " + this.rtmpUrl, Toast.LENGTH_LONG).show(); + mCamera = getCameraInstance(); + configCamera(mCamera); } @Override public void onDestroy() { stopForeground(true); - Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show(); - gLogger.error( "onDestroy"); super.onDestroy(); - + gLogger.error( "onDestroy --------->"); + Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show(); + if(mCamera != null){ + mCamera.stopPreview(); + mCamera.release(); + } } - @Override - public void onStart(Intent intent, int startid){ - super.onStart(intent, startid); - gLogger.error("onStart"); - - } @Override public int onStartCommand(Intent intent, int flags, int startId) { - String url = "hahahahhah"; //intent.getExtras().getString("url"); - - - gLogger.error("onStartCommand: url is:" + url + ". isRunning: " + isRunning); - runnable = new FfmpegRunnable(url, this); - if (!isRunning) { - createNotification(); - Toast.makeText(this, "Video stream pushed to " + url, Toast.LENGTH_LONG).show(); - new Thread(runnable).start(); - } + gLogger.error("onStartCommand"); + //nrunnable = new FfmpegRunnable("xxx", this); + //new Thread(runnable).start(); return START_STICKY; } @@ -165,49 +160,82 @@ public class MyService extends Service implements Camera.PreviewCallback { // Camera.PreviewCallback @Override public void onPreviewFrame(final byte[] data, Camera camera){ - gLogger.error("onPreviewFrame"); + if(frameCount % (15 * 60) == 0) { + gLogger.error("onPreviewFrame"); + } + frameCount++; ffmpeg.process(data); } - public void onChange (SurfaceHolder holder){ - gLogger.error("onChange"); + public void startPreview (SurfaceHolder holder){ + gLogger.error("startPreview"); + if (mCamera == null){ + gLogger.error("startPreview - error: camera is null"); + return; + } try { - if (holder == null){ - gLogger.error("xxxx holder is null xxxxxx"); - } - if (mCamera == null) { - gLogger.error("xxxx camera is null xxxx"); - } - mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (Exception e){ + gLogger.error("startPreview - error: " + e.getMessage()); e.printStackTrace(); } } + public boolean setRtmpUrl (String url){ + this.rtmpUrl = url; + Camera.Parameters param = mCamera.getParameters(); + int width = param.getPictureSize().width; + int height = param.getPictureSize().height; + gLogger.error("setRtmpUrl - size: " + width + "x" + height + ". url: " + url); + int ret = ffmpeg.initnew(width, height, url); + return ret == 0 ? true : false; + } + private void configCamera(Camera camera){ + if(mCamera == null){ + gLogger.error("configCamera - camera is null"); + return; + } + Camera.Parameters paras = camera.getParameters(); + gLogger.error("Supported Picture Sizes:"); + Camera.Size preferredSize = paras.getSupportedPictureSizes().get(0); + for (Camera.Size cc : paras.getSupportedPictureSizes()){ + if (cc.width == 640) + preferredSize = cc; + gLogger.error(cc.width + "x" + cc.height); + } + gLogger.error("Supported Preview fps range:"); + for(int[] i : paras.getSupportedPreviewFpsRange()){ + gLogger.error("[" + i[0] + "," + i[1] + "]"); + } + paras.setPictureSize(preferredSize.width, preferredSize.height); // use 640x480 preferred + camera.setParameters(paras); + camera.setDisplayOrientation(0); + gLogger.error("Preview Format: " + paras.getPreviewFormat() + ". Size: " + paras.getPreviewSize().width + "x" + paras.getPreviewSize().height); + gLogger.error("Picture Format: " + paras.getPictureFormat() + ". Size: " + paras.getPictureSize().width + "x" + paras.getPictureSize().height); + camera.setPreviewCallback(this); + //camera.startPreview(); + } +} -// // SurfaceHolder.Callback implementation -// @Override -// public void surfaceCreated(final SurfaceHolder holder){ -// gLogger.error("SurfacedCreated"); -// } -// -// @Override -// public void surfaceChanged(SurfaceHolder holder, int format, int widht, int height){ -// gLogger.error("surfaceChanged"); -// try { -// mCamera.setPreviewDisplay(holder); -// } catch (Exception e){ -// e.printStackTrace(); +// private class FfmpegRunnable implements Runnable { +// private String url; +// private Camera.PreviewCallback cb; +// public FfmpegRunnable(String _url, Camera.PreviewCallback _cb){ +// this.url = _url; +// this.cb = _cb; +// } +// @Override +// public void run(){ +// gLogger.error("Run Ffmpeg url: " + url); +// isRunning = true; +// gLogger.error("Open camera"); +// mCamera = getCameraInstance(); +// if(mCamera == null) { +// gLogger.error("Open camera, camera is null"); +// } +// configCamera(mCamera); +// mCamera.setPreviewCallback(this.cb); // } -// mCamera.startPreview(); -// -// } -// -// @Override -// public void surfaceDestroyed(SurfaceHolder holder){ -// gLogger.error("surfaceDestroyed"); // } -} diff --git a/app/src/main/java/ai/suanzi/rtmpclient/UserInfo.java b/app/src/main/java/ai/suanzi/rtmpclient/UserInfo.java index 1d655da..ebac937 100644 --- a/app/src/main/java/ai/suanzi/rtmpclient/UserInfo.java +++ b/app/src/main/java/ai/suanzi/rtmpclient/UserInfo.java @@ -1,7 +1,5 @@ package ai.suanzi.rtmpclient; -import android.content.Context; -import android.util.Log; import android.widget.Toast; import java.io.BufferedReader; @@ -10,10 +8,11 @@ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.io.InputStream; + import org.json.JSONException; import org.json.JSONObject; +import org.apache.log4j.Logger; public class UserInfo { @@ -22,19 +21,34 @@ public class UserInfo { public String macAddr = ""; public String cameraId = ""; private static String configPath; + private static Logger gLogger = Logger.getLogger("UserInfo"); + private static UserInfo instance = null; private UserInfo () {} public static void setConfigPath(String fname){ configPath = fname; + File file = new File(configPath); + if(!file.exists()) { + gLogger.error("Config file: " + configPath + " not exists! Create it"); + try { + file.createNewFile(); + }catch (IOException e){ + gLogger.error("Create file error: " + e.getMessage()); + e.printStackTrace(); + } + } } public static UserInfo getConfig() { if (instance != null) return instance; File file = new File(configPath); - if (!file.exists()) return new UserInfo(); + if (!file.exists()){ + gLogger.error("getConfig - file configpath: " + configPath + " not exists"); + return new UserInfo(); + } StringBuilder text = new StringBuilder(); try { @@ -59,6 +73,7 @@ public class UserInfo { info.macAddr = jobj.getString("macAddr"); info.cameraId = jobj.getString("cameraId"); } catch (JSONException e){ + gLogger.error("getConfig - error: " + e.getMessage()); e.printStackTrace(); } instance = info; @@ -69,14 +84,13 @@ public class UserInfo { String jstring = toString(); - Log.e("Config", "xxxxxxxxx " + jstring); - File file = new File(configPath); try{ BufferedWriter bw = new BufferedWriter(new FileWriter(file)); bw.write(jstring); bw.close(); } catch (IOException e){ + gLogger.error("saveConfig - error: " + e.getMessage()); e.printStackTrace(); return false; } @@ -92,8 +106,16 @@ public class UserInfo { public String toString () { JSONObject obj = toJsonObj(); - if (obj.equals(null)) return ""; - return obj.toString(); + String str = ""; + if (!obj.equals(null)) { + try { + str = obj.toString(4); + }catch (JSONException e){ + gLogger.error("toString - error: " + e.getMessage()); + e.printStackTrace(); + } + } + return str; } private JSONObject toJsonObj () { @@ -109,4 +131,9 @@ public class UserInfo { return null; } } + + public String toUrl () { + //rtmp://gpussh.suanzi.ai:1935/myapp/suanzi_ac83f34ead90_cameraid + return server + "/" + user + "_" + macAddr + "_" + cameraId; + } } diff --git a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp index e6c8d15..decd9b4 100644 --- a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp +++ b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.cpp @@ -109,6 +109,90 @@ JNIEXPORT jstring JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_getVersion (JNIEnv *e return env->NewStringUTF("====== Ffmpeg call ======="); } +//const char* out_path; + +JNIEXPORT void JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_setRtmpUrl (JNIEnv *env, jobject obj, jstring url){ + LOGE("set rtmpurl"); + //const char* out_path = "rtmp://gpussh.suanzi.ai:1935/myapp/suanzi_ac83f34ead90_cameraid"; + //out_path = env->GetStringUTFChars(url, 0); + +} +JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_initnew (JNIEnv *env, jobject obj, jint width, jint height, jstring url) +{ + const char * out_path= env->GetStringUTFChars(url, 0); + LOGE("Ffmpeg init, width=%d, heigh=%d, url=%s", width, height, out_path); + + yuv_width=width; + yuv_height=height; + y_length=width*height; + uv_length=width*height/4; + + + av_register_all(); + + //output initialize + avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_path); + //output encoder initialize + pCodec = avcodec_find_encoder(AV_CODEC_ID_H264); + if (!pCodec){ + LOGE("Can not find encoder!\n"); + return -1; + } + pCodecCtx = avcodec_alloc_context3(pCodec); + pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; + pCodecCtx->width = width; + pCodecCtx->height = height; + pCodecCtx->time_base.num = 1; + pCodecCtx->time_base.den = 30; + pCodecCtx->bit_rate = 800000; + pCodecCtx->gop_size = 300; + /* Some formats want stream headers to be separate. */ + if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) + pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + //H264 codec param + //pCodecCtx->me_range = 16; + //pCodecCtx->max_qdiff = 4; + //pCodecCtx->qcompress = 0.6; + pCodecCtx->qmin = 10; + pCodecCtx->qmax = 51; + //Optional Param + pCodecCtx->max_b_frames = 3; + // Set H264 preset and tune + AVDictionary *param = 0; + av_dict_set(¶m, "preset", "ultrafast", 0); + av_dict_set(¶m, "tune", "zerolatency", 0); + + if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0){ + LOGE("Failed to open encoder!\n"); + return -1; + } + + //Add a new stream to output,should be called by the user before avformat_write_header() for muxing + video_st = avformat_new_stream(ofmt_ctx, pCodec); + if (video_st == NULL){ + return -1; + } + video_st->time_base.num = 1; + video_st->time_base.den = 30; + video_st->codec = pCodecCtx; + + //Open output URL,set before avformat_write_header() for muxing + jint ret = 0; + if (( ret = avio_open(&ofmt_ctx->pb, out_path, AVIO_FLAG_READ_WRITE)) < 0){ + LOGE("Failed to open output file! return :%s(%d)\n", av_err2str(ret),ret); + return -1; + } + + //Write File Header + avformat_write_header(ofmt_ctx, NULL); + + start_time = av_gettime(); + env->ReleaseStringUTFChars(url, out_path); + return 0; + +} + JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_inithaha (JNIEnv *env, jobject obj, jint width, jint height) { //const char* out_path = "/storage/emulated/0/Movies/output.flv"; @@ -245,12 +329,20 @@ JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_close (JNIEnv *env, jobj return 0; } + + +JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_processnew (JNIEnv *env, jobject obj, jbyteArray yuv){ + +} + + + JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_process (JNIEnv *env, jobject obj, jbyteArray yuv){ int ret; int enc_got_frame=0; int i=0; - LOGE(" process data - ffmpeg"); + //LOGE(" process data - ffmpeg"); pFrameYUV = av_frame_alloc(); uint8_t *out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); @@ -275,7 +367,9 @@ JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_process (JNIEnv *env, jo av_frame_free(&pFrameYUV); if (enc_got_frame == 1){ - //LOGE("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, enc_pkt.size); + if (framecnt % (15 * 60) == 0){ + LOGE("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, enc_pkt.size); + } framecnt++; enc_pkt.stream_index = video_st->index; @@ -301,6 +395,7 @@ JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_process (JNIEnv *env, jo ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt); av_free_packet(&enc_pkt); } + av_free(out_buffer); return 0; } @@ -493,8 +588,8 @@ JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_push (JNIEnv *env, jobje // av_log_set_callback(custom_log); // Open Output //const char* out_path = "rtmp://192.168.1.35:1935/myapp/peng2"; - //const char* out_path = "rtmp://gpussh.suanzi.ai:1935/myapp/suanzi_ac83f34ead90_cameraid"; - const char* out_path = env->GetStringUTFChars(url, 0); + const char* out_path = "rtmp://gpussh.suanzi.ai:1935/myapp/suanzi_ac83f34ead90_cameraid"; + //const char* out_path = env->GetStringUTFChars(url, 0); //const char * file_name = env->GetStringUTFChars(fname, 0); diff --git a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h index bf88a71..22b129f 100644 --- a/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h +++ b/app/src/main/jni/ai_suanzi_rtmpclient_Ffmpeg.h @@ -33,6 +33,14 @@ JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_inithaha /* * Class: ai_suanzi_rtmpclient_Ffmpeg + * Method: initnew + * Signature: (IILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_initnew + (JNIEnv *, jobject, jint, jint, jstring); + +/* + * Class: ai_suanzi_rtmpclient_Ffmpeg * Method: flush * Signature: ()I */ @@ -95,6 +103,14 @@ JNIEXPORT jstring JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_getPerfectDevice JNIEXPORT jint JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_test (JNIEnv *, jobject, jint); +/* + * Class: ai_suanzi_rtmpclient_Ffmpeg + * Method: setRtmpUrl + * Signature: (Ljava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_ai_suanzi_rtmpclient_Ffmpeg_setRtmpUrl + (JNIEnv *, jobject, jstring); + #ifdef __cplusplus } #endif diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 83a1ab4..05a334a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ Button play RTMP Server - rtmp://gpussh.suanzi.ai:1935/myapp + rtmp://gpussh.suanzi.ai:1935/myapp/ User suanzi Camera ID