From 8d31aa730de984087a22454c2c59dcbb5f7f274c Mon Sep 17 00:00:00 2001 From: bangdc90 <65581036+bangdc90@users.noreply.github.com> Date: Wed, 25 Sep 2024 22:47:57 +0700 Subject: [PATCH] 1. Add VR feature. Screen will be splitted to 2 when mode is On. (#10) 2. Add record button to OSD, now user can move that button to main screen to interact --- .../com/openipc/pixelpilot/VideoActivity.java | 186 +++++++++++++++++- .../openipc/pixelpilot/osd/MovableLayout.java | 9 + .../openipc/pixelpilot/osd/OSDManager.java | 1 + app/src/main/res/drawable/record.png | Bin 0 -> 9619 bytes app/src/main/res/drawable/recording.png | Bin 0 -> 10344 bytes app/src/main/res/layout/activity_video.xml | 57 ++++++ app/videonative/src/main/cpp/VideoDecoder.cpp | 86 ++++---- app/videonative/src/main/cpp/VideoDecoder.h | 16 +- app/videonative/src/main/cpp/VideoPlayer.cpp | 15 +- app/videonative/src/main/cpp/VideoPlayer.h | 2 +- .../com/openipc/videonative/VideoPlayer.java | 58 +++--- 11 files changed, 345 insertions(+), 85 deletions(-) create mode 100644 app/src/main/res/drawable/record.png create mode 100644 app/src/main/res/drawable/recording.png diff --git a/app/src/main/java/com/openipc/pixelpilot/VideoActivity.java b/app/src/main/java/com/openipc/pixelpilot/VideoActivity.java index 9136845..5642d4f 100644 --- a/app/src/main/java/com/openipc/pixelpilot/VideoActivity.java +++ b/app/src/main/java/com/openipc/pixelpilot/VideoActivity.java @@ -21,15 +21,19 @@ import android.util.Base64; import android.util.Log; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.SubMenu; import android.view.View; import android.view.WindowManager; import android.widget.PopupMenu; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.constraintlayout.widget.ConstraintSet; import androidx.documentfile.provider.DocumentFile; import com.github.mikephil.charting.charts.PieChart; @@ -87,6 +91,24 @@ public void run() { private OSDManager osdManager; private ParcelFileDescriptor dvrFd = null; private Timer dvrIconTimer = null; + private Timer recordTimer = null; + private int seconds = 0; + private boolean isVRMode = false; + private boolean isStreaming = false; + private ConstraintLayout constraintLayout; + private ConstraintSet constraintSet; + + public boolean getVRSetting() { + return getSharedPreferences("general", Context.MODE_PRIVATE).getBoolean("vr-mode", false); + } + + public void setVRSetting(boolean v) + { + SharedPreferences prefs = getSharedPreferences("general", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean("vr-mode", v); + editor.apply(); + } public static int getChannel(Context context) { return context.getSharedPreferences("general", @@ -119,6 +141,19 @@ public static String bytesToHex(byte[] bytes) { return hexString.toString(); } + private void resetApp() + { + // Restart the app + Intent intent = getPackageManager().getLaunchIntentForPackage(getPackageName()); + if (intent != null) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + finish(); + System.exit(0); // Ensure the app is fully restarted + } + } + + @SuppressLint("ClickableViewAccessibility") @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "lifecycle onCreate"); @@ -139,7 +174,84 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(binding.getRoot()); videoPlayer = new VideoPlayer(this); videoPlayer.setIVideoParamsChanged(this); - binding.mainVideo.getHolder().addCallback(videoPlayer.configure1()); + isVRMode = getVRSetting(); + if(isVRMode) { + binding.mainVideo.setVisibility(View.GONE); + binding.surfaceViewLeft.getHolder().addCallback(videoPlayer.configure1(0)); + binding.surfaceViewRight.getHolder().addCallback(videoPlayer.configure1(1)); + + SeekBar seekBar = binding.seekBar; + // Retrieve saved progress value + SharedPreferences sharedPreferences = getSharedPreferences("SeekBarPrefs", MODE_PRIVATE); + int savedProgress = sharedPreferences.getInt("seekBarProgress", 0); // Default to 0 if no value is found + seekBar.setProgress(savedProgress); + seekBar.setVisibility(View.VISIBLE); + constraintLayout = binding.frameLayout; + constraintSet = new ConstraintSet(); + constraintSet.clone(constraintLayout); + + // Apply the saved margin + int margin = savedProgress * 10; // Adjust the multiplier as needed + constraintSet.setMargin(R.id.surfaceViewLeft, ConstraintSet.END, margin); + constraintSet.setMargin(R.id.surfaceViewRight, ConstraintSet.START, margin); + constraintSet.applyTo(constraintLayout); + + // Hide SeekBar after 3 seconds + handler.postDelayed(new Runnable() { + @Override + public void run() { + seekBar.setVisibility(View.GONE); + } + }, 3000); + + // Show SeekBar when touched + constraintLayout.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + seekBar.setVisibility(View.VISIBLE); + // Hide SeekBar again after 3 seconds of inactivity + handler.postDelayed(new Runnable() { + @Override + public void run() { + seekBar.setVisibility(View.GONE); + } + }, 3000); + } + return false; + } + }); + + seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + int margin = progress * 10; // Adjust the multiplier as needed + constraintSet.setMargin(R.id.surfaceViewLeft, ConstraintSet.END, margin); + constraintSet.setMargin(R.id.surfaceViewRight, ConstraintSet.START, margin); + constraintSet.applyTo(constraintLayout); + // Save progress value + SharedPreferences sharedPreferences = getSharedPreferences("SeekBarPrefs", MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putInt("seekBarProgress", progress); + editor.apply(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + }); + } + else { + binding.surfaceViewRight.setVisibility(View.GONE); + binding.surfaceViewLeft.setVisibility(View.GONE); + binding.mainVideo.getHolder().addCallback(videoPlayer.configure1(0)); + } osdManager = new OSDManager(this, binding); osdManager.setUp(); @@ -160,8 +272,37 @@ protected void onCreate(Bundle savedInstanceState) { PieData noData = new PieData(new PieDataSet(new ArrayList<>(), "")); chart.setData(noData); + binding.imgBtnRecord.setOnClickListener(item -> { + if(!isStreaming) return; + + if (dvrFd == null) { + Uri dvrUri = openDvrFile(); + if (dvrUri != null) { + startDvr(dvrUri); + } else { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + intent.addCategory(Intent.CATEGORY_DEFAULT); + startActivityForResult(intent, PICK_DVR_REQUEST_CODE); + } + } else { + stopDvr(); + } + }); + binding.btnSettings.setOnClickListener(v -> { PopupMenu popup = new PopupMenu(this, v); + SubMenu vrMenu = popup.getMenu().addSubMenu("VR mode"); + MenuItem vrItem = vrMenu.add(getVRSetting() ? "On" : "Off"); + vrItem.setOnMenuItemClickListener(item -> { + isVRMode = !getVRSetting(); + setVRSetting(isVRMode); + vrItem.setTitle(isVRMode ? "On" : "Off"); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); + item.setActionView(new View(this)); + resetApp(); + return false; + }); + SubMenu chnMenu = popup.getMenu().addSubMenu("Channel"); int channelPref = getChannel(this); chnMenu.setHeaderTitle("Current: " + channelPref); @@ -211,6 +352,7 @@ protected void onCreate(Bundle savedInstanceState) { SubMenu recording = popup.getMenu().addSubMenu("Recording"); MenuItem dvrBtn = recording.add(dvrFd == null ? "Start" : "Stop"); dvrBtn.setOnMenuItemClickListener(item -> { + if(!isStreaming) return false; if (dvrFd == null) { Uri dvrUri = openDvrFile(); if (dvrUri != null) { @@ -285,6 +427,7 @@ private Uri openDvrFile() { String dvrFolder = getSharedPreferences("general", Context.MODE_PRIVATE).getString("dvr_folder_", ""); if (dvrFolder.isEmpty()) { + Log.e(TAG, "dvrFolder is empty"); return null; } Uri uri = Uri.parse(dvrFolder); @@ -297,6 +440,8 @@ private Uri openDvrFile() { String filename = "pixelpilot_" + formattedNow + ".mp4"; DocumentFile newFile = pickedDir.createFile("video/mp4", filename); Toast.makeText(this, "Recording to " + filename, Toast.LENGTH_SHORT).show(); + if(newFile == null) + Log.e(TAG, "dvr newFile null"); return newFile != null ? newFile.getUri() : null; } return null; @@ -309,10 +454,26 @@ private void startDvr(Uri dvrUri) { try { dvrFd = getContentResolver().openFileDescriptor(dvrUri, "rw"); videoPlayer.startDvr(dvrFd.getFd(), getDvrMP4()); + binding.imgBtnRecord.setImageResource(R.drawable.recording); } catch (IOException e) { Log.e(TAG, "Failed to open dvr file ", e); dvrFd = null; } + + binding.txtRecordLabel.setVisibility(View.VISIBLE); + recordTimer = new Timer(); + recordTimer.schedule(new TimerTask() { + @Override + public void run() { + int minutes = seconds / 60; + int secs = seconds % 60; + + String timeFormatted = String.format("%02d:%02d", minutes, secs); + runOnUiThread(() -> binding.txtRecordLabel.setText(timeFormatted)); + seconds++; + } + }, 0, 1000); + dvrIconTimer = new Timer(); dvrIconTimer.schedule(new TimerTask() { @Override @@ -328,10 +489,20 @@ private void stopDvr() { return; } binding.imgRecIndicator.setVisibility(View.INVISIBLE); + binding.imgBtnRecord.setImageResource(R.drawable.record); videoPlayer.stopDvr(); - dvrIconTimer.cancel(); - dvrIconTimer.purge(); - dvrIconTimer = null; + if(recordTimer != null) { + recordTimer.cancel(); + recordTimer.purge(); + recordTimer = null; + seconds = 0; + binding.txtRecordLabel.setVisibility(View.GONE); + } + if(dvrIconTimer != null) { + dvrIconTimer.cancel(); + dvrIconTimer.purge(); + dvrIconTimer = null; + } try { dvrFd.close(); } catch (IOException e) { @@ -511,9 +682,6 @@ public void onDecodingInfoChanged(final DecodingInfo decodingInfo) { runOnUiThread(() -> { if (lastCodec != decodingInfo.nCodec) { lastCodec = decodingInfo.nCodec; - videoPlayer.stopAndRemoveReceiverDecoder(); - videoPlayer.addAndStartDecoderReceiver(binding.mainVideo.getHolder().getSurface()); - videoPlayer.start(); } if (decodingInfo.currentFPS > 0) { binding.tvMessage.setVisibility(View.GONE); @@ -574,9 +742,13 @@ public void onWfbNgStatsChanged(WfbNGStats data) { paddedDigits(data.count_p_dec_ok, 6), paddedDigits(data.count_p_fec_recovered, 6), paddedDigits(data.count_p_lost, 6))); + isStreaming = true; } } else { binding.tvLinkStatus.setText("No wfb-ng data."); + isStreaming = false; + binding.imgBtnRecord.setImageResource(R.drawable.record); + stopDvr(); } }); } diff --git a/app/src/main/java/com/openipc/pixelpilot/osd/MovableLayout.java b/app/src/main/java/com/openipc/pixelpilot/osd/MovableLayout.java index 7d49e0a..74b8846 100644 --- a/app/src/main/java/com/openipc/pixelpilot/osd/MovableLayout.java +++ b/app/src/main/java/com/openipc/pixelpilot/osd/MovableLayout.java @@ -42,6 +42,15 @@ private void init(Context context) { defaultY = (float) displaySize.y / 2 - ((float) displaySize.y / 4); } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + // Intercept touch events and pass them to onTouchEvent + if (isMovable) { + return true; + } + return false; + } + @Override public boolean onTouchEvent(MotionEvent event) { if (!isMovable) { diff --git a/app/src/main/java/com/openipc/pixelpilot/osd/OSDManager.java b/app/src/main/java/com/openipc/pixelpilot/osd/OSDManager.java index 9e78f3a..719feee 100644 --- a/app/src/main/java/com/openipc/pixelpilot/osd/OSDManager.java +++ b/app/src/main/java/com/openipc/pixelpilot/osd/OSDManager.java @@ -91,6 +91,7 @@ public void onFinish() { listOSDItems.add(new OSDElement("Pitch", binding.itemPitch)); listOSDItems.add(new OSDElement("RC Link", binding.itemRCLink)); listOSDItems.add(new OSDElement("Recording Indicator", binding.itemRecIndicator)); + listOSDItems.add(new OSDElement("Recording Button", binding.btnRecord)); listOSDItems.add(new OSDElement("Roll", binding.itemRoll)); listOSDItems.add(new OSDElement("Satellites", binding.itemSat)); listOSDItems.add(new OSDElement("Status", binding.itemStatus)); diff --git a/app/src/main/res/drawable/record.png b/app/src/main/res/drawable/record.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc447cbce24fd7eec01bd388e7557b470b5de1a GIT binary patch literal 9619 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 WwyI`k?hE*!3SwA0S>4^qO#EN%@z*{8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/recording.png b/app/src/main/res/drawable/recording.png new file mode 100644 index 0000000000000000000000000000000000000000..19a202a5782e5b119c2ccea96277b968f0744731 GIT binary patch literal 10344 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

>85Qy3C&RQ-n&Q zmWF9XvYtD3a61ydbN457NEt7dV^^h6xHsrYi_#9@sRUoKCUH+w-5mGP#q9%sWbt_* zuff{SOAlUj!V#qtWxu+b^UtE+H+L9M@1H{xpt1^=~CeprX_(fCKp%qcT-qW9l04LE_#A!~nOSEB7WzOAp>IsX~j_ z_a}FYKlxDE>Tl$)?l0}{ZhxD6Sh6Ag+S9#5Sg489(m&p7zBxl7R<43~#q@ngLS1b4 zYyTXsarkxTZZj6o!so9S{Q8}SMB?#D-juy3>{1g8Nu%GCh literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_video.xml b/app/src/main/res/layout/activity_video.xml index 79ee930..a0e3a09 100644 --- a/app/src/main/res/layout/activity_video.xml +++ b/app/src/main/res/layout/activity_video.xml @@ -15,6 +15,38 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + +