diff --git a/library/src/main/java/org/easydarwin/push/MediaStream.java b/library/src/main/java/org/easydarwin/push/MediaStream.java index ef9a3dd..ab53430 100644 --- a/library/src/main/java/org/easydarwin/push/MediaStream.java +++ b/library/src/main/java/org/easydarwin/push/MediaStream.java @@ -3,12 +3,14 @@ package org.easydarwin.push; import android.app.Activity; import android.app.Application; import android.app.Service; + import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.OnLifecycleEvent; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,9 +32,11 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Process; import android.preference.PreferenceManager; + import androidx.annotation.MainThread; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; + import android.text.TextUtils; import android.util.Log; import android.view.Surface; @@ -90,15 +94,15 @@ public class MediaStream extends Service implements LifecycleObserver { } public class MediaBinder extends Binder { - public MediaStream getService(){ + public MediaStream getService() { return MediaStream.this; } } - static class MediaStreamPublisher implements Publisher, LifecycleObserver{ + static class MediaStreamPublisher implements Publisher, LifecycleObserver { private final LifecycleOwner lifecyclerOwner; - private ServiceConnection conn; + private ServiceConnection conn; private final WeakReference context; private MediaStreamPublisher(Context context, LifecycleOwner owner) { @@ -129,21 +133,21 @@ public class MediaStream extends Service implements LifecycleObserver { Context c = context.get(); if (c == null) return; Intent serv = new Intent(c, MediaStream.class); - if (!c.bindService(serv, conn, 0)){ + if (!c.bindService(serv, conn, 0)) { s.onError(new IllegalStateException("bindService error!")); s.onComplete(); } } @OnLifecycleEvent(value = Lifecycle.Event.ON_DESTROY) - void destory(){ + void destory() { Context c = context.get(); if (c == null) return; c.unbindService(conn); } } - public static Publisher getBindedMediaStream(final Context context, LifecycleOwner owner){ + public static Publisher getBindedMediaStream(final Context context, LifecycleOwner owner) { final MediaStreamPublisher publisher = new MediaStreamPublisher(context, owner); return publisher; } @@ -186,13 +190,14 @@ public class MediaStream extends Service implements LifecycleObserver { boolean hevcEncode = false; public String mime = ""; } + public CodecInfo info = new CodecInfo(); public void startStream(String ip, String port, String id, InitCallback callback) { - mEasyPusher.initPush( mApplicationContext, callback); - PushingState.sCodec = mSWCodec ? "x264":(info.hevcEncode ? "hevc":"avc"); - mEasyPusher.setMediaInfo(!mSWCodec && info.hevcEncode ? Pusher.Codec.EASY_SDK_VIDEO_CODEC_H265:Pusher.Codec.EASY_SDK_VIDEO_CODEC_H264, 25, Pusher.Codec.EASY_SDK_AUDIO_CODEC_AAC, 1, 8000, 16); + mEasyPusher.initPush(mApplicationContext, callback); + PushingState.sCodec = mSWCodec ? "x264" : (info.hevcEncode ? "hevc" : "avc"); + mEasyPusher.setMediaInfo(!mSWCodec && info.hevcEncode ? Pusher.Codec.EASY_SDK_VIDEO_CODEC_H265 : Pusher.Codec.EASY_SDK_VIDEO_CODEC_H264, 25, Pusher.Codec.EASY_SDK_AUDIO_CODEC_AAC, 1, 8000, 16); mEasyPusher.start(ip, port, String.format("%s.sdp", id), Pusher.TransType.EASY_RTP_OVER_TCP); } @@ -212,7 +217,7 @@ public class MediaStream extends Service implements LifecycleObserver { if (pushScreenService != null) { stopPushScreen(); - }else{ + } else { Intent intent = new Intent(mApplicationContext, PushScreenService.class); conn = new ServiceConnection() { @@ -252,7 +257,7 @@ public class MediaStream extends Service implements LifecycleObserver { public final String msg; public final String url; public final boolean screenPushing; - static String sCodec ="avc"; + static String sCodec = "avc"; public String videoCodec = sCodec; public PushingState(int state, String msg) { @@ -351,7 +356,7 @@ public class MediaStream extends Service implements LifecycleObserver { mApplicationContext = getApplication(); File youyuan = getFileStreamPath("SIMYOU.ttf"); - if (!youyuan.exists()){ + if (!youyuan.exists()) { AssetManager am = getAssets(); try { InputStream is = am.open("zk/SIMYOU.ttf"); @@ -403,32 +408,30 @@ public class MediaStream extends Service implements LifecycleObserver { } public void onPreviewFrame2(byte[] data, Object camera) { + boolean enableOverlay = PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true); if (camera instanceof Camera) { - if (PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true)) { - overlay.javaOverlay(data, "EasyPusher"); + Camera.CameraInfo camInfo = new Camera.CameraInfo(); + Camera.getCameraInfo(mCameraId, camInfo); + int cameraRotationOffset = camInfo.orientation; + if (enableOverlay) { + overlay.javaOverlay(data, width, height, cameraRotationOffset, "EasyPusher"); } if (mDgree == 0) { - Camera.CameraInfo camInfo = new Camera.CameraInfo(); - Camera.getCameraInfo(mCameraId, camInfo); - int cameraRotationOffset = camInfo.orientation; - if (cameraRotationOffset % 180 != 0) { yuvRotate(data, 1, width, height, cameraRotationOffset); } save2file(data, String.format("/sdcard/yuv_%d_%d.yuv", height, width)); } - if (PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true)) { - String txt;// = String.format("drawtext=fontfile=" + mApplicationContext.getFileStreamPath("SIMYOU.ttf") + ": text='%s%s':x=(w-text_w)/2:y=H-60 :fontcolor=white :box=1:boxcolor=0x00000000@0.3", "EasyPusher", new SimpleDateFormat("yyyy-MM-ddHHmmss").format(new Date())); - txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date()); + if (enableOverlay) { + String txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date()); overlay.overlay(data, txt); } mVC.onVideo(data, NV21); mCamera.addCallbackBuffer(data); } else { - if (PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true)) { - String txt;// = String.format("drawtext=fontfile=" + mApplicationContext.getFileStreamPath("SIMYOU.ttf") + ": text='%s%s':x=(w-text_w)/2:y=H-60 :fontcolor=white :box=1:boxcolor=0x00000000@0.3", "EasyPusher", new SimpleDateFormat("yyyy-MM-ddHHmmss").format(new Date())); - txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date()); - overlay.javaOverlay(data, txt); + if (enableOverlay) { + String txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date()); + overlay.javaOverlay(data, width, height, 0, txt); overlay.overlay(data, txt); } mVC.onVideo(data, NV21); @@ -442,7 +445,7 @@ public class MediaStream extends Service implements LifecycleObserver { mCameraHandler.post(new Runnable() { @Override public void run() { - startStream(ip,port, id); + startStream(ip, port, id); } }); return; @@ -454,7 +457,7 @@ public class MediaStream extends Service implements LifecycleObserver { @Override public void onCallback(int code) { String msg = ""; - String url = String.format("rtsp://%s:%s/%s.sdp", ip,port,id); + String url = String.format("rtsp://%s:%s/%s.sdp", ip, port, id); switch (code) { case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_INVALID_KEY: msg = ("无效Key"); @@ -495,7 +498,7 @@ public class MediaStream extends Service implements LifecycleObserver { }; // mEasyPusher.initPush(ip, port, String.format("%s.sdp", id), mApplicationContext, callback); - startStream(ip,port,id, callback); + startStream(ip, port, id, callback); } @@ -515,12 +518,12 @@ public class MediaStream extends Service implements LifecycleObserver { } - public boolean isScreenPushing(){ + public boolean isScreenPushing() { return pushScreenService != null; } - public boolean isCameraPushing(){ + public boolean isCameraPushing() { return cameraPushing; } @@ -546,7 +549,7 @@ public class MediaStream extends Service implements LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) public void destory() { if (false) - closeCameraPreview(); + closeCameraPreview(); if (lifecycle != null) lifecycle.removeObserver(this); } @@ -592,15 +595,15 @@ public class MediaStream extends Service implements LifecycleObserver { } - public static void initEncoder(Context context, CodecInfo info){ + public static void initEncoder(Context context, CodecInfo info) { info.hevcEncode = false; boolean try265Encode = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("try_265_encode", false); - ArrayList infos = listEncoders(try265Encode ?MediaFormat.MIMETYPE_VIDEO_HEVC:MediaFormat.MIMETYPE_VIDEO_AVC); + ArrayList infos = listEncoders(try265Encode ? MediaFormat.MIMETYPE_VIDEO_HEVC : MediaFormat.MIMETYPE_VIDEO_AVC); if (infos.isEmpty()) { - if (try265Encode){ + if (try265Encode) { infos = listEncoders(MediaFormat.MIMETYPE_VIDEO_AVC); } - }else{ + } else { if (try265Encode) info.hevcEncode = true; } if (!infos.isEmpty()) { @@ -608,7 +611,7 @@ public class MediaStream extends Service implements LifecycleObserver { info.mName = ci.mName; info.mColorFormat = ci.mColorFormat; info.mime = ci.mime; - }else{ + } else { info.mName = ""; info.mColorFormat = 0; } @@ -643,10 +646,10 @@ public class MediaStream extends Service implements LifecycleObserver { if (value != null) { // uvc camera. uvcCamera = value; - value.setPreviewSize(width, height,1, 30, UVCCamera.PIXEL_FORMAT_YUV420SP,1.0f); + value.setPreviewSize(width, height, 1, 30, UVCCamera.PIXEL_FORMAT_YUV420SP, 1.0f); return; // value.startPreview(); - }else{ + } else { Log.i(TAG, "NO UVCCamera"); uvcError = new Exception("no uvccamera connected!"); return; @@ -752,7 +755,7 @@ public class MediaStream extends Service implements LifecycleObserver { return length; } - public synchronized boolean isRecording(){ + public synchronized boolean isRecording() { return mIsRecording; } @@ -828,11 +831,11 @@ public class MediaStream extends Service implements LifecycleObserver { value.setFrameCallback(uvcFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP/*UVCCamera.PIXEL_FORMAT_NV21*/); value.startPreview(); cameraPreviewResolution.postValue(new int[]{width, height}); - }catch (Throwable e){ + } catch (Throwable e) { uvcError = e; } - }else if (mCamera != null) { + } else if (mCamera != null) { int previewFormat = mCamera.getParameters().getPreviewFormat(); Camera.Size previewSize = mCamera.getParameters().getPreviewSize(); int size = previewSize.width * previewSize.height * ImageFormat.getBitsPerPixel(previewFormat) / 8; @@ -989,8 +992,7 @@ public class MediaStream extends Service implements LifecycleObserver { } /** - * - * @param cameraId 0表示后置,1表示前置,2表示uvc摄像头,-1表示默认切换(比如前后置来回切换.).在非-1的情况下,如果没有ID对应的摄像头,则也会作默认切换. + * @param cameraId 0表示后置,1表示前置,2表示uvc摄像头,-1表示默认切换(比如前后置来回切换.).在非-1的情况下,如果没有ID对应的摄像头,则也会作默认切换. */ public Publisher switchCamera(final int cameraId) { Publisher pub = new Publisher() { @@ -1005,7 +1007,7 @@ public class MediaStream extends Service implements LifecycleObserver { return pub; } - public void switchCamera(){ + public void switchCamera() { switchCamera(-1).subscribe(new Subscriber() { @Override public void onSubscribe(Subscription s) { @@ -1062,23 +1064,23 @@ public class MediaStream extends Service implements LifecycleObserver { destroyCamera(); createCamera(); startPreview(); - }finally { - if (uvcCamera != null){ - if (mSwitchCameraSubscriber != null){ + } finally { + if (uvcCamera != null) { + if (mSwitchCameraSubscriber != null) { mSwitchCameraSubscriber.onNext(uvcCamera); } - }else if (mCamera != null){ - if (mSwitchCameraSubscriber != null){ + } else if (mCamera != null) { + if (mSwitchCameraSubscriber != null) { mSwitchCameraSubscriber.onNext(mCamera); } - }else { - if (mSwitchCameraSubscriber != null){ - if (uvcError != null){ + } else { + if (mSwitchCameraSubscriber != null) { + if (uvcError != null) { mSwitchCameraSubscriber.onError(uvcError); - }else { + } else { mSwitchCameraSubscriber.onError(new IOException("could not create camera of id:" + mCameraId)); } - }else{ + } else { // uvcCamera = new UVCCamera(); // mSwitchCameraSubscriber.onNext(uvcCamera); // if (uvcFrameCallback != null){ @@ -1173,9 +1175,9 @@ public class MediaStream extends Service implements LifecycleObserver { mCameraHandler.post(new Runnable() { @Override public void run() { - if (uvcCamera != null){ + if (uvcCamera != null) { uvcCamera.setPreviewDisplay((Surface) null); - }else { + } else { stopPreview(); } } @@ -1187,9 +1189,9 @@ public class MediaStream extends Service implements LifecycleObserver { mCameraHandler.post(new Runnable() { @Override public void run() { - if (uvcCamera != null){ + if (uvcCamera != null) { uvcCamera.setPreviewDisplay(new Surface(texture)); - }else { + } else { stopPreview(); if (cameraOpened) openCameraPreview(); } @@ -1245,7 +1247,6 @@ public class MediaStream extends Service implements LifecycleObserver { } - public static ArrayList listEncoders(String mime) { // 可能有多个编码库,都获取一下。。。 ArrayList codecInfos = new ArrayList(); diff --git a/library/src/main/java/org/easydarwin/sw/TxtOverlay.java b/library/src/main/java/org/easydarwin/sw/TxtOverlay.java index 39cb5ce..6caacb0 100644 --- a/library/src/main/java/org/easydarwin/sw/TxtOverlay.java +++ b/library/src/main/java/org/easydarwin/sw/TxtOverlay.java @@ -22,8 +22,8 @@ public class TxtOverlay { private final Context context; - int startY = 100;//水印Y轴的位置 - int startX = 100;//水印X轴的位置 + int startY = 50;//水印Y轴的位置 + int startX = 10;//水印X轴的位置 Bitmap bmp; byte[] mark; @@ -55,15 +55,23 @@ public class TxtOverlay { txtOverlay(ctx, data, txt); } - public void javaOverlay(byte[] data, + public void javaOverlay(byte[] data, int width, int height, int cameraRotationOffset, String txt) { - if (ctx == 0) return; + int bmpWidth = bmp.getWidth(); + int bmpHeight = bmp.getHeight(); + if (cameraRotationOffset == 90) { + startY = height - 50 - bmpHeight; + startX = 10; + } else if (cameraRotationOffset == 270) { + startY = 50; + startX = width - 50 - bmpWidth; + } int j = 0; - for (int i = startY; i < bmp.getHeight() + startY; i++) { - for (int c = 0; c < bmp.getWidth(); c++) { + for (int i = startY; i < bmpHeight + startY; i++) { + for (int c = 0; c < bmpWidth; c++) { //去掉PNG水印的黑边 - if (mark[j * bmp.getWidth() + c] != 0x10 && mark[j * bmp.getWidth() + c] != 0x80 && mark[j * bmp.getWidth() + c] != 0xeb) { - System.arraycopy(mark, j * bmp.getWidth() + c, data, startX + i * 1280 + c, 1); + if (mark[j * bmpWidth + c] != 0x10 && mark[j * bmpWidth + c] != 0x80 && mark[j * bmpWidth + c] != 0xeb) { + System.arraycopy(mark, j * bmpWidth + c, data, startX + i * width + c, 1); } } j++;