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.mpapp.v2.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; public class Camera2VideoActivity 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 = 32; private static final float DEFAULT_STROKE_WIDTH = 0.8f; 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; protected int mPreviewWidth = 1280; protected int mPreviewHeight = 720; private int mCameraId; private long mVideoId = 0; private long 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 GlWatermarkFilter mOSDFilter = null; 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 Thread mOsdThread = new Thread(new Runnable() { @Override public void run() { try { long ts = System.currentTimeMillis(); Bitmap bm = Bitmap.createBitmap(mPreviewView.getMeasuredWidth(), mPreviewView.getMeasuredHeight(), Bitmap.Config.ARGB_8888); Bitmap oldBm = null; Canvas canvas = new Canvas(bm); long zeroTs = ts - (ts % 1000); long nextTs = zeroTs + 1000; while (true) { try { if (nextTs > ts) { Log.i("OSD", "Sleep " + Long.toString(nextTs - ts)); Thread.sleep(nextTs - ts); } } catch (InterruptedException ex){ break; } catch(Exception ex){ } canvas.setBitmap(bm); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); updateOSD(bm, nextTs); oldBm = mOSDFilter.updateBitmap(bm); bm = oldBm; ts = System.currentTimeMillis(); zeroTs = ts - (ts % 1000); if (zeroTs + 1000 > nextTs) { nextTs = zeroTs + 1000; } } } catch (Exception ex) { } } }); @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(Camera2VideoActivity.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; AspectRatioFrameLayout frameLayout = (AspectRatioFrameLayout)findViewById(R.id.wrap_view); frameLayout.setAspectRatio((float)mVideoWidth / (float)mVideoHeight); mTimeMask = 0; if (!TextUtils.isEmpty(mOSDLeftTop)) { 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)) { 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)) { 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)) { 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(); // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } @Override protected void onResume() { super.onResume(); mHandler.postDelayed(new Runnable() { @Override public void run() { setUpCamera(); mHandler.postDelayed(new Runnable() { @Override public void run() { mNextVideoAbsolutePath = getVideoFilePath(); mGPUCameraRecorder.start(mNextVideoAbsolutePath); } }, 0); } }, 100); } @Override protected void onStop() { super.onStop(); releaseCamera(); } private void initOSD(Bitmap bm, long ts) { Log.i("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.i("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(() -> { AspectRatioFrameLayout frameLayout = (AspectRatioFrameLayout)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); if (!TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop) || !TextUtils.isEmpty(mOSDLeftTop)) { mPreviewWidth = frameLayout.getMeasuredWidth(); mPreviewHeight = frameLayout.getMeasuredHeight(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.FILL); mPaint.setColor(Color.WHITE); int fontSize = DEFAULT_FONT_SIZE * mPreviewHeight / 1024; float strokeWidth = DEFAULT_STROKE_WIDTH * mPreviewHeight / 1024; mPaint.setTextSize(fontSize); mPaintStroker = new Paint(Paint.ANTI_ALIAS_FLAG); mPaintStroker.setStyle(Paint.Style.STROKE); mPaintStroker.setColor(Color.BLACK); mPaintStroker.setTextSize(fontSize); mPaintStroker.setStrokeWidth(strokeWidth); Bitmap bm = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bm); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); long ts = System.currentTimeMillis(); long zeroTs = ts - (ts % 1000); initOSD(bm, zeroTs); mOSDFilter = new GlWatermarkFilter(bm); if (mGPUCameraRecorder != null) { mGPUCameraRecorder.setFilter(mOSDFilter); } mOsdThread.start(); } }); } 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); } @Override public void onRecordStart() { Log.i("OSD", "Record Start "); mHandler.postDelayed(new Runnable() { @Override public void run() { Log.i("OSD", "Record Stop " + Long.toString(mDuration)); mGPUCameraRecorder.stop(); int aa = 0; } }, 1200L + mDuration * 1000); } @Override public void onError(Exception exception) { Log.e("GPUCameraRecorder", exception.toString()); mOsdThread.interrupt(); broadcastVideoFile(false, mNextVideoAbsolutePath); } @Override public void onCameraThreadFinish() { } @Override public void onVideoFileReady() { // exportMp4ToGallery(getApplicationContext(), mNextVideoAbsolutePath); mOsdThread.interrupt(); broadcastVideoFile(true, mNextVideoAbsolutePath); mHandler.postDelayed(new Runnable() { @Override public void run() { Camera2VideoActivity.this.finish(); } }, 1000); } @Override public void onDurationArrived() { } }) .videoSize(mVideoWidth, mVideoHeight) .cameraSize(mCameraWidth, mCameraHeight) .cameraId(Integer.toString(mCameraId)) .mute(true) .duration(mDuration * 1000) .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); } }