desc:java方式水印适配视频分辨率,考虑旋转

main
xiaowusky 2 years ago
parent 73004fe84d
commit be26453cfe

@ -3,12 +3,14 @@ package org.easydarwin.push;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.app.Service; import android.app.Service;
import androidx.lifecycle.Lifecycle; import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.OnLifecycleEvent; import androidx.lifecycle.OnLifecycleEvent;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -30,9 +32,11 @@ import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Process; import android.os.Process;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.Surface; import android.view.Surface;
@ -90,15 +94,15 @@ public class MediaStream extends Service implements LifecycleObserver {
} }
public class MediaBinder extends Binder { public class MediaBinder extends Binder {
public MediaStream getService(){ public MediaStream getService() {
return MediaStream.this; return MediaStream.this;
} }
} }
static class MediaStreamPublisher implements Publisher<MediaStream>, LifecycleObserver{ static class MediaStreamPublisher implements Publisher<MediaStream>, LifecycleObserver {
private final LifecycleOwner lifecyclerOwner; private final LifecycleOwner lifecyclerOwner;
private ServiceConnection conn; private ServiceConnection conn;
private final WeakReference<Context> context; private final WeakReference<Context> context;
private MediaStreamPublisher(Context context, LifecycleOwner owner) { private MediaStreamPublisher(Context context, LifecycleOwner owner) {
@ -129,21 +133,21 @@ public class MediaStream extends Service implements LifecycleObserver {
Context c = context.get(); Context c = context.get();
if (c == null) return; if (c == null) return;
Intent serv = new Intent(c, MediaStream.class); 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.onError(new IllegalStateException("bindService error!"));
s.onComplete(); s.onComplete();
} }
} }
@OnLifecycleEvent(value = Lifecycle.Event.ON_DESTROY) @OnLifecycleEvent(value = Lifecycle.Event.ON_DESTROY)
void destory(){ void destory() {
Context c = context.get(); Context c = context.get();
if (c == null) return; if (c == null) return;
c.unbindService(conn); c.unbindService(conn);
} }
} }
public static Publisher<MediaStream> getBindedMediaStream(final Context context, LifecycleOwner owner){ public static Publisher<MediaStream> getBindedMediaStream(final Context context, LifecycleOwner owner) {
final MediaStreamPublisher publisher = new MediaStreamPublisher(context, owner); final MediaStreamPublisher publisher = new MediaStreamPublisher(context, owner);
return publisher; return publisher;
} }
@ -186,13 +190,14 @@ public class MediaStream extends Service implements LifecycleObserver {
boolean hevcEncode = false; boolean hevcEncode = false;
public String mime = ""; public String mime = "";
} }
public CodecInfo info = new CodecInfo(); public CodecInfo info = new CodecInfo();
public void startStream(String ip, String port, String id, InitCallback callback) { public void startStream(String ip, String port, String id, InitCallback callback) {
mEasyPusher.initPush( mApplicationContext, callback); mEasyPusher.initPush(mApplicationContext, callback);
PushingState.sCodec = mSWCodec ? "x264":(info.hevcEncode ? "hevc":"avc"); 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.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); 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) { if (pushScreenService != null) {
stopPushScreen(); stopPushScreen();
}else{ } else {
Intent intent = new Intent(mApplicationContext, PushScreenService.class); Intent intent = new Intent(mApplicationContext, PushScreenService.class);
conn = new ServiceConnection() { conn = new ServiceConnection() {
@ -252,7 +257,7 @@ public class MediaStream extends Service implements LifecycleObserver {
public final String msg; public final String msg;
public final String url; public final String url;
public final boolean screenPushing; public final boolean screenPushing;
static String sCodec ="avc"; static String sCodec = "avc";
public String videoCodec = sCodec; public String videoCodec = sCodec;
public PushingState(int state, String msg) { public PushingState(int state, String msg) {
@ -351,7 +356,7 @@ public class MediaStream extends Service implements LifecycleObserver {
mApplicationContext = getApplication(); mApplicationContext = getApplication();
File youyuan = getFileStreamPath("SIMYOU.ttf"); File youyuan = getFileStreamPath("SIMYOU.ttf");
if (!youyuan.exists()){ if (!youyuan.exists()) {
AssetManager am = getAssets(); AssetManager am = getAssets();
try { try {
InputStream is = am.open("zk/SIMYOU.ttf"); 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) { public void onPreviewFrame2(byte[] data, Object camera) {
boolean enableOverlay = PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true);
if (camera instanceof Camera) { if (camera instanceof Camera) {
if (PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true)) { Camera.CameraInfo camInfo = new Camera.CameraInfo();
overlay.javaOverlay(data, "EasyPusher"); Camera.getCameraInfo(mCameraId, camInfo);
int cameraRotationOffset = camInfo.orientation;
if (enableOverlay) {
overlay.javaOverlay(data, width, height, cameraRotationOffset, "EasyPusher");
} }
if (mDgree == 0) { if (mDgree == 0) {
Camera.CameraInfo camInfo = new Camera.CameraInfo();
Camera.getCameraInfo(mCameraId, camInfo);
int cameraRotationOffset = camInfo.orientation;
if (cameraRotationOffset % 180 != 0) { if (cameraRotationOffset % 180 != 0) {
yuvRotate(data, 1, width, height, cameraRotationOffset); yuvRotate(data, 1, width, height, cameraRotationOffset);
} }
save2file(data, String.format("/sdcard/yuv_%d_%d.yuv", height, width)); save2file(data, String.format("/sdcard/yuv_%d_%d.yuv", height, width));
} }
if (PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true)) { if (enableOverlay) {
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())); String txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date());
txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date());
overlay.overlay(data, txt); overlay.overlay(data, txt);
} }
mVC.onVideo(data, NV21); mVC.onVideo(data, NV21);
mCamera.addCallbackBuffer(data); mCamera.addCallbackBuffer(data);
} else { } else {
if (PreferenceManager.getDefaultSharedPreferences(mApplicationContext).getBoolean("key_enable_video_overlay", true)) { if (enableOverlay) {
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())); String txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date());
txt = "EasyPusher " + new SimpleDateFormat("yy-MM-dd HH:mm:ss SSS").format(new Date()); overlay.javaOverlay(data, width, height, 0, txt);
overlay.javaOverlay(data, txt);
overlay.overlay(data, txt); overlay.overlay(data, txt);
} }
mVC.onVideo(data, NV21); mVC.onVideo(data, NV21);
@ -442,7 +445,7 @@ public class MediaStream extends Service implements LifecycleObserver {
mCameraHandler.post(new Runnable() { mCameraHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
startStream(ip,port, id); startStream(ip, port, id);
} }
}); });
return; return;
@ -454,7 +457,7 @@ public class MediaStream extends Service implements LifecycleObserver {
@Override @Override
public void onCallback(int code) { public void onCallback(int code) {
String msg = ""; 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) { switch (code) {
case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_INVALID_KEY: case EasyPusher.OnInitPusherCallback.CODE.EASY_ACTIVATE_INVALID_KEY:
msg = ("无效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); // 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; return pushScreenService != null;
} }
public boolean isCameraPushing(){ public boolean isCameraPushing() {
return cameraPushing; return cameraPushing;
} }
@ -546,7 +549,7 @@ public class MediaStream extends Service implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void destory() { public void destory() {
if (false) if (false)
closeCameraPreview(); closeCameraPreview();
if (lifecycle != null) lifecycle.removeObserver(this); 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; info.hevcEncode = false;
boolean try265Encode = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("try_265_encode", false); boolean try265Encode = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("try_265_encode", false);
ArrayList<CodecInfo> infos = listEncoders(try265Encode ?MediaFormat.MIMETYPE_VIDEO_HEVC:MediaFormat.MIMETYPE_VIDEO_AVC); ArrayList<CodecInfo> infos = listEncoders(try265Encode ? MediaFormat.MIMETYPE_VIDEO_HEVC : MediaFormat.MIMETYPE_VIDEO_AVC);
if (infos.isEmpty()) { if (infos.isEmpty()) {
if (try265Encode){ if (try265Encode) {
infos = listEncoders(MediaFormat.MIMETYPE_VIDEO_AVC); infos = listEncoders(MediaFormat.MIMETYPE_VIDEO_AVC);
} }
}else{ } else {
if (try265Encode) info.hevcEncode = true; if (try265Encode) info.hevcEncode = true;
} }
if (!infos.isEmpty()) { if (!infos.isEmpty()) {
@ -608,7 +611,7 @@ public class MediaStream extends Service implements LifecycleObserver {
info.mName = ci.mName; info.mName = ci.mName;
info.mColorFormat = ci.mColorFormat; info.mColorFormat = ci.mColorFormat;
info.mime = ci.mime; info.mime = ci.mime;
}else{ } else {
info.mName = ""; info.mName = "";
info.mColorFormat = 0; info.mColorFormat = 0;
} }
@ -643,10 +646,10 @@ public class MediaStream extends Service implements LifecycleObserver {
if (value != null) { if (value != null) {
// uvc camera. // uvc camera.
uvcCamera = value; 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; return;
// value.startPreview(); // value.startPreview();
}else{ } else {
Log.i(TAG, "NO UVCCamera"); Log.i(TAG, "NO UVCCamera");
uvcError = new Exception("no uvccamera connected!"); uvcError = new Exception("no uvccamera connected!");
return; return;
@ -752,7 +755,7 @@ public class MediaStream extends Service implements LifecycleObserver {
return length; return length;
} }
public synchronized boolean isRecording(){ public synchronized boolean isRecording() {
return mIsRecording; 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.setFrameCallback(uvcFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP/*UVCCamera.PIXEL_FORMAT_NV21*/);
value.startPreview(); value.startPreview();
cameraPreviewResolution.postValue(new int[]{width, height}); cameraPreviewResolution.postValue(new int[]{width, height});
}catch (Throwable e){ } catch (Throwable e) {
uvcError = e; uvcError = e;
} }
}else if (mCamera != null) { } else if (mCamera != null) {
int previewFormat = mCamera.getParameters().getPreviewFormat(); int previewFormat = mCamera.getParameters().getPreviewFormat();
Camera.Size previewSize = mCamera.getParameters().getPreviewSize(); Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
int size = previewSize.width * previewSize.height * ImageFormat.getBitsPerPixel(previewFormat) / 8; 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,2uvc,-1(.).-1,ID,.
* @param cameraId 0,1,2uvc,-1(.).-1,ID,.
*/ */
public Publisher<Object> switchCamera(final int cameraId) { public Publisher<Object> switchCamera(final int cameraId) {
Publisher pub = new Publisher<Object>() { Publisher pub = new Publisher<Object>() {
@ -1005,7 +1007,7 @@ public class MediaStream extends Service implements LifecycleObserver {
return pub; return pub;
} }
public void switchCamera(){ public void switchCamera() {
switchCamera(-1).subscribe(new Subscriber<Object>() { switchCamera(-1).subscribe(new Subscriber<Object>() {
@Override @Override
public void onSubscribe(Subscription s) { public void onSubscribe(Subscription s) {
@ -1062,23 +1064,23 @@ public class MediaStream extends Service implements LifecycleObserver {
destroyCamera(); destroyCamera();
createCamera(); createCamera();
startPreview(); startPreview();
}finally { } finally {
if (uvcCamera != null){ if (uvcCamera != null) {
if (mSwitchCameraSubscriber != null){ if (mSwitchCameraSubscriber != null) {
mSwitchCameraSubscriber.onNext(uvcCamera); mSwitchCameraSubscriber.onNext(uvcCamera);
} }
}else if (mCamera != null){ } else if (mCamera != null) {
if (mSwitchCameraSubscriber != null){ if (mSwitchCameraSubscriber != null) {
mSwitchCameraSubscriber.onNext(mCamera); mSwitchCameraSubscriber.onNext(mCamera);
} }
}else { } else {
if (mSwitchCameraSubscriber != null){ if (mSwitchCameraSubscriber != null) {
if (uvcError != null){ if (uvcError != null) {
mSwitchCameraSubscriber.onError(uvcError); mSwitchCameraSubscriber.onError(uvcError);
}else { } else {
mSwitchCameraSubscriber.onError(new IOException("could not create camera of id:" + mCameraId)); mSwitchCameraSubscriber.onError(new IOException("could not create camera of id:" + mCameraId));
} }
}else{ } else {
// uvcCamera = new UVCCamera(); // uvcCamera = new UVCCamera();
// mSwitchCameraSubscriber.onNext(uvcCamera); // mSwitchCameraSubscriber.onNext(uvcCamera);
// if (uvcFrameCallback != null){ // if (uvcFrameCallback != null){
@ -1173,9 +1175,9 @@ public class MediaStream extends Service implements LifecycleObserver {
mCameraHandler.post(new Runnable() { mCameraHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
if (uvcCamera != null){ if (uvcCamera != null) {
uvcCamera.setPreviewDisplay((Surface) null); uvcCamera.setPreviewDisplay((Surface) null);
}else { } else {
stopPreview(); stopPreview();
} }
} }
@ -1187,9 +1189,9 @@ public class MediaStream extends Service implements LifecycleObserver {
mCameraHandler.post(new Runnable() { mCameraHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
if (uvcCamera != null){ if (uvcCamera != null) {
uvcCamera.setPreviewDisplay(new Surface(texture)); uvcCamera.setPreviewDisplay(new Surface(texture));
}else { } else {
stopPreview(); stopPreview();
if (cameraOpened) openCameraPreview(); if (cameraOpened) openCameraPreview();
} }
@ -1245,7 +1247,6 @@ public class MediaStream extends Service implements LifecycleObserver {
} }
public static ArrayList<CodecInfo> listEncoders(String mime) { public static ArrayList<CodecInfo> listEncoders(String mime) {
// 可能有多个编码库,都获取一下。。。 // 可能有多个编码库,都获取一下。。。
ArrayList<CodecInfo> codecInfos = new ArrayList<CodecInfo>(); ArrayList<CodecInfo> codecInfos = new ArrayList<CodecInfo>();

@ -22,8 +22,8 @@ public class TxtOverlay {
private final Context context; private final Context context;
int startY = 100;//水印Y轴的位置 int startY = 50;//水印Y轴的位置
int startX = 100;//水印X轴的位置 int startX = 10;//水印X轴的位置
Bitmap bmp; Bitmap bmp;
byte[] mark; byte[] mark;
@ -55,15 +55,23 @@ public class TxtOverlay {
txtOverlay(ctx, data, txt); txtOverlay(ctx, data, txt);
} }
public void javaOverlay(byte[] data, public void javaOverlay(byte[] data, int width, int height, int cameraRotationOffset,
String txt) { 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; int j = 0;
for (int i = startY; i < bmp.getHeight() + startY; i++) { for (int i = startY; i < bmpHeight + startY; i++) {
for (int c = 0; c < bmp.getWidth(); c++) { for (int c = 0; c < bmpWidth; c++) {
//去掉PNG水印的黑边 //去掉PNG水印的黑边
if (mark[j * bmp.getWidth() + c] != 0x10 && mark[j * bmp.getWidth() + c] != 0x80 && mark[j * bmp.getWidth() + c] != 0xeb) { if (mark[j * bmpWidth + c] != 0x10 && mark[j * bmpWidth + c] != 0x80 && mark[j * bmpWidth + c] != 0xeb) {
System.arraycopy(mark, j * bmp.getWidth() + c, data, startX + i * 1280 + c, 1); System.arraycopy(mark, j * bmpWidth + c, data, startX + i * width + c, 1);
} }
} }
j++; j++;

Loading…
Cancel
Save