diff --git a/app/src/main/java/com/xypower/mpapp/v2/Camera2VideoActivity.java b/app/src/main/java/com/xypower/mpapp/v2/Camera2VideoActivity.java index 89da4313..1fa14643 100644 --- a/app/src/main/java/com/xypower/mpapp/v2/Camera2VideoActivity.java +++ b/app/src/main/java/com/xypower/mpapp/v2/Camera2VideoActivity.java @@ -17,8 +17,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.provider.MediaStore; import android.text.TextUtils; import android.util.Log; @@ -51,7 +49,6 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.Timer; import java.util.concurrent.Semaphore; public class Camera2VideoActivity extends AppCompatActivity { @@ -84,12 +81,11 @@ public class Camera2VideoActivity extends AppCompatActivity { private int mOSDMargin = 0; private Paint mPaint; private Paint mPaintStroker; - - private List mBitmaps = new ArrayList<>(); - + private Bitmap mBitmap; private GlWatermarkFilter mOSDFilter = null; private Object mBitmapLocker = new Object(); + private SimpleDateFormat mDateFormater; // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss a"); @@ -98,6 +94,9 @@ public class Camera2VideoActivity extends AppCompatActivity { private int mTimeMask = 0; private int mStatusBarHeight = -1; + private long mOsdTs = 0; + private Semaphore mOSDSemaphore = new Semaphore(0); + private static class OSD_ITEM { @@ -127,75 +126,49 @@ public class Camera2VideoActivity extends AppCompatActivity { private final static int TIME_MASK_LB = TIME_MASK_LB_TS | TIME_MASK_LB_DT | TIME_MASK_LB_ML; private Handler mHandler = null; - - private class TimerRunner implements Runnable { - private Bitmap mBitmap; - - TimerRunner(Bitmap bm) { - mBitmap = bm; - } - + private Runnable mTimerRunnable = new Runnable() { @Override public void run() { - Bitmap oldBm = null; - if (mBitmap != null) { - oldBm = mOSDFilter.updateBitmap(mBitmap); - } - if (oldBm != null) { - synchronized (mBitmapLocker) { - mBitmaps.add(oldBm); + synchronized (mBitmapLocker) { + if (mBitmap != null) { + Bitmap bitmap = mBitmap; + mBitmap = null; + mOSDFilter.updateBitmap(bitmap); } } long ts = System.currentTimeMillis(); + mOsdTs = ts + 1000; // next second long ms = ts % 1000; Log.d("OSD", "Cur TS=" + Long.toString(ts / 1000) + " Timer=" + Long.toString(1000 - ms)); - Message msg = Message.obtain(); - msg.what = 1; - msg.obj = new Long(ts - ms + 1000); - mOsdHandler.sendMessage(msg); + mOSDSemaphore.release(); + + mHandler.postDelayed(this, 1000 - ms); } - } + }; - private Handler mOsdHandler; private Thread mOsdThread = new Thread(new Runnable() { @Override public void run() { - Looper.prepare(); - - mOsdHandler = new Handler(Looper.myLooper()) { - public void handleMessage(Message msg) { - if (msg.what == 1) { - Long targetTs = (Long)msg.obj; - long ts = System.currentTimeMillis(); - if (ts > targetTs.longValue()) { - return; - } - Bitmap bm = null; - synchronized (mBitmapLocker) { - if (!mBitmaps.isEmpty()) { - bm = mBitmaps.remove(0); - } - } - if (bm != null) { - updateOSD(bm, targetTs.longValue()); - - TimerRunner runner = new TimerRunner(bm); - mHandler.postDelayed(runner, targetTs.longValue() - ts); - } - } else if (msg.what == 0) { - Looper.myLooper().quitSafely(); - } + while (true) { + try { + mOSDSemaphore.acquire(); + } catch (Exception ex) { } - }; - Looper.loop(); + if (mOsdTs == -1) { + break; + } + + updateOSD(mOsdTs); + } } }); + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -204,6 +177,18 @@ public class Camera2VideoActivity extends AppCompatActivity { Window win = getWindow(); win.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + /* + win.setDecorFitsSystemWindows(false); + WindowInsetsController controller = win.getInsetsController(); + if (controller != null) { + controller.hide(WindowInsets.Type.statusBars() | WindowInsets.Type.navigationBars()); + controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + + */ + } + setContentView(R.layout.activity_camera2_video); getSupportActionBar().hide(); @@ -261,7 +246,7 @@ public class Camera2VideoActivity extends AppCompatActivity { mOSDLeftBottom = intent.getStringExtra("leftBottomOsd"); mOSDRightBottom = intent.getStringExtra("rightBottomOsd"); mOSDRightTop = intent.getStringExtra("rightTopOsd"); - mOSDMargin = px2dip(this, intent.getIntExtra("margin", 12)); + mOSDMargin = intent.getIntExtra("margin", 20); mCameraWidth = mVideoWidth; mCameraHeight = mVideoHeight; @@ -332,7 +317,6 @@ public class Camera2VideoActivity extends AppCompatActivity { long ts = 0; long zeroTs = 0; - Bitmap bm2 = null; if (!TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop)) { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); @@ -346,27 +330,22 @@ public class Camera2VideoActivity extends AppCompatActivity { mPaintStroker.setTextSize(fontSize); mPaintStroker.setStrokeWidth(1); - bm2 = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.ARGB_8888); - - Bitmap bm = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.ARGB_8888); + mBitmap = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bm); + Canvas canvas = new Canvas(mBitmap); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); ts = System.currentTimeMillis(); zeroTs = ts - (ts % 1000); - initOSD(bm, zeroTs); - mOSDFilter = new GlWatermarkFilter(bm); + initOSD(zeroTs); - if (mGPUCameraRecorder != null) { - mGPUCameraRecorder.setFilter(mOSDFilter); - } + mOSDFilter = new GlWatermarkFilter(mBitmap); - updateOSD(bm2, zeroTs + 1000); + mBitmap = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.ARGB_8888); + updateOSD(zeroTs + 1000); } final long prevZeroTs = zeroTs; - final Bitmap finalBm = bm2; mHandler.postDelayed(new Runnable() { @Override public void run() { @@ -380,22 +359,14 @@ public class Camera2VideoActivity extends AppCompatActivity { if (zeroTs2 > prevZeroTs) { // Next second - Bitmap oldBm = mOSDFilter.updateBitmap(finalBm); - if (oldBm != null) { - synchronized (mBitmapLocker) { - mBitmaps.add(oldBm); - } - } - Message msg = Message.obtain(); - msg.what = 1; - msg.obj = new Long(zeroTs2 + 1000); - // mOsdTs = zeroTs2 + 1000; - mOsdHandler.sendMessage(msg); - } else { - TimerRunner runner = new TimerRunner(finalBm); - mHandler.postDelayed(runner, 1000 - (ts2 - zeroTs2)); + mOSDFilter.updateBitmap(mBitmap); + mBitmap = null; + mOsdTs = zeroTs2 + 1000; + mOSDSemaphore.release(); } + Log.d("OSD", "Cur TS=" + Long.toString(ts2 / 1000) + " Timer=" + Long.toString(1000 - (ts2 - zeroTs2))); + mHandler.postDelayed(mTimerRunnable, 1000 - (ts2 - zeroTs2)); } } }, 0); @@ -422,7 +393,7 @@ public class Camera2VideoActivity extends AppCompatActivity { releaseCamera(); } - private void initOSD(Bitmap bm, long ts) { + private void initOSD(long ts) { Log.d("OSD", "INIT OSD " + Long.toString(ts / 1000)); @@ -430,183 +401,184 @@ public class Camera2VideoActivity extends AppCompatActivity { mStatusBarHeight = getStatusBarHeight(this); } int statusHeight = mStatusBarHeight; + synchronized (mBitmapLocker) { + int bmWidth = mBitmap.getWidth(); + int bmHeight = mBitmap.getHeight(); + int margin = mOSDMargin; + int x = 0; + int y = 0; + // mOSDFilter. - int bmWidth = bm.getWidth(); - int bmHeight = bm.getHeight(); - int margin = mOSDMargin; - int x = 0; - int y = 0; - - Canvas canvas = new Canvas(bm); - Rect textBounds = new Rect(); + Canvas canvas = new Canvas(mBitmap); + Rect textBounds = new Rect(); - canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); - if (!TextUtils.isEmpty(mOSDLeftTop)) { - String[] items = mOSDLeftTop.split("\n"); - Point origin = new Point(margin, margin + statusHeight); + if (!TextUtils.isEmpty(mOSDLeftTop)) { + String[] items = mOSDLeftTop.split("\n"); + Point origin = new Point(margin, margin + statusHeight); - for (String item : items) { + for (String item : items) { - int mask = 0; - if (item.indexOf(TIME_MICRO_TS) != 0) { - mask |= TIME_MASK_LT_TS; - } - if (item.indexOf(TIME_MICRO_DT) != 0) { - mask |= TIME_MASK_LT_DT; - } + int mask = 0; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_LT_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_LT_DT; + } - OSD_ITEM osdItem = new OSD_ITEM(); - osdItem.text = item; - osdItem.mask = mask; - osdItem.origin = new Point(origin); + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.mask = mask; + osdItem.origin = new Point(origin); - if (mask == 0) { - canvas.drawText(item, origin.x, origin.y, mPaint); - canvas.drawText(item, origin.x, origin.y, mPaintStroker); + if (mask == 0) { + canvas.drawText(item, origin.x, origin.y, mPaint); + canvas.drawText(item, origin.x, origin.y, mPaintStroker); - mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); - } else { - String newText = updateOSDTime(item, ts); + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); + } else { + String newText = updateOSDTime(item, ts); - canvas.drawText(newText, origin.x, origin.y, mPaint); - canvas.drawText(newText, origin.x, origin.y, mPaintStroker); + canvas.drawText(newText, origin.x, origin.y, mPaint); + canvas.drawText(newText, origin.x, origin.y, mPaintStroker); - mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); - } + mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); + } - osdItem.previousRect = new Rect(origin.x, origin.y, origin.x + textBounds.width(), origin.y + textBounds.height()); + osdItem.previousRect = new Rect(origin.x, origin.y, origin.x + textBounds.width(), origin.y + textBounds.height()); - mOSDItems.add(osdItem); - origin.y += (textBounds.height() * 3) >> 1; + mOSDItems.add(osdItem); + origin.y += (textBounds.height() * 3) >> 1; + } } - } - if (!TextUtils.isEmpty(mOSDLeftBottom)) { + if (!TextUtils.isEmpty(mOSDLeftBottom)) { - String[] items = mOSDLeftBottom.split("\n"); - Point origin = new Point(margin, bmHeight - margin); + String[] items = mOSDLeftBottom.split("\n"); + Point origin = new Point(margin, bmHeight - margin); - for(int idx = items.length-1; idx >= 0; idx--) { + for(int idx = items.length-1; idx >= 0; idx--) { - int mask = 0; - String item = items[idx]; - if (item.indexOf(TIME_MICRO_TS) != 0) { - mask |= TIME_MASK_LB_TS; - } - if (item.indexOf(TIME_MICRO_DT) != 0) { - mask |= TIME_MASK_LB_DT; - } + int mask = 0; + String item = items[idx]; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_LB_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_LB_DT; + } - OSD_ITEM osdItem = new OSD_ITEM(); - osdItem.text = item; - osdItem.mask = mask; - osdItem.origin = new Point(origin); - if (mask == 0) { - mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); - y = origin.y - textBounds.height(); + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.mask = mask; + osdItem.origin = new Point(origin); + if (mask == 0) { + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); + y = origin.y - textBounds.height(); - canvas.drawText(item, origin.x, y, mPaint); - canvas.drawText(item, origin.x, y, mPaintStroker); - } else { - String newText = updateOSDTime(item, ts); + canvas.drawText(item, origin.x, y, mPaint); + canvas.drawText(item, origin.x, y, mPaintStroker); + } else { + String newText = updateOSDTime(item, ts); - mPaintStroker.getTextBounds(newText, 0, newText.length(), textBounds); - y = origin.y - textBounds.height(); + mPaintStroker.getTextBounds(newText, 0, newText.length(), textBounds); + y = origin.y - textBounds.height(); - canvas.drawText(newText, origin.x, y, mPaint); - canvas.drawText(newText, origin.x, y, mPaintStroker); - } + canvas.drawText(newText, origin.x, y, mPaint); + canvas.drawText(newText, origin.x, y, mPaintStroker); + } - osdItem.previousRect = new Rect(origin.x, y, origin.x + textBounds.width(), y + textBounds.height()); - mOSDItems.add(osdItem); - origin.y -= (textBounds.height() * 3) >> 1; + osdItem.previousRect = new Rect(origin.x, y, origin.x + textBounds.width(), y + textBounds.height()); + mOSDItems.add(osdItem); + origin.y -= (textBounds.height() * 3) >> 1; + } } - } - if (!TextUtils.isEmpty(mOSDRightTop)) { + if (!TextUtils.isEmpty(mOSDRightTop)) { - String[] items = mOSDRightTop.split("\n"); - Point origin = new Point(bmWidth - margin, margin + statusHeight); + String[] items = mOSDRightTop.split("\n"); + Point origin = new Point(bmWidth - margin, margin + statusHeight); - for (String item : items) { + for (String item : items) { - int mask = 0; - if (item.indexOf(TIME_MICRO_TS) != 0) { - mask |= TIME_MASK_RT_TS; - } - if (item.indexOf(TIME_MICRO_DT) != 0) { - mask |= TIME_MASK_RT_DT; - } + int mask = 0; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_RT_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_RT_DT; + } - OSD_ITEM osdItem = new OSD_ITEM(); - osdItem.text = item; - osdItem.origin = new Point(origin); - osdItem.mask = mask; + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.origin = new Point(origin); + osdItem.mask = mask; - if (mask == 0) { - mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); + if (mask == 0) { + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); - canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaint); - canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaintStroker); - } else { + canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaint); + canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaintStroker); + } else { - String newText = updateOSDTime(item, ts); - mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); + String newText = updateOSDTime(item, ts); + mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); - canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaint); - canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaintStroker); - } - osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y, origin.x, origin.y + textBounds.height()); - mOSDItems.add(osdItem); + canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaint); + canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaintStroker); + } + osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y, origin.x, origin.y + textBounds.height()); + mOSDItems.add(osdItem); - origin.y += (textBounds.height() * 3) >> 1; + origin.y += (textBounds.height() * 3) >> 1; + } } - } - if (!TextUtils.isEmpty(mOSDRightBottom)) { + if (!TextUtils.isEmpty(mOSDRightBottom)) { - String[] items = mOSDRightBottom.split("\n"); - Point origin = new Point(bmWidth - margin, bmHeight - margin); + String[] items = mOSDRightBottom.split("\n"); + Point origin = new Point(bmWidth - margin, bmHeight - margin); - for(int idx = items.length-1; idx >= 0; idx--) { + for(int idx = items.length-1; idx >= 0; idx--) { - int mask = 0; - String item = items[idx]; - if (item.indexOf(TIME_MICRO_TS) != 0) { - mask |= TIME_MASK_RB_TS; - } - if (item.indexOf(TIME_MICRO_DT) != 0) { - mask |= TIME_MASK_RB_DT; - } + int mask = 0; + String item = items[idx]; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_RB_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_RB_DT; + } - OSD_ITEM osdItem = new OSD_ITEM(); - osdItem.text = item; - osdItem.origin = new Point(origin); - osdItem.mask = mask; - if (mask == 0) { - mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); - - canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint); - canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker); - } else { - String newText = updateOSDTime(item, ts); - mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); - - canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint); - canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker); - } + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.origin = new Point(origin); + osdItem.mask = mask; + if (mask == 0) { + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); - osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y - textBounds.height(), origin.x, origin.y); - mOSDItems.add(osdItem); + canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint); + canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker); + } else { + String newText = updateOSDTime(item, ts); + mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); + + canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint); + canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker); + } - origin.y -= (textBounds.height() * 3) >> 1; + osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y - textBounds.height(), origin.x, origin.y); + mOSDItems.add(osdItem); + + origin.y -= (textBounds.height() * 3) >> 1; + } } } - } - private void updateOSD(Bitmap bm, long ts) { + private void updateOSD(long ts) { Log.d("OSD", "prepareOSD " + Long.toString(ts / 1000)); if (mStatusBarHeight == -1) { @@ -614,6 +586,8 @@ public class Camera2VideoActivity extends AppCompatActivity { } int statusHeight = mStatusBarHeight; + Bitmap bm = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bm); boolean aa = canvas.isHardwareAccelerated(); Rect textBounds = new Rect(); @@ -664,6 +638,9 @@ public class Camera2VideoActivity extends AppCompatActivity { } } + synchronized (mBitmapLocker) { + mBitmap = bm; + } } private String updateOSDTime(String osd, long ts) { @@ -711,8 +688,11 @@ public class Camera2VideoActivity extends AppCompatActivity { if (mGPUCameraRecorder == null) return; mGPUCameraRecorder.changeManualFocusPoint(event.getX(), event.getY(), width, height); }); - frameLayout.addView(mPreviewView); + + if (mGPUCameraRecorder != null) { + mGPUCameraRecorder.setFilter(mOSDFilter); + } }); } @@ -733,11 +713,9 @@ public class Camera2VideoActivity extends AppCompatActivity { @Override public void onRecordComplete() { - // mHandler.removeCallbacks(mTimerRunnable); - Message msg = Message.obtain(); - msg.what = 0; - mOsdHandler.sendMessage(msg); - + mHandler.removeCallbacks(mTimerRunnable); + mOsdTs = -1; + mOSDSemaphore.release(); exportMp4ToGallery(getApplicationContext(), mNextVideoAbsolutePath); broadcastVideoFile(true, mNextVideoAbsolutePath); mHandler.postDelayed(new Runnable() { diff --git a/app/src/main/java/com/xypower/mpapp/v2/Camera2VideoActivityNew.java b/app/src/main/java/com/xypower/mpapp/v2/Camera2VideoActivityNew.java new file mode 100644 index 00000000..d5339f01 --- /dev/null +++ b/app/src/main/java/com/xypower/mpapp/v2/Camera2VideoActivityNew.java @@ -0,0 +1,916 @@ +package com.xypower.mpapp.v2; + +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.net.Uri; +import android.opengl.GLException; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.MediaStore; +import android.text.TextUtils; +import android.util.Log; +import android.view.Window; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import android.widget.FrameLayout; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.xypower.gpuv.camerarecorder.CameraRecordListener; +import com.xypower.gpuv.camerarecorder.GPUCameraRecorder; +import com.xypower.gpuv.camerarecorder.GPUCameraRecorderBuilder; +import com.xypower.gpuv.egl.filter.GlWatermarkFilter; +import com.xypower.gpuvideoandroid.widget.AutoFitGLView; +import com.xypower.mpapp.MicroPhotoService; +import com.xypower.mpapp.R; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.opengles.GL10; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.IntBuffer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Timer; +import java.util.concurrent.Semaphore; + +public class Camera2VideoActivityNew extends AppCompatActivity { + + public static final String ACTION_FINISH = "com.xypower.mvapp.ACT_FINISH"; + public static final String ACTION_MP_VIDEO_FINISHED = "com.xypower.mpapp.ACT_V_FINISHED"; + + private static final int DEFAULT_FONT_SIZE = 20; + private AutoFitGLView mPreviewView; + protected GPUCameraRecorder mGPUCameraRecorder; + // protected LensFacing lensFacing = LensFacing.BACK; + protected int mCameraWidth = 1280; + protected int mCameraHeight = 720; + protected int mVideoWidth = 1280; + protected int mVideoHeight = 720; + + private int mCameraId; + private long mVideoId = 0; + private int mDuration = 0; + + private int mOrientation = -1; + + private String mNextVideoAbsolutePath; + + private String mOSDLeftTop = null; + private String mOSDRightTop = null; + private String mOSDRightBottom = null; + private String mOSDLeftBottom = null; + + private int mOSDMargin = 0; + private Paint mPaint; + private Paint mPaintStroker; + + private List mBitmaps = new ArrayList<>(); + + private GlWatermarkFilter mOSDFilter = null; + private Object mBitmapLocker = new Object(); + + private SimpleDateFormat mDateFormater; + // SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss a"); + + private final String TIME_MICRO_TS = "$$TS$$"; + private final String TIME_MICRO_DT = "$$DATETIME$$"; + + private int mTimeMask = 0; + private int mStatusBarHeight = -1; + + private static class OSD_ITEM + { + String text; + int mask; + Point origin; + Rect previousRect; + } + + private List mOSDItems = new ArrayList<>(); + + private final static int TIME_MASK_LT_TS = 1; + private final static int TIME_MASK_LT_DT = 2; + private final static int TIME_MASK_LT_ML = 4; + private final static int TIME_MASK_LT = TIME_MASK_LT_TS | TIME_MASK_LT_DT | TIME_MASK_LT_ML; + private final static int TIME_MASK_RT_TS = 8; + private final static int TIME_MASK_RT_DT = 16; + private final static int TIME_MASK_RT_ML = 32; + private final static int TIME_MASK_RT = TIME_MASK_RT_TS | TIME_MASK_RT_DT | TIME_MASK_RT_ML; + private final static int TIME_MASK_RB_TS = 64; + private final static int TIME_MASK_RB_DT = 128; + private final static int TIME_MASK_RB_ML = 256; + private final static int TIME_MASK_RB = TIME_MASK_RB_TS | TIME_MASK_RB_DT | TIME_MASK_RB_ML; + private final static int TIME_MASK_LB_TS = 512; + private final static int TIME_MASK_LB_DT = 1024; + private final static int TIME_MASK_LB_ML = 2048; + private final static int TIME_MASK_LB = TIME_MASK_LB_TS | TIME_MASK_LB_DT | TIME_MASK_LB_ML; + + private Handler mHandler = null; + + private class TimerRunner implements Runnable { + private Bitmap mBitmap; + + TimerRunner(Bitmap bm) { + mBitmap = bm; + } + + @Override + public void run() { + + Bitmap oldBm = null; + if (mBitmap != null) { + oldBm = mOSDFilter.updateBitmap(mBitmap); + } + if (oldBm != null) { + synchronized (mBitmapLocker) { + mBitmaps.add(oldBm); + } + } + + long ts = System.currentTimeMillis(); + long ms = ts % 1000; + + Log.d("OSD", "Cur TS=" + Long.toString(ts / 1000) + " Timer=" + Long.toString(1000 - ms)); + + Message msg = Message.obtain(); + msg.what = 1; + msg.obj = new Long(ts - ms + 1000); + mOsdHandler.sendMessage(msg); + } + } + + private Handler mOsdHandler; + private Thread mOsdThread = new Thread(new Runnable() { + @Override + public void run() { + Looper.prepare(); + + mOsdHandler = new Handler(Looper.myLooper()) { + public void handleMessage(Message msg) { + if (msg.what == 1) { + Long targetTs = (Long)msg.obj; + long ts = System.currentTimeMillis(); + if (ts > targetTs.longValue()) { + return; + } + Bitmap bm = null; + synchronized (mBitmapLocker) { + if (!mBitmaps.isEmpty()) { + bm = mBitmaps.remove(0); + } + } + if (bm != null) { + updateOSD(bm, targetTs.longValue()); + + TimerRunner runner = new TimerRunner(bm); + mHandler.postDelayed(runner, targetTs.longValue() - ts); + } + } else if (msg.what == 0) { + Looper.myLooper().quitSafely(); + } + } + }; + + Looper.loop(); + } + }); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + + Window win = getWindow(); + win.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + + setContentView(R.layout.activity_camera2_video); + + getSupportActionBar().hide(); + + // mStatusBarHeight = getStatusBarHeight(this); + onCreateActivity(); + + getWindow().getDecorView().setOnApplyWindowInsetsListener((v, insets) -> { + mStatusBarHeight = px2dip(Camera2VideoActivityNew.this, insets.getStableInsetTop()); + + return insets; + }); + } + + public static int px2dip(Context context, float pxValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (pxValue / scale + 0.5f); + } + + public int getStatusBarHeight(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); + WindowInsets windowInsets = windowMetrics.getWindowInsets(); + Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.statusBars() | WindowInsets.Type.displayCutout()); + return px2dip(this, insets.top); + } + + Rect frame = new Rect(); + getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); + int statusBarHeight = frame.top; + + if (statusBarHeight == 0) { + int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + statusBarHeight = getResources().getDimensionPixelSize(resourceId); + } + } + + return px2dip(context, statusBarHeight); + } + + protected void onCreateActivity() { + + Intent intent = getIntent(); + + mCameraId = intent.getIntExtra("cameraId", 0); + mVideoId = intent.getLongExtra("videoId", 0); + mDuration = intent.getIntExtra("duration", 0); + mVideoWidth = intent.getIntExtra("width", 0); + mVideoHeight = intent.getIntExtra("height", 0); + mOrientation = intent.getIntExtra("orientation", -1); + mOSDLeftTop = intent.getStringExtra("leftTopOsd"); + mOSDLeftBottom = intent.getStringExtra("leftBottomOsd"); + mOSDRightBottom = intent.getStringExtra("rightBottomOsd"); + mOSDRightTop = intent.getStringExtra("rightTopOsd"); + mOSDMargin = px2dip(this, intent.getIntExtra("margin", 12)); + + mCameraWidth = mVideoWidth; + mCameraHeight = mVideoHeight; + + mTimeMask = 0; + if (!TextUtils.isEmpty(mOSDLeftTop)) { + mOSDLeftTop = mOSDLeftTop.replace("\r\n", "\n"); + mOSDLeftTop = mOSDLeftTop.replace("\n\r", "\n"); + mOSDLeftTop = mOSDLeftTop.replace("\r", "\n"); + if (mOSDLeftTop.indexOf(TIME_MICRO_TS) != 0) { + mTimeMask |= TIME_MASK_LT_TS; + } + if (mOSDLeftTop.indexOf(TIME_MICRO_DT) != 0) { + mTimeMask |= TIME_MASK_LT_DT; + } + if (mOSDLeftTop.indexOf("\n") != 0) { + mTimeMask |= TIME_MASK_LT_ML; + } + } + if (!TextUtils.isEmpty(mOSDRightTop)) { + mOSDRightTop = mOSDRightTop.replace("\r\n", "\n"); + mOSDRightTop = mOSDRightTop.replace("\n\r", "\n"); + mOSDRightTop = mOSDRightTop.replace("\r", "\n"); + if (mOSDRightTop.indexOf(TIME_MICRO_TS) != 0) { + mTimeMask |= TIME_MASK_RT_TS; + } + if (mOSDRightTop.indexOf(TIME_MICRO_DT) != 0) { + mTimeMask |= TIME_MASK_RT_DT; + } + if (mOSDRightTop.indexOf("\n") != 0) { + mTimeMask |= TIME_MASK_RT_ML; + } + } + if (!TextUtils.isEmpty(mOSDRightBottom)) { + mOSDRightBottom = mOSDRightBottom.replace("\r\n", "\n"); + mOSDRightBottom = mOSDRightBottom.replace("\n\r", "\n"); + mOSDRightBottom = mOSDRightBottom.replace("\r", "\n"); + if (mOSDRightBottom.indexOf(TIME_MICRO_TS) != 0) { + mTimeMask |= TIME_MASK_RB_TS; + } + if (mOSDRightBottom.indexOf(TIME_MICRO_DT) != 0) { + mTimeMask |= TIME_MASK_RB_DT; + } + if (mOSDRightBottom.indexOf("\n") != 0) { + mTimeMask |= TIME_MASK_RB_ML; + } + } + + if (!TextUtils.isEmpty(mOSDLeftBottom)) { + mOSDLeftBottom = mOSDLeftBottom.replace("\r\n", "\n"); + mOSDLeftBottom = mOSDLeftBottom.replace("\n\r", "\n"); + mOSDLeftBottom = mOSDLeftBottom.replace("\r", "\n"); + if (mOSDLeftBottom.indexOf(TIME_MICRO_TS) != 0) { + mTimeMask |= TIME_MASK_LB_TS; + } + if (mOSDLeftBottom.indexOf(TIME_MICRO_DT) != 0) { + mTimeMask |= TIME_MASK_LB_DT; + } + if (mOSDLeftBottom.indexOf("\n") != 0) { + mTimeMask |= TIME_MASK_LB_ML; + } + } + + mHandler = new Handler(); + + mOsdThread.start(); + + long ts = 0; + long zeroTs = 0; + + Bitmap bm2 = null; + if (!TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop)) { + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setStyle(Paint.Style.FILL); + mPaint.setColor(Color.WHITE); + int fontSize = DEFAULT_FONT_SIZE; + mPaint.setTextSize(fontSize); + + mPaintStroker = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaintStroker.setStyle(Paint.Style.STROKE); + mPaintStroker.setColor(Color.BLACK); + mPaintStroker.setTextSize(fontSize); + mPaintStroker.setStrokeWidth(1); + + bm2 = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.ARGB_8888); + + Bitmap bm = Bitmap.createBitmap(mVideoWidth, mVideoHeight, Bitmap.Config.ARGB_8888); + + Canvas canvas = new Canvas(bm); + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + + ts = System.currentTimeMillis(); + zeroTs = ts - (ts % 1000); + initOSD(bm, zeroTs); + mOSDFilter = new GlWatermarkFilter(bm); + + if (mGPUCameraRecorder != null) { + mGPUCameraRecorder.setFilter(mOSDFilter); + } + + updateOSD(bm2, zeroTs + 1000); + } + + final long prevZeroTs = zeroTs; + final Bitmap finalBm = bm2; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mNextVideoAbsolutePath = getVideoFilePath(); + + mGPUCameraRecorder.start(mNextVideoAbsolutePath); + + if (mOSDFilter != null) { + long ts2 = System.currentTimeMillis(); + long zeroTs2 = ts2 - (ts2 % 1000); + + if (zeroTs2 > prevZeroTs) { + // Next second + Bitmap oldBm = mOSDFilter.updateBitmap(finalBm); + if (oldBm != null) { + synchronized (mBitmapLocker) { + mBitmaps.add(oldBm); + } + } + Message msg = Message.obtain(); + msg.what = 1; + msg.obj = new Long(zeroTs2 + 1000); + // mOsdTs = zeroTs2 + 1000; + mOsdHandler.sendMessage(msg); + } else { + TimerRunner runner = new TimerRunner(finalBm); + mHandler.postDelayed(runner, 1000 - (ts2 - zeroTs2)); + } + Log.d("OSD", "Cur TS=" + Long.toString(ts2 / 1000) + " Timer=" + Long.toString(1000 - (ts2 - zeroTs2))); + } + } + }, 0); + + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mGPUCameraRecorder.stop(); + } + }, 16 + mDuration * 1000); + + // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + @Override + protected void onResume() { + super.onResume(); + setUpCamera(); + } + + @Override + protected void onStop() { + super.onStop(); + releaseCamera(); + } + + private void initOSD(Bitmap bm, long ts) { + + Log.d("OSD", "INIT OSD " + Long.toString(ts / 1000)); + + if (mStatusBarHeight == -1) { + mStatusBarHeight = getStatusBarHeight(this); + } + int statusHeight = mStatusBarHeight; + + int bmWidth = bm.getWidth(); + int bmHeight = bm.getHeight(); + int margin = mOSDMargin; + int x = 0; + int y = 0; + + Canvas canvas = new Canvas(bm); + Rect textBounds = new Rect(); + + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + + if (!TextUtils.isEmpty(mOSDLeftTop)) { + String[] items = mOSDLeftTop.split("\n"); + Point origin = new Point(margin, margin + statusHeight); + + for (String item : items) { + + int mask = 0; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_LT_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_LT_DT; + } + + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.mask = mask; + osdItem.origin = new Point(origin); + + if (mask == 0) { + canvas.drawText(item, origin.x, origin.y, mPaint); + canvas.drawText(item, origin.x, origin.y, mPaintStroker); + + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); + } else { + String newText = updateOSDTime(item, ts); + + canvas.drawText(newText, origin.x, origin.y, mPaint); + canvas.drawText(newText, origin.x, origin.y, mPaintStroker); + + mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); + } + + osdItem.previousRect = new Rect(origin.x, origin.y, origin.x + textBounds.width(), origin.y + textBounds.height()); + + mOSDItems.add(osdItem); + origin.y += (textBounds.height() * 3) >> 1; + } + } + + if (!TextUtils.isEmpty(mOSDLeftBottom)) { + + String[] items = mOSDLeftBottom.split("\n"); + Point origin = new Point(margin, bmHeight - margin); + + for(int idx = items.length-1; idx >= 0; idx--) { + + int mask = 0; + String item = items[idx]; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_LB_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_LB_DT; + } + + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.mask = mask; + osdItem.origin = new Point(origin); + if (mask == 0) { + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); + y = origin.y - textBounds.height(); + + canvas.drawText(item, origin.x, y, mPaint); + canvas.drawText(item, origin.x, y, mPaintStroker); + } else { + String newText = updateOSDTime(item, ts); + + mPaintStroker.getTextBounds(newText, 0, newText.length(), textBounds); + y = origin.y - textBounds.height(); + + canvas.drawText(newText, origin.x, y, mPaint); + canvas.drawText(newText, origin.x, y, mPaintStroker); + } + + osdItem.previousRect = new Rect(origin.x, y, origin.x + textBounds.width(), y + textBounds.height()); + mOSDItems.add(osdItem); + origin.y -= (textBounds.height() * 3) >> 1; + } + } + + if (!TextUtils.isEmpty(mOSDRightTop)) { + + String[] items = mOSDRightTop.split("\n"); + Point origin = new Point(bmWidth - margin, margin + statusHeight); + + for (String item : items) { + + int mask = 0; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_RT_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_RT_DT; + } + + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.origin = new Point(origin); + osdItem.mask = mask; + + if (mask == 0) { + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); + + canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaint); + canvas.drawText(item, origin.x - textBounds.width(), origin.y, mPaintStroker); + } else { + + String newText = updateOSDTime(item, ts); + mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); + + canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaint); + canvas.drawText(newText, origin.x - textBounds.width(), origin.y, mPaintStroker); + } + osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y, origin.x, origin.y + textBounds.height()); + mOSDItems.add(osdItem); + + origin.y += (textBounds.height() * 3) >> 1; + } + } + + if (!TextUtils.isEmpty(mOSDRightBottom)) { + + String[] items = mOSDRightBottom.split("\n"); + Point origin = new Point(bmWidth - margin, bmHeight - margin); + + for(int idx = items.length-1; idx >= 0; idx--) { + + int mask = 0; + String item = items[idx]; + if (item.indexOf(TIME_MICRO_TS) != 0) { + mask |= TIME_MASK_RB_TS; + } + if (item.indexOf(TIME_MICRO_DT) != 0) { + mask |= TIME_MASK_RB_DT; + } + + OSD_ITEM osdItem = new OSD_ITEM(); + osdItem.text = item; + osdItem.origin = new Point(origin); + osdItem.mask = mask; + if (mask == 0) { + mPaintStroker.getTextBounds(item, 0, item.length(), textBounds); + + canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint); + canvas.drawText(item, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker); + } else { + String newText = updateOSDTime(item, ts); + mPaintStroker.getTextBounds(newText, 0, item.length(), textBounds); + + canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaint); + canvas.drawText(newText, origin.x - textBounds.width(), origin.y - textBounds.height(), mPaintStroker); + } + + osdItem.previousRect = new Rect(origin.x - textBounds.width(), origin.y - textBounds.height(), origin.x, origin.y); + mOSDItems.add(osdItem); + + origin.y -= (textBounds.height() * 3) >> 1; + } + } + + } + + private void updateOSD(Bitmap bm, long ts) { + + Log.d("OSD", "prepareOSD " + Long.toString(ts / 1000)); + if (mStatusBarHeight == -1) { + mStatusBarHeight = getStatusBarHeight(this); + } + int statusHeight = mStatusBarHeight; + + Canvas canvas = new Canvas(bm); + boolean aa = canvas.isHardwareAccelerated(); + Rect textBounds = new Rect(); + + canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + + for (OSD_ITEM osdItem : mOSDItems) { + String text = updateOSDTime(osdItem.text, ts); + + int x = osdItem.previousRect.left; + int y = osdItem.previousRect.top; + + if ((osdItem.mask & TIME_MASK_LT) != 0) { + mPaintStroker.getTextBounds(text, 0, text.length(), textBounds); + + // Log.d("OSD", "UPD OSD x=" + x + " y=" + y); + canvas.drawText(text, x, y, mPaint); + canvas.drawText(text, x, y, mPaintStroker); + osdItem.previousRect.set(x, y, x + textBounds.width(), y + textBounds.height()); + + } else if ((osdItem.mask & TIME_MASK_LB) != 0) { + mPaintStroker.getTextBounds(text, 0, text.length(), textBounds); + + y = osdItem.origin.y - textBounds.height(); + canvas.drawText(text, x, y, mPaint); + canvas.drawText(text, x, y, mPaintStroker); + osdItem.previousRect.set(x, y, x + textBounds.width(), y + textBounds.height()); + + } else if ((osdItem.mask & TIME_MASK_RT) != 0) { + mPaintStroker.getTextBounds(text, 0, text.length(), textBounds); + + x = osdItem.origin.x - textBounds.width(); + canvas.drawText(text, x, osdItem.origin.y, mPaint); + canvas.drawText(text, x, osdItem.origin.y, mPaintStroker); + osdItem.previousRect.set(x, osdItem.origin.y, x + textBounds.width(), osdItem.origin.y + textBounds.height()); + + } else if ((osdItem.mask & TIME_MASK_RB) != 0) { + mPaintStroker.getTextBounds(text, 0, text.length(), textBounds); + + x = osdItem.origin.x - textBounds.width(); + y = osdItem.origin.y - textBounds.height(); + canvas.drawText(text, x, y, mPaint); + canvas.drawText(text, x, y, mPaintStroker); + osdItem.previousRect.set(x, y, x + textBounds.width(), y + textBounds.height()); + } else { + canvas.drawText(text, x, y, mPaint); + canvas.drawText(text, x, y, mPaintStroker); + } + } + + } + + private String updateOSDTime(String osd, long ts) { + String newOSD = osd; + if (newOSD.indexOf(TIME_MICRO_TS) != -1) { + newOSD = newOSD.replace(TIME_MICRO_TS, Long.toString(ts / 1000)); + } + if (newOSD.indexOf(TIME_MICRO_DT) != -1) { + if (mDateFormater == null) { + mDateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + Date dt = new Date(ts); + newOSD = newOSD.replace(TIME_MICRO_DT, mDateFormater.format(dt)); + } + + return newOSD; + } + + private void releaseCamera() { + if (mPreviewView != null) { + mPreviewView.onPause(); + } + + if (mGPUCameraRecorder != null) { + mGPUCameraRecorder.stop(); + mGPUCameraRecorder.release(); + mGPUCameraRecorder = null; + } + + if (mPreviewView != null) { + ((FrameLayout) findViewById(R.id.wrap_view)).removeView(mPreviewView); + mPreviewView = null; + } + } + + private void setUpCameraView() { + + runOnUiThread(() -> { + + FrameLayout frameLayout = findViewById(R.id.wrap_view); + frameLayout.removeAllViews(); + mPreviewView = null; + mPreviewView = new AutoFitGLView(getApplicationContext()); + mPreviewView.setTouchListener((event, width, height) -> { + if (mGPUCameraRecorder == null) return; + mGPUCameraRecorder.changeManualFocusPoint(event.getX(), event.getY(), width, height); + }); + + frameLayout.addView(mPreviewView); + }); + } + + private void setUpCamera() { + setUpCameraView(); + + if (mNextVideoAbsolutePath == null || mNextVideoAbsolutePath.isEmpty()) { + mNextVideoAbsolutePath = getVideoFilePath(this); + } + + mGPUCameraRecorder = new GPUCameraRecorderBuilder(this, mPreviewView) + //.recordNoFilter(true) + .cameraRecordListener(new CameraRecordListener() { + @Override + public void onGetFlashSupport(boolean flashSupport) { + + } + + @Override + public void onRecordComplete() { + // mHandler.removeCallbacks(mTimerRunnable); + Message msg = Message.obtain(); + msg.what = 0; + mOsdHandler.sendMessage(msg); + + exportMp4ToGallery(getApplicationContext(), mNextVideoAbsolutePath); + broadcastVideoFile(true, mNextVideoAbsolutePath); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Camera2VideoActivityNew.this.finish(); + } + }, 500); + } + + @Override + public void onRecordStart() { + } + + @Override + public void onError(Exception exception) { + Log.e("GPUCameraRecorder", exception.toString()); + broadcastVideoFile(false, mNextVideoAbsolutePath); + } + + @Override + public void onCameraThreadFinish() { + } + + @Override + public void onVideoFileReady() { + } + }) + .videoSize(mVideoWidth, mVideoHeight) + .cameraSize(mCameraWidth, mCameraHeight) + .cameraId(Integer.toString(mCameraId)) + .build(); + + if (mOSDFilter != null) { + mGPUCameraRecorder.setFilter(mOSDFilter); + } + } + +// private void changeFilter(Filters filters) { +// GPUCameraRecorder.setFilter(Filters.getFilterInstance(filters, getApplicationContext())); +// } + + + private interface BitmapReadyCallbacks { + void onBitmapReady(Bitmap bitmap); + } + + private void captureBitmap(final BitmapReadyCallbacks bitmapReadyCallbacks) { + mPreviewView.queueEvent(() -> { + EGL10 egl = (EGL10) EGLContext.getEGL(); + GL10 gl = (GL10) egl.eglGetCurrentContext().getGL(); + Bitmap snapshotBitmap = createBitmapFromGLSurface(mPreviewView.getMeasuredWidth(), mPreviewView.getMeasuredHeight(), gl); + + runOnUiThread(() -> { + bitmapReadyCallbacks.onBitmapReady(snapshotBitmap); + }); + }); + } + + private void broadcastVideoFile(boolean result, String path) { + if (mDuration <= 0) { + return; + } + + Context context = getApplicationContext(); + String receiverName = MicroPhotoService.AlarmReceiver.class.getName(); + String packageName = context.getPackageName(); + + Intent intent = new Intent(ACTION_MP_VIDEO_FINISHED); + // intent.setPackage(packageName); + intent.putExtra("result", result); + intent.putExtra("path", path); + intent.putExtra("videoId", mVideoId); + + // intent.setComponent(new ComponentName(packageName, receiverName)); + + // Log.i(TAG, "Notify recording videoId=" + Long.toString(mVideoId) + " " + path); + LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(getApplicationContext()); + localBroadcastManager.sendBroadcast(intent); + + context.sendBroadcast(intent); + } + + private String getVideoFilePath(Context context) { + // final File dir = context.getExternalFilesDir(null); + String path = Environment.getExternalStorageDirectory().getAbsolutePath(); + if (!path.endsWith(File.separator)) { + path += File.separator; + } + path += context.getPackageName() + File.separator; + File file = new File(path); + if (!file.exists()) { + file.mkdirs(); + } + path += System.currentTimeMillis() + ".mp4"; + return path; + } + + private Bitmap createBitmapFromGLSurface(int w, int h, GL10 gl) { + + int bitmapBuffer[] = new int[w * h]; + int bitmapSource[] = new int[w * h]; + IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer); + intBuffer.position(0); + + try { + gl.glReadPixels(0, 0, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, intBuffer); + int offset1, offset2, texturePixel, blue, red, pixel; + for (int i = 0; i < h; i++) { + offset1 = i * w; + offset2 = (h - i - 1) * w; + for (int j = 0; j < w; j++) { + texturePixel = bitmapBuffer[offset1 + j]; + blue = (texturePixel >> 16) & 0xff; + red = (texturePixel << 16) & 0x00ff0000; + pixel = (texturePixel & 0xff00ff00) | red | blue; + bitmapSource[offset2 + j] = pixel; + } + } + } catch (GLException e) { + Log.e("CreateBitmap", "createBitmapFromGLSurface: " + e.getMessage(), e); + return null; + } + + return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888); + } + + public void saveAsPngImage(Bitmap bitmap, String filePath) { + try { + File file = new File(filePath); + FileOutputStream outStream = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream); + outStream.close(); + + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void exportMp4ToGallery(Context context, String filePath) { + final ContentValues values = new ContentValues(2); + values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); + values.put(MediaStore.Video.Media.DATA, filePath); + context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, + values); + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, + Uri.parse("file://" + filePath))); + } + + public static String getVideoFilePath() { + return getAndroidMoviesFolder().getAbsolutePath() + "/" + new SimpleDateFormat("yyyyMM_dd-HHmmss").format(new Date()) + "GPUCameraRecorder.mp4"; + } + + public static File getAndroidMoviesFolder() { + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES); + } + + private static void exportPngToGallery(Context context, String filePath) { + Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + File f = new File(filePath); + Uri contentUri = Uri.fromFile(f); + mediaScanIntent.setData(contentUri); + context.sendBroadcast(mediaScanIntent); + } + + public static String getImageFilePath() { + return getAndroidImageFolder().getAbsolutePath() + "/" + new SimpleDateFormat("yyyyMM_dd-HHmmss").format(new Date()) + "GPUCameraRecorder.png"; + } + + public static File getAndroidImageFolder() { + return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + } + +}