From 518a325583b511b6533b26c7437bfa1c05a31817 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sun, 23 May 2021 00:33:19 +0200 Subject: [PATCH 01/16] WIP refacto --- .../com/fpvout/digiview/H264Extractor.java | 23 +-- .../InputStreamBufferedDataSource.java | 7 +- .../digiview/InputStreamDataSource.java | 7 +- .../com/fpvout/digiview/MainActivity.java | 167 ++++++++--------- .../java/com/fpvout/digiview/OverlayView.java | 2 + .../fpvout/digiview/PerformancePreset.java | 24 +-- .../digiview/UsbDeviceBroadcastReceiver.java | 5 +- .../fpvout/digiview/UsbMaskConnection.java | 43 ++++- .../fpvout/digiview/VideoReaderExoplayer.java | 171 ++++++++++-------- .../main/java/usb/AndroidUSBInputStream.java | 47 +---- .../main/java/usb/AndroidUSBOutputStream.java | 73 +------- 11 files changed, 251 insertions(+), 318 deletions(-) diff --git a/app/src/main/java/com/fpvout/digiview/H264Extractor.java b/app/src/main/java/com/fpvout/digiview/H264Extractor.java index f59a02c..d48597e 100644 --- a/app/src/main/java/com/fpvout/digiview/H264Extractor.java +++ b/app/src/main/java/com/fpvout/digiview/H264Extractor.java @@ -1,17 +1,16 @@ package com.fpvout.digiview; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorOutput; -import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.ts.H264Reader; +import com.google.android.exoplayer2.extractor.ts.SeiReader; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader; +import com.google.android.exoplayer2.util.NonNullApi; import com.google.android.exoplayer2.util.ParsableByteArray; -import com.google.android.exoplayer2.extractor.ts.SeiReader; import java.io.IOException; import java.util.ArrayList; @@ -21,9 +20,6 @@ * Extracts data from H264 bitstreams. */ public final class H264Extractor implements Extractor { - /** Factory for {@link H264Extractor} instances. */ - public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new H264Extractor()}; - private static int MAX_SYNC_FRAME_SIZE = 131072; private long firstSampleTimestampUs; @@ -33,33 +29,27 @@ public final class H264Extractor implements Extractor { private boolean startedPacket; - public H264Extractor() { - this(0); - } - public H264Extractor(int mMaxSyncFrameSize, int mSampleTime) { this(0, mMaxSyncFrameSize, mSampleTime); } - public H264Extractor(long firstSampleTimestampUs) { - this(firstSampleTimestampUs, MAX_SYNC_FRAME_SIZE, (int) sampleTime); - } - public H264Extractor(long firstSampleTimestampUs, int mMaxSyncFrameSize, int mSampleTime) { MAX_SYNC_FRAME_SIZE = mMaxSyncFrameSize; sampleTime = mSampleTime; this.firstSampleTimestampUs = firstSampleTimestampUs; - reader = new H264Reader(new SeiReader(new ArrayList()),false,true); + reader = new H264Reader(new SeiReader(new ArrayList<>()), false, true); sampleData = new ParsableByteArray(MAX_SYNC_FRAME_SIZE); } // Extractor implementation. @Override - public boolean sniff(ExtractorInput input) throws IOException { + @NonNullApi + public boolean sniff(ExtractorInput input) { return true; } @Override + @NonNullApi public void init(ExtractorOutput output) { reader.createTracks(output, new TsPayloadReader.TrackIdGenerator(0, 1)); output.endTracks(); @@ -78,6 +68,7 @@ public void release() { } @Override + @NonNullApi public int read(ExtractorInput input, PositionHolder seekPosition) throws IOException { int bytesRead = input.read(sampleData.getData(), 0, MAX_SYNC_FRAME_SIZE); if (bytesRead == C.RESULT_END_OF_INPUT) { diff --git a/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java b/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java index 7ba7af9..11fb41d 100644 --- a/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java +++ b/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java @@ -1,6 +1,5 @@ package com.fpvout.digiview; -import android.content.Context; import android.net.Uri; import com.google.android.exoplayer2.C; @@ -19,8 +18,7 @@ public class InputStreamBufferedDataSource implements DataSource { private static final String ERROR_THREAD_NOT_INITIALIZED = "Read thread not initialized, call first 'startReadThread()'"; private static final long READ_TIMEOUT = 200; - private Context context; - private DataSpec dataSpec; + private final DataSpec dataSpec; private InputStream inputStream; private long bytesRemaining; private boolean opened; @@ -30,8 +28,7 @@ public class InputStreamBufferedDataSource implements DataSource { private boolean working; - public InputStreamBufferedDataSource(Context context, DataSpec dataSpec, InputStream inputStream) { - this.context = context; + public InputStreamBufferedDataSource(DataSpec dataSpec, InputStream inputStream) { this.dataSpec = dataSpec; this.inputStream = inputStream; startReadThread(); diff --git a/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java b/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java index 7b10606..1e5f6ad 100644 --- a/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java +++ b/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java @@ -1,6 +1,5 @@ package com.fpvout.digiview; -import android.content.Context; import android.net.Uri; import com.google.android.exoplayer2.C; @@ -13,14 +12,12 @@ import java.io.InputStream; public class InputStreamDataSource implements DataSource { - private Context context; - private DataSpec dataSpec; + private final DataSpec dataSpec; private InputStream inputStream; private long bytesRemaining; private boolean opened; - public InputStreamDataSource(Context context, DataSpec dataSpec, InputStream inputStream) { - this.context = context; + public InputStreamDataSource(DataSpec dataSpec, InputStream inputStream) { this.dataSpec = dataSpec; this.inputStream = inputStream; } diff --git a/app/src/main/java/com/fpvout/digiview/MainActivity.java b/app/src/main/java/com/fpvout/digiview/MainActivity.java index f471c71..31bc346 100644 --- a/app/src/main/java/com/fpvout/digiview/MainActivity.java +++ b/app/src/main/java/com/fpvout/digiview/MainActivity.java @@ -3,7 +3,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.LayoutTransition; -import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -25,24 +24,20 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.preference.PreferenceManager; -import java.util.HashMap; - import io.sentry.SentryLevel; import io.sentry.android.core.SentryAndroid; +import static com.fpvout.digiview.UsbMaskConnection.ACTION_USB_PERMISSION; +import static com.fpvout.digiview.VideoReaderExoplayer.VideoReaderEventMessageCode; import static com.fpvout.digiview.VideoReaderExoplayer.VideoZoomedIn; public class MainActivity extends AppCompatActivity implements UsbDeviceListener { - private static final String ACTION_USB_PERMISSION = "com.fpvout.digiview.USB_PERMISSION"; private static final String TAG = "DIGIVIEW"; - private static final int VENDOR_ID = 11427; - private static final int PRODUCT_ID = 31; - private int shortAnimationDuration; + private final int shortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); private float buttonAlpha = 1; private View settingsButton; private View watermarkView; private OverlayView overlayView; - PendingIntent permissionIntent; UsbDeviceBroadcastReceiver usbDeviceBroadcastReceiver; UsbManager usbManager; UsbDevice usbDevice; @@ -65,63 +60,66 @@ protected void onCreate(Bundle savedInstanceState) { checkDataCollectionAgreement(); // Hide top bar and status bar - View decorView = getWindow().getDecorView(); - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN); - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.hide(); - } + setFullscreen(); // Prevent screen from sleeping getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); - permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); + // Register app for auto launch usbDeviceBroadcastReceiver = new UsbDeviceBroadcastReceiver(this); - IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(usbDeviceBroadcastReceiver, filter); IntentFilter filterDetached = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED); registerReceiver(usbDeviceBroadcastReceiver, filterDetached); - shortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); watermarkView = findViewById(R.id.watermarkView); overlayView = findViewById(R.id.overlayView); fpvView = findViewById(R.id.fpvView); - settingsButton = findViewById(R.id.settingsButton); - settingsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(v.getContext(), SettingsActivity.class); - v.getContext().startActivity(intent); - } - }); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + // Enable resizing animations ((ViewGroup) findViewById(R.id.mainLayout)).getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); setupGestureDetectors(); - mUsbMaskConnection = new UsbMaskConnection(); - mVideoReader = new VideoReaderExoplayer(fpvView, overlayView, this); + settingsButton.setOnClickListener(v -> { + Intent intent = new Intent(v.getContext(), SettingsActivity.class); + v.getContext().startActivity(intent); + }); + Handler videoReaderEventListener = new Handler(this.getMainLooper(), msg -> onVideoReaderEvent((VideoReaderEventMessageCode) msg.obj)); + mVideoReader = new VideoReaderExoplayer(fpvView, this, videoReaderEventListener); + + mUsbMaskConnection = new UsbMaskConnection(); if (!usbConnected) { - if (searchDevice()) { + usbDevice = UsbMaskConnection.searchDevice(usbManager, getApplicationContext()); + if (usbDevice != null) { + Log.i(TAG, "USB - usbDevice attached"); + showOverlay(R.string.usb_device_found, OverlayStatus.Connected); connect(); } else { - showOverlay(R.string.waiting_for_usb_device, OverlayStatus.Connected); + showOverlay(R.string.waiting_for_usb_device, OverlayStatus.Disconnected); } } } + private void setFullscreen() { + View decorView = getWindow().getDecorView(); + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.hide(); + } + } + private void setupGestureDetectors() { gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { @Override @@ -181,7 +179,7 @@ private void updateVideoZoom() { private void cancelButtonAnimation() { Handler handler = settingsButton.getHandler(); if (handler != null) { - settingsButton.getHandler().removeCallbacksAndMessages(null); + handler.removeCallbacksAndMessages(null); } } @@ -221,17 +219,41 @@ private void autoHideSettingsButton() { if (overlayView.getVisibility() == View.VISIBLE) return; if (buttonAlpha == 0) return; - settingsButton.postDelayed(new Runnable() { - @Override - public void run() { - buttonAlpha = 0; - settingsButton.animate() - .alpha(0) - .setDuration(shortAnimationDuration); - } + settingsButton.postDelayed(() -> { + buttonAlpha = 0; + settingsButton.animate() + .alpha(0) + .setDuration(shortAnimationDuration); }, 3000); } + private boolean onVideoReaderEvent(VideoReaderEventMessageCode m) { + if (VideoReaderEventMessageCode.WAITING_FOR_VIDEO.equals(m)) { + Log.d(TAG, "event: WAITING_FOR_VIDEO"); + showOverlay(R.string.waiting_for_video, OverlayStatus.Connected); + } else if (VideoReaderEventMessageCode.VIDEO_PLAYING.equals(m)) { + Log.d(TAG, "event: VIDEO_PLAYING"); + hideOverlay(); + } + return false; // false to continue listening + } + + private void showOverlay(int textId, OverlayStatus connected) { + overlayView.show(textId, connected); + updateWatermark(); + autoHideSettingsButton(); + updateVideoZoom(); + + } + + private void hideOverlay() { + overlayView.hide(); + updateWatermark(); + autoHideSettingsButton(); + updateVideoZoom(); + } + + @Override public void usbDeviceApproved(UsbDevice device) { Log.i(TAG, "USB - usbDevice approved"); @@ -247,41 +269,13 @@ public void usbDeviceDetached() { this.onStop(); } - private boolean searchDevice() { - HashMap deviceList = usbManager.getDeviceList(); - if (deviceList.size() <= 0) { - usbDevice = null; - return false; - } - - for (UsbDevice device : deviceList.values()) { - if (device.getVendorId() == VENDOR_ID && device.getProductId() == PRODUCT_ID) { - if (usbManager.hasPermission(device)) { - Log.i(TAG, "USB - usbDevice attached"); - showOverlay(R.string.usb_device_found, OverlayStatus.Connected); - usbDevice = device; - return true; - } - - usbManager.requestPermission(device, permissionIntent); - } - } - - return false; - } private void connect() { usbConnected = true; - mUsbMaskConnection.setUsbDevice(usbManager.openDevice(usbDevice), usbDevice); + mUsbMaskConnection.setUsbDevice(usbManager, usbDevice); mVideoReader.setUsbMaskConnection(mUsbMaskConnection); overlayView.hide(); mVideoReader.start(); - updateWatermark(); - autoHideSettingsButton(); - } - - private void showOverlay() { - } @Override @@ -289,37 +283,20 @@ public void onResume() { super.onResume(); Log.d(TAG, "APP - On Resume"); - View decorView = getWindow().getDecorView(); - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN); - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.hide(); - } + setFullscreen(); if (!usbConnected) { - if (searchDevice()) { + usbDevice = UsbMaskConnection.searchDevice(usbManager, getApplicationContext()); + if (usbDevice != null) { Log.d(TAG, "APP - On Resume usbDevice device found"); + showOverlay(R.string.usb_device_found, OverlayStatus.Connected); connect(); } else { - showOverlay(R.string.waiting_for_usb_device, OverlayStatus.Connected); + showOverlay(R.string.waiting_for_usb_device, OverlayStatus.Disconnected); } } settingsButton.setAlpha(1); - autoHideSettingsButton(); - updateWatermark(); - updateVideoZoom(); - } - - private void showOverlay(int textId, OverlayStatus connected) { - overlayView.show(textId, connected); - updateWatermark(); - showSettingsButton(); } @Override diff --git a/app/src/main/java/com/fpvout/digiview/OverlayView.java b/app/src/main/java/com/fpvout/digiview/OverlayView.java index b1327c8..8a6c736 100644 --- a/app/src/main/java/com/fpvout/digiview/OverlayView.java +++ b/app/src/main/java/com/fpvout/digiview/OverlayView.java @@ -38,6 +38,8 @@ private void showInfo(String text, OverlayStatus status){ int image = R.drawable.ic_goggles_white; switch(status){ + case Connected: + break; case Disconnected: image = R.drawable.ic_goggles_disconnected_white; break; diff --git a/app/src/main/java/com/fpvout/digiview/PerformancePreset.java b/app/src/main/java/com/fpvout/digiview/PerformancePreset.java index 17b2a8a..cdb8a27 100644 --- a/app/src/main/java/com/fpvout/digiview/PerformancePreset.java +++ b/app/src/main/java/com/fpvout/digiview/PerformancePreset.java @@ -1,19 +1,17 @@ package com.fpvout.digiview; -public class PerformancePreset { - int h264ReaderMaxSyncFrameSize = 131072; - int h264ReaderSampleTime = 10000; - int exoPlayerMinBufferMs = 500; - int exoPlayerMaxBufferMs = 2000; - int exoPlayerBufferForPlaybackMs = 17; - int exoPlayerBufferForPlaybackAfterRebufferMs = 17; - DataSourceType dataSourceType = DataSourceType.INPUT_STREAM; - - private PerformancePreset(){ +import androidx.annotation.NonNull; - } +public class PerformancePreset { + int h264ReaderMaxSyncFrameSize; + int h264ReaderSampleTime; + int exoPlayerMinBufferMs; + int exoPlayerMaxBufferMs; + int exoPlayerBufferForPlaybackMs; + int exoPlayerBufferForPlaybackAfterRebufferMs; + DataSourceType dataSourceType; - private PerformancePreset(int mH264ReaderMaxSyncFrameSize, int mH264ReaderSampleTime, int mExoPlayerMinBufferMs, int mExoPlayerMaxBufferMs, int mExoPlayerBufferForPlaybackMs, int mExoPlayerBufferForPlaybackAfterRebufferMs, DataSourceType mDataSourceType){ + private PerformancePreset(int mH264ReaderMaxSyncFrameSize, int mH264ReaderSampleTime, int mExoPlayerMinBufferMs, int mExoPlayerMaxBufferMs, int mExoPlayerBufferForPlaybackMs, int mExoPlayerBufferForPlaybackAfterRebufferMs, DataSourceType mDataSourceType) { h264ReaderMaxSyncFrameSize = mH264ReaderMaxSyncFrameSize; h264ReaderSampleTime = mH264ReaderSampleTime; exoPlayerMinBufferMs = mExoPlayerMinBufferMs; @@ -63,7 +61,9 @@ static PerformancePreset getPreset(String p) { } } + @Override + @NonNull public String toString() { return "PerformancePreset{" + "h264ReaderMaxSyncFrameSize=" + h264ReaderMaxSyncFrameSize + diff --git a/app/src/main/java/com/fpvout/digiview/UsbDeviceBroadcastReceiver.java b/app/src/main/java/com/fpvout/digiview/UsbDeviceBroadcastReceiver.java index db64b09..e2eebc9 100644 --- a/app/src/main/java/com/fpvout/digiview/UsbDeviceBroadcastReceiver.java +++ b/app/src/main/java/com/fpvout/digiview/UsbDeviceBroadcastReceiver.java @@ -6,11 +6,12 @@ import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; +import static com.fpvout.digiview.UsbMaskConnection.ACTION_USB_PERMISSION; + public class UsbDeviceBroadcastReceiver extends BroadcastReceiver { - private static final String ACTION_USB_PERMISSION = "com.fpvout.digiview.USB_PERMISSION"; private final UsbDeviceListener listener; - public UsbDeviceBroadcastReceiver(UsbDeviceListener listener ){ + public UsbDeviceBroadcastReceiver(UsbDeviceListener listener) { this.listener = listener; } diff --git a/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java b/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java index 1d6d90e..b841bd9 100644 --- a/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java +++ b/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java @@ -1,19 +1,26 @@ package com.fpvout.digiview; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; import java.io.IOException; +import java.util.HashMap; import usb.AndroidUSBInputStream; import usb.AndroidUSBOutputStream; public class UsbMaskConnection { + public static final String ACTION_USB_PERMISSION = "com.fpvout.digiview.USB_PERMISSION"; + private static final int VENDOR_ID = 11427; + private static final int PRODUCT_ID = 31; private final byte[] magicPacket = "RMVT".getBytes(); private UsbDeviceConnection usbConnection; - private UsbDevice device; private UsbInterface usbInterface; AndroidUSBInputStream mInputStream; AndroidUSBOutputStream mOutputStream; @@ -22,16 +29,23 @@ public class UsbMaskConnection { public UsbMaskConnection() { } - public void setUsbDevice(UsbDeviceConnection c, UsbDevice d) { - usbConnection = c; - device = d; - usbInterface = device.getInterface(3); + public static UsbDevice searchDevice(UsbManager usbManager, Context c) { + PendingIntent permissionIntent = PendingIntent.getBroadcast(c, 0, new Intent(ACTION_USB_PERMISSION), 0); - usbConnection.claimInterface(usbInterface,true); + HashMap deviceList = usbManager.getDeviceList(); + if (deviceList.size() <= 0) { + return null; + } - mOutputStream = new AndroidUSBOutputStream(usbInterface.getEndpoint(0), usbConnection); - mInputStream = new AndroidUSBInputStream(usbInterface.getEndpoint(1), usbInterface.getEndpoint(0), usbConnection); - ready = true; + for (UsbDevice device : deviceList.values()) { + if (device.getVendorId() == VENDOR_ID && device.getProductId() == PRODUCT_ID) { + if (usbManager.hasPermission(device)) { + return device; + } + usbManager.requestPermission(device, permissionIntent); + } + } + return null; } public void start(){ @@ -59,4 +73,15 @@ public void stop() { public boolean isReady() { return ready; } + + public void setUsbDevice(UsbManager usbManager, UsbDevice d) { + usbConnection = usbManager.openDevice(d); + usbInterface = d.getInterface(3); + + usbConnection.claimInterface(usbInterface, true); + + mOutputStream = new AndroidUSBOutputStream(usbInterface.getEndpoint(0), usbConnection); + mInputStream = new AndroidUSBInputStream(usbInterface.getEndpoint(1), usbInterface.getEndpoint(0), usbConnection); + ready = true; + } } diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index 586b1e3..b897577 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -5,6 +5,7 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.util.Log; import android.view.SurfaceView; @@ -25,62 +26,58 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.util.NonNullApi; import com.google.android.exoplayer2.video.VideoListener; import usb.AndroidUSBInputStream; public class VideoReaderExoplayer { - private static final String TAG = "DIGIVIEW"; - private SimpleExoPlayer mPlayer; + private static final String TAG = "DIGIVIEW"; + private Handler videoReaderEventListener; + private SimpleExoPlayer mPlayer; + private UsbMaskConnection mUsbMaskConnection; static final String VideoPreset = "VideoPreset"; - private final OverlayView overlayView; private final SurfaceView surfaceView; private AndroidUSBInputStream inputStream; - private UsbMaskConnection mUsbMaskConnection; + + VideoReaderExoplayer(SurfaceView videoSurface, Context c) { + surfaceView = videoSurface; + context = c; + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(c); + } + private boolean zoomedIn; private final Context context; private PerformancePreset performancePreset = PerformancePreset.getPreset(PerformancePreset.PresetType.DEFAULT); static final String VideoZoomedIn = "VideoZoomedIn"; private final SharedPreferences sharedPreferences; - VideoReaderExoplayer(SurfaceView videoSurface, OverlayView overlayView, Context c) { - surfaceView = videoSurface; - this.overlayView = overlayView; - context = c; - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(c); - } - - VideoReaderExoplayer(SurfaceView videoSurface, OverlayView overlayView, Context c, PerformancePreset p) { - this(videoSurface,overlayView,c); - performancePreset = p; - } + VideoReaderExoplayer(SurfaceView videoSurface, Context c, Handler v) { + this(videoSurface, c); + videoReaderEventListener = v; + } - public void setUsbMaskConnection(UsbMaskConnection connection) { - mUsbMaskConnection = connection; - inputStream = mUsbMaskConnection.mInputStream; - } + public void start() { + zoomedIn = sharedPreferences.getBoolean(VideoZoomedIn, true); + performancePreset = PerformancePreset.getPreset(sharedPreferences.getString(VideoPreset, "default")); - public void start() { - zoomedIn = sharedPreferences.getBoolean(VideoZoomedIn, true); - performancePreset = PerformancePreset.getPreset(sharedPreferences.getString(VideoPreset, "default")); + DefaultLoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(performancePreset.exoPlayerMinBufferMs, performancePreset.exoPlayerMaxBufferMs, performancePreset.exoPlayerBufferForPlaybackMs, performancePreset.exoPlayerBufferForPlaybackAfterRebufferMs).build(); + mPlayer = new SimpleExoPlayer.Builder(context).setLoadControl(loadControl).build(); + mPlayer.setVideoSurfaceView(surfaceView); + mPlayer.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); + mPlayer.setWakeMode(C.WAKE_MODE_LOCAL); - DefaultLoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(performancePreset.exoPlayerMinBufferMs, performancePreset.exoPlayerMaxBufferMs, performancePreset.exoPlayerBufferForPlaybackMs, performancePreset.exoPlayerBufferForPlaybackAfterRebufferMs).build(); - mPlayer = new SimpleExoPlayer.Builder(context).setLoadControl(loadControl).build(); - mPlayer.setVideoSurfaceView(surfaceView); - mPlayer.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING); - mPlayer.setWakeMode(C.WAKE_MODE_LOCAL); + DataSpec dataSpec = new DataSpec(Uri.EMPTY, 0, C.LENGTH_UNSET); - DataSpec dataSpec = new DataSpec(Uri.EMPTY, 0, C.LENGTH_UNSET); + Log.d(TAG, "preset: " + performancePreset); - Log.d(TAG, "preset: " + performancePreset); - - DataSource.Factory dataSourceFactory = () -> { - switch (performancePreset.dataSourceType){ - case INPUT_STREAM: - return (DataSource) new InputStreamDataSource(context, dataSpec, inputStream); + DataSource.Factory dataSourceFactory = () -> { + switch (performancePreset.dataSourceType) { + case INPUT_STREAM: + return (DataSource) new InputStreamDataSource(dataSpec, inputStream); case BUFFERED_INPUT_STREAM: default: - return (DataSource) new InputStreamBufferedDataSource(context, dataSpec, inputStream); + return (DataSource) new InputStreamBufferedDataSource(dataSpec, inputStream); } }; @@ -92,37 +89,48 @@ public void start() { mPlayer.play(); mPlayer.addListener(new ExoPlayer.EventListener() { @Override + @NonNullApi public void onPlayerError(ExoPlaybackException error) { switch (error.type) { case ExoPlaybackException.TYPE_SOURCE: Log.e(TAG, "PLAYER_SOURCE - TYPE_SOURCE: " + error.getSourceException().getMessage()); - overlayView.show(R.string.waiting_for_video, OverlayStatus.Error); - // TODO: let MainActivity know so it can hide watermark/show settings button - (new Handler(Looper.getMainLooper())).postDelayed(() -> { - restart(); - }, 1000); + (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); + break; + case ExoPlaybackException.TYPE_REMOTE: + Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getSourceException().getMessage()); + break; + case ExoPlaybackException.TYPE_RENDERER: + Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getSourceException().getMessage()); + break; + case ExoPlaybackException.TYPE_UNEXPECTED: + Log.e(TAG, "PLAYER_SOURCE - TYPE_UNEXPECTED: " + error.getSourceException().getMessage()); break; } } @Override - public void onPlaybackStateChanged(int state) { - if (state == Player.STATE_ENDED) { - Log.d(TAG, "PLAYER_STATE - ENDED"); - overlayView.show(R.string.waiting_for_video, OverlayStatus.Connected); - // TODO: let MainActivity know so it can hide watermark/show settings button - (new Handler(Looper.getMainLooper())).postDelayed(() -> { - restart(); - }, 1000); - - }else if(state == Player.STATE_READY){ - overlayView.hide(); - // TODO: let MainActivity know so it can show watermark/auto-hide settings button + public void onPlaybackStateChanged(@NonNullApi int state) { + switch (state) { + case Player.STATE_IDLE: + case Player.STATE_READY: + case Player.STATE_BUFFERING: + break; + case Player.STATE_ENDED: + Log.d(TAG, "PLAYER_STATE - ENDED"); + sendEvent(VideoReaderEventMessageCode.WAITING_FOR_VIDEO); // let MainActivity know so it can hide watermark/show settings button + (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); + break; } } }); mPlayer.addVideoListener(new VideoListener() { + @Override + public void onRenderedFirstFrame() { + Log.d(TAG, "PLAYER_RENDER - FIRST FRAME"); + sendEvent(VideoReaderEventMessageCode.VIDEO_PLAYING); // let MainActivity know so it can hide watermark/show settings button + } + @Override public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { if (!zoomedIn) { @@ -132,23 +140,36 @@ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegre } } }); + } + + public void setUsbMaskConnection(UsbMaskConnection connection) { + mUsbMaskConnection = connection; + inputStream = mUsbMaskConnection.mInputStream; + } + + private void sendEvent(VideoReaderEventMessageCode eventCode) { + if (videoReaderEventListener != null) { // let MainActivity know so it can hide watermark/show settings button + Message videoReaderEventMessage = new Message(); + videoReaderEventMessage.obj = eventCode; + videoReaderEventListener.sendMessage(videoReaderEventMessage); } + } - public void toggleZoom() { - zoomedIn = !zoomedIn; + public void toggleZoom() { + zoomedIn = !zoomedIn; - SharedPreferences.Editor preferencesEditor = sharedPreferences.edit(); - preferencesEditor.putBoolean(VideoZoomedIn, zoomedIn); - preferencesEditor.apply(); + SharedPreferences.Editor preferencesEditor = sharedPreferences.edit(); + preferencesEditor.putBoolean(VideoZoomedIn, zoomedIn); + preferencesEditor.apply(); - ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) surfaceView.getLayoutParams(); + ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) surfaceView.getLayoutParams(); - if (zoomedIn) { - params.dimensionRatio = ""; - } else { - if (mPlayer == null) return; - Format videoFormat = mPlayer.getVideoFormat(); - if (videoFormat == null) return; + if (zoomedIn) { + params.dimensionRatio = ""; + } else { + if (mPlayer == null) return; + Format videoFormat = mPlayer.getVideoFormat(); + if (videoFormat == null) return; params.dimensionRatio = videoFormat.width + ":" + videoFormat.height; } @@ -168,17 +189,19 @@ public void zoomOut() { } } - public void restart() { - mPlayer.release(); + public void restart() { + mPlayer.release(); - if (mUsbMaskConnection.isReady()) { - mUsbMaskConnection.start(); - start(); - } + if (mUsbMaskConnection.isReady()) { + mUsbMaskConnection.start(); + start(); } + } - public void stop() { - if (mPlayer != null) - mPlayer.release(); - } + public void stop() { + if (mPlayer != null) + mPlayer.release(); + } + + public enum VideoReaderEventMessageCode {WAITING_FOR_VIDEO, VIDEO_PLAYING} } diff --git a/app/src/main/java/usb/AndroidUSBInputStream.java b/app/src/main/java/usb/AndroidUSBInputStream.java index 84fdd24..52ca056 100644 --- a/app/src/main/java/usb/AndroidUSBInputStream.java +++ b/app/src/main/java/usb/AndroidUSBInputStream.java @@ -1,54 +1,27 @@ -/* - * Copyright 2019, Digi International Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at http://mozilla.org/MPL/2.0/. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ package usb; -import java.io.IOException; -import java.io.InputStream; - import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.util.Log; -/** - * This class acts as a wrapper to read data from the USB Interface in Android - * behaving like an {@code InputputStream} class. - */ +import java.io.InputStream; + public class AndroidUSBInputStream extends InputStream { private final String TAG = "USBInputStream"; - // Constants. - private static final int OFFSET = 0; private static final int READ_TIMEOUT = 100; - // Variables. - private UsbDeviceConnection usbConnection; - - private UsbEndpoint receiveEndPoint; + private final UsbDeviceConnection usbConnection; + private final UsbEndpoint receiveEndPoint; private final UsbEndpoint sendEndPoint; - private boolean working = false; - - /** * Class constructor. Instantiates a new {@code AndroidUSBInputStream} * object with the given parameters. * * @param readEndpoint The USB end point to use to read data from. - * @param connection The USB connection to use to read data from. - * + * @param sendEndpoint The USB end point to use to sent data to. + * @param connection The USB connection to use to read data from. * @see UsbDeviceConnection * @see UsbEndpoint */ @@ -59,13 +32,13 @@ public AndroidUSBInputStream( UsbEndpoint readEndpoint, UsbEndpoint sendEndpoint } @Override - public int read() throws IOException { + public int read() { byte[] buffer = new byte[131072]; - return read(buffer, 0, buffer.length); + return read(buffer, 0, buffer.length); } @Override - public int read(byte[] buffer, int offset, int length) throws IOException { + public int read(byte[] buffer, int offset, int length) { int receivedBytes = usbConnection.bulkTransfer(receiveEndPoint, buffer, buffer.length, READ_TIMEOUT); if (receivedBytes <= 0) { // send magic packet again; Would be great to handle this in UsbMaskConnection directly... @@ -78,6 +51,6 @@ public int read(byte[] buffer, int offset, int length) throws IOException { @Override - public void close() throws IOException {} + public void close(){} } diff --git a/app/src/main/java/usb/AndroidUSBOutputStream.java b/app/src/main/java/usb/AndroidUSBOutputStream.java index 627a4e5..b994761 100644 --- a/app/src/main/java/usb/AndroidUSBOutputStream.java +++ b/app/src/main/java/usb/AndroidUSBOutputStream.java @@ -1,29 +1,12 @@ -/* - * Copyright 2019, Digi International Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at http://mozilla.org/MPL/2.0/. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ package usb; -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.util.Log; +import java.io.IOException; +import java.io.OutputStream; + /** * This class acts as a wrapper to write data to the USB Interface in Android * behaving like an {@code OutputStream} class. @@ -32,33 +15,24 @@ public class AndroidUSBOutputStream extends OutputStream { // Constants. private static final int WRITE_TIMEOUT = 2000; - - // Variables. - private UsbDeviceConnection usbConnection; - private UsbEndpoint sendEndPoint; + private final UsbDeviceConnection usbConnection; + private final UsbEndpoint sendEndPoint; - private LinkedBlockingQueue writeQueue; - - private boolean streamOpen = true; + private final boolean streamOpen = true; /** * Class constructor. Instantiates a new {@code AndroidUSBOutputStream} * object with the given parameters. - * + * * @param writeEndpoint The USB end point to use to write data to. - * @param connection The USB connection to use to write data to. - * + * @param connection The USB connection to use to write data to. * @see UsbDeviceConnection * @see UsbEndpoint */ public AndroidUSBOutputStream(UsbEndpoint writeEndpoint, UsbDeviceConnection connection) { this.usbConnection = connection; this.sendEndPoint = writeEndpoint; - - writeQueue = new LinkedBlockingQueue<>(512); - DataWriter dataWriter = new DataWriter(); - dataWriter.start(); } /* @@ -85,39 +59,12 @@ public void write(byte[] buffer) { */ @Override public void write(byte[] buffer, int offset, int count) { - final byte[] finalData = new byte[count + offset]; - System.arraycopy(buffer, offset, finalData, 0, count); - try { - writeQueue.add(finalData); - } catch (IllegalStateException e) { - Log.e("USBOutputStream","Could not add data, write queue is full: " + e.getMessage(), e); - } - } - - /** - * Internal class used to write data coming from a queue. - */ - class DataWriter extends Thread { - @Override - public void run() { - while (streamOpen) { - try { - byte[] dataToWrite = writeQueue.poll(100, TimeUnit.MILLISECONDS); - if (dataToWrite == null) - continue; - usbConnection.bulkTransfer(sendEndPoint, dataToWrite, dataToWrite.length, WRITE_TIMEOUT); - Log.d("USBOutputStream","Message sent: " + dataToWrite.toString()); - } catch (InterruptedException e) { - Log.e("USBOutputStream","Interrupted while getting data from the write queue: " + e.getMessage(), e); - } - } - } + usbConnection.bulkTransfer(sendEndPoint, buffer, count, WRITE_TIMEOUT); + Log.d("USBOutputStream", "Message sent: " + buffer.toString()); } @Override public void close() throws IOException { - // Stop the data writer. - streamOpen = false; super.close(); } } From 258e9a28cd4bf257963e45947914f1e43f5a45fa Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Mon, 24 May 2021 03:07:59 +0200 Subject: [PATCH 02/16] bug fixes + Proper event handling for player (Observable Java pattern) --- .../com/fpvout/digiview/MainActivity.java | 24 ++++------- .../fpvout/digiview/PerformancePreset.java | 3 +- .../fpvout/digiview/VideoReaderExoplayer.java | 42 +++++++++++-------- .../main/java/usb/AndroidUSBInputStream.java | 5 ++- 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/fpvout/digiview/MainActivity.java b/app/src/main/java/com/fpvout/digiview/MainActivity.java index 19d1476..22c909e 100644 --- a/app/src/main/java/com/fpvout/digiview/MainActivity.java +++ b/app/src/main/java/com/fpvout/digiview/MainActivity.java @@ -3,6 +3,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.LayoutTransition; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; @@ -27,12 +28,11 @@ import io.sentry.android.core.SentryAndroid; import static com.fpvout.digiview.UsbMaskConnection.ACTION_USB_PERMISSION; -import static com.fpvout.digiview.VideoReaderExoplayer.VideoReaderEventMessageCode; import static com.fpvout.digiview.VideoReaderExoplayer.VideoZoomedIn; public class MainActivity extends AppCompatActivity implements UsbDeviceListener { private static final String TAG = "DIGIVIEW"; - private final int shortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); + private int shortAnimationDuration; private float buttonAlpha = 1; private View settingsButton; private View watermarkView; @@ -64,6 +64,7 @@ protected void onCreate(Bundle savedInstanceState) { // Prevent screen from sleeping getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); // Register app for auto launch usbDeviceBroadcastReceiver = new UsbDeviceBroadcastReceiver(this); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); @@ -77,7 +78,7 @@ protected void onCreate(Bundle savedInstanceState) { settingsButton = findViewById(R.id.settingsButton); sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - + shortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); // Enable resizing animations ((ViewGroup) findViewById(R.id.mainLayout)).getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); @@ -89,8 +90,9 @@ protected void onCreate(Bundle savedInstanceState) { v.getContext().startActivity(intent); }); - Handler videoReaderEventListener = new Handler(this.getMainLooper(), msg -> onVideoReaderEvent((VideoReaderEventMessageCode) msg.obj)); - mVideoReader = new VideoReaderExoplayer(fpvView, this, videoReaderEventListener); + mVideoReader = new VideoReaderExoplayer(fpvView, this); + mVideoReader.setVideoPlayingEventListener(this::hideOverlay); + mVideoReader.setVideoWaitingEventListener(() -> showOverlay(R.string.waiting_for_video, OverlayStatus.Connected)); mUsbMaskConnection = new UsbMaskConnection(); @@ -227,17 +229,6 @@ private void autoHideSettingsButton() { }, 3000); } - private boolean onVideoReaderEvent(VideoReaderEventMessageCode m) { - if (VideoReaderEventMessageCode.WAITING_FOR_VIDEO.equals(m)) { - Log.d(TAG, "event: WAITING_FOR_VIDEO"); - showOverlay(R.string.waiting_for_video, OverlayStatus.Connected); - } else if (VideoReaderEventMessageCode.VIDEO_PLAYING.equals(m)) { - Log.d(TAG, "event: VIDEO_PLAYING"); - hideOverlay(); - } - return false; // false to continue listening - } - private void showOverlay(int textId, OverlayStatus connected) { overlayView.show(textId, connected); updateWatermark(); @@ -354,6 +345,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } + setFullscreen(); } //onActivityResult private void checkDataCollectionAgreement() { diff --git a/app/src/main/java/com/fpvout/digiview/PerformancePreset.java b/app/src/main/java/com/fpvout/digiview/PerformancePreset.java index 3eeba24..44c4f5a 100644 --- a/app/src/main/java/com/fpvout/digiview/PerformancePreset.java +++ b/app/src/main/java/com/fpvout/digiview/PerformancePreset.java @@ -25,7 +25,8 @@ public enum PresetType { DEFAULT, CONSERVATIVE, AGGRESSIVE, - LEGACY + LEGACY, + LEGACY_BUFFERED } public enum DataSourceType { diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index 7d0cf08..2d695db 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -5,7 +5,6 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; -import android.os.Message; import android.util.Log; import android.view.SurfaceView; @@ -38,24 +37,30 @@ public class VideoReaderExoplayer { static final String VideoPreset = "VideoPreset"; private final SurfaceView surfaceView; private AndroidUSBInputStream inputStream; - private UsbMaskConnection mUsbMaskConnection; + private UsbMaskConnection mUsbMaskConnection; private boolean zoomedIn; private final Context context; private PerformancePreset performancePreset = PerformancePreset.getPreset(PerformancePreset.PresetType.DEFAULT); static final String VideoZoomedIn = "VideoZoomedIn"; private final SharedPreferences sharedPreferences; + private VideoPlayingListener videoPlayingListener = null; + private VideoWaitingListener videoWaitingListener = null; + + public void setVideoPlayingEventListener(VideoPlayingListener listener) { + this.videoPlayingListener = listener; + } + + public void setVideoWaitingEventListener(VideoWaitingListener listener) { + this.videoWaitingListener = listener; + } + VideoReaderExoplayer(SurfaceView videoSurface, Context c) { surfaceView = videoSurface; context = c; sharedPreferences = PreferenceManager.getDefaultSharedPreferences(c); } - VideoReaderExoplayer(SurfaceView videoSurface, Context c, Handler v) { - this(videoSurface, c); - videoReaderEventListener = v; - } - public void setUsbMaskConnection(UsbMaskConnection connection) { mUsbMaskConnection = connection; inputStream = mUsbMaskConnection.mInputStream; @@ -121,7 +126,8 @@ public void onPlaybackStateChanged(@NonNullApi int state) { break; case Player.STATE_ENDED: Log.d(TAG, "PLAYER_STATE - ENDED"); - sendEvent(VideoReaderEventMessageCode.WAITING_FOR_VIDEO); // let MainActivity know so it can hide watermark/show settings button + if (videoWaitingListener != null) + videoWaitingListener.onVideoWaiting(); // let MainActivity know so it can hide watermark/show settings button (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); break; } @@ -132,7 +138,8 @@ public void onPlaybackStateChanged(@NonNullApi int state) { @Override public void onRenderedFirstFrame() { Log.d(TAG, "PLAYER_RENDER - FIRST FRAME"); - sendEvent(VideoReaderEventMessageCode.VIDEO_PLAYING); // let MainActivity know so it can hide watermark/show settings button + if (videoPlayingListener != null) + videoPlayingListener.onVideoPlaying(); // let MainActivity know so it can hide watermark/show settings button } @Override @@ -144,14 +151,15 @@ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegre } } }); - } + } - private void sendEvent(VideoReaderEventMessageCode eventCode) { - if (videoReaderEventListener != null) { // let MainActivity know so it can hide watermark/show settings button - Message videoReaderEventMessage = new Message(); - videoReaderEventMessage.obj = eventCode; - videoReaderEventListener.sendMessage(videoReaderEventMessage); - } + public interface VideoPlayingListener { + void onVideoPlaying(); + } + + + public interface VideoWaitingListener { + void onVideoWaiting(); } public void toggleZoom() { @@ -201,6 +209,4 @@ public void stop() { if (mPlayer != null) mPlayer.release(); } - - public enum VideoReaderEventMessageCode {WAITING_FOR_VIDEO, VIDEO_PLAYING} } diff --git a/app/src/main/java/usb/AndroidUSBInputStream.java b/app/src/main/java/usb/AndroidUSBInputStream.java index 52ca056..5d74542 100644 --- a/app/src/main/java/usb/AndroidUSBInputStream.java +++ b/app/src/main/java/usb/AndroidUSBInputStream.java @@ -4,6 +4,7 @@ import android.hardware.usb.UsbEndpoint; import android.util.Log; +import java.io.IOException; import java.io.InputStream; public class AndroidUSBInputStream extends InputStream { @@ -51,6 +52,8 @@ public int read(byte[] buffer, int offset, int length) { @Override - public void close(){} + public void close() throws IOException { + super.close(); + } } From 8aed1233e1cd463ee8e5d97ced2f265cc99b5b27 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Mon, 24 May 2021 04:27:57 +0200 Subject: [PATCH 03/16] No more warnings + update dependencies --- app/build.gradle | 8 +- .../InputStreamBufferedDataSource.java | 10 +- .../digiview/InputStreamDataSource.java | 8 +- .../com/fpvout/digiview/MainActivity.java | 72 ++++++----- .../fpvout/digiview/VideoReaderExoplayer.java | 85 ++++++------- .../main/java/usb/AndroidUSBInputStream.java | 4 +- .../main/java/usb/AndroidUSBOutputStream.java | 2 - app/src/main/java/usb/CircularByteBuffer.java | 118 ++++++++---------- 8 files changed, 142 insertions(+), 165 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index de58b67..f360733 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { keyAlias digiviewKeyAlias } catch (ex) { - println("You should define mStoreFile, mStorePassword, mKeyPassword and mKeyAlias in ~/.gradle/gradle.properties.") + println("You should define mStoreFile, mStorePassword, mKeyPassword and mKeyAlias in ~/.gradle/gradle.properties : "+ ex.message) } } } @@ -65,14 +65,14 @@ android { dependencies { - implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - implementation 'com.google.android.exoplayer:exoplayer:2.13.3' + implementation 'com.google.android.exoplayer:exoplayer:2.14.0' implementation 'io.sentry:sentry-android:4.3.0' implementation 'androidx.preference:preference:1.1.1' - testImplementation 'junit:junit:4.+' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' } \ No newline at end of file diff --git a/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java b/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java index 11fb41d..d6bc5b6 100644 --- a/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java +++ b/app/src/main/java/com/fpvout/digiview/InputStreamBufferedDataSource.java @@ -2,6 +2,8 @@ import android.net.Uri; +import androidx.annotation.NonNull; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; @@ -20,7 +22,6 @@ public class InputStreamBufferedDataSource implements DataSource { private final DataSpec dataSpec; private InputStream inputStream; - private long bytesRemaining; private boolean opened; private CircularByteBuffer readBuffer; @@ -35,12 +36,13 @@ public InputStreamBufferedDataSource(DataSpec dataSpec, InputStream inputStream) } @Override - public void addTransferListener(TransferListener transferListener) { + public void addTransferListener(@NonNull TransferListener transferListener) { } @Override public long open(DataSpec dataSpec) throws IOException { + long bytesRemaining; try { long skipped = inputStream.skip(dataSpec.position); if (skipped < dataSpec.position) @@ -60,7 +62,7 @@ public long open(DataSpec dataSpec) throws IOException { } @Override - public int read(byte[] buffer, int offset, int readLength) throws IOException { + public int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException { if (readBuffer == null) throw new IOException(ERROR_THREAD_NOT_INITIALIZED); @@ -68,8 +70,6 @@ public int read(byte[] buffer, int offset, int readLength) throws IOException { int readBytes = 0; while (System.currentTimeMillis() < deadLine && readBytes <= 0) readBytes = readBuffer.read(buffer, offset, readLength); - if (readBytes <= 0) - return readBytes; return readBytes; } diff --git a/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java b/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java index 1e5f6ad..fe1634c 100644 --- a/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java +++ b/app/src/main/java/com/fpvout/digiview/InputStreamDataSource.java @@ -2,6 +2,8 @@ import android.net.Uri; +import androidx.annotation.NonNull; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; @@ -14,7 +16,6 @@ public class InputStreamDataSource implements DataSource { private final DataSpec dataSpec; private InputStream inputStream; - private long bytesRemaining; private boolean opened; public InputStreamDataSource(DataSpec dataSpec, InputStream inputStream) { @@ -23,12 +24,13 @@ public InputStreamDataSource(DataSpec dataSpec, InputStream inputStream) { } @Override - public void addTransferListener(TransferListener transferListener) { + public void addTransferListener(@NonNull TransferListener transferListener) { } @Override public long open(DataSpec dataSpec) throws IOException { + long bytesRemaining; try { long skipped = inputStream.skip(dataSpec.position); if (skipped < dataSpec.position) @@ -48,7 +50,7 @@ public long open(DataSpec dataSpec) throws IOException { } @Override - public int read(byte[] buffer, int offset, int readLength) throws IOException { + public int read(@NonNull byte[] buffer, int offset, int readLength) throws IOException { return inputStream.read(buffer, offset, readLength); } diff --git a/app/src/main/java/com/fpvout/digiview/MainActivity.java b/app/src/main/java/com/fpvout/digiview/MainActivity.java index 22c909e..1a59c4b 100644 --- a/app/src/main/java/com/fpvout/digiview/MainActivity.java +++ b/app/src/main/java/com/fpvout/digiview/MainActivity.java @@ -20,8 +20,14 @@ import android.view.ViewGroup; import android.view.WindowManager; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.view.WindowInsetsControllerCompat; import androidx.preference.PreferenceManager; import io.sentry.SentryLevel; @@ -109,13 +115,13 @@ protected void onCreate(Bundle savedInstanceState) { } private void setFullscreen() { - View decorView = getWindow().getDecorView(); - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN); + WindowInsetsControllerCompat insetsControllerCompat = new WindowInsetsControllerCompat(getWindow(), getWindow().getDecorView()); + insetsControllerCompat.hide(WindowInsetsCompat.Type.statusBars() + | WindowInsetsCompat.Type.navigationBars() + | WindowInsetsCompat.Type.captionBar() + | WindowInsetsCompat.Type.ime() + ); + insetsControllerCompat.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.hide(); @@ -185,14 +191,14 @@ private void cancelButtonAnimation() { } } - private void showSettingsButton() { - cancelButtonAnimation(); - - if (overlayView.getVisibility() == View.VISIBLE) { - buttonAlpha = 1; - settingsButton.setAlpha(1); - } - } +// private void showSettingsButton() { +// cancelButtonAnimation(); +// +// if (overlayView.getVisibility() == View.VISIBLE) { +// buttonAlpha = 1; +// settingsButton.setAlpha(1); +// } +// } private void toggleSettingsButton() { if (buttonAlpha == 1 && overlayView.getVisibility() == View.VISIBLE) return; @@ -327,34 +333,26 @@ protected void onDestroy() { usbConnected = false; } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - boolean dataCollectionAccepted = preferences.getBoolean("dataCollectionAccepted", false); - - if (requestCode == 1) { // Data Collection agreement Activity - if (resultCode == RESULT_OK && dataCollectionAccepted) { - SentryAndroid.init(this, options -> options.setBeforeSend((event, hint) -> { - if (SentryLevel.DEBUG.equals(event.getLevel())) - return null; - else - return event; - })); - } - - } - setFullscreen(); - } //onActivityResult - private void checkDataCollectionAgreement() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); boolean dataCollectionAccepted = preferences.getBoolean("dataCollectionAccepted", false); boolean dataCollectionReplied = preferences.getBoolean("dataCollectionReplied", false); if (!dataCollectionReplied) { Intent intent = new Intent(this, DataCollectionAgreementPopupActivity.class); - startActivityForResult(intent, 1); + ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), (ActivityResultCallback) result -> { + if (result.getResultCode() == RESULT_OK) { + SentryAndroid.init(getApplicationContext(), options -> options.setBeforeSend((event, hint) -> { + if (SentryLevel.DEBUG.equals(event.getLevel())) + return null; + else + return event; + })); + } + setFullscreen(); + }); + activityResultLauncher.launch(intent); + + } else if (dataCollectionAccepted) { SentryAndroid.init(this, options -> options.setBeforeSend((event, hint) -> { if (SentryLevel.DEBUG.equals(event.getLevel())) diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index 2d695db..ba68bb1 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -8,13 +8,13 @@ import android.util.Log; import android.view.SurfaceView; +import androidx.annotation.NonNull; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.preference.PreferenceManager; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Player; @@ -25,14 +25,12 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; -import com.google.android.exoplayer2.util.NonNullApi; -import com.google.android.exoplayer2.video.VideoListener; +import com.google.android.exoplayer2.video.VideoSize; import usb.AndroidUSBInputStream; public class VideoReaderExoplayer { private static final String TAG = "DIGIVIEW"; - private Handler videoReaderEventListener; private SimpleExoPlayer mPlayer; static final String VideoPreset = "VideoPreset"; private final SurfaceView surfaceView; @@ -96,20 +94,19 @@ public void start() { mPlayer.prepare(); mPlayer.play(); - mPlayer.addListener(new ExoPlayer.EventListener() { - @Override - @NonNullApi - public void onPlayerError(ExoPlaybackException error) { - switch (error.type) { - case ExoPlaybackException.TYPE_SOURCE: - Log.e(TAG, "PLAYER_SOURCE - TYPE_SOURCE: " + error.getSourceException().getMessage()); - (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); - break; - case ExoPlaybackException.TYPE_REMOTE: - Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getSourceException().getMessage()); - break; - case ExoPlaybackException.TYPE_RENDERER: - Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getSourceException().getMessage()); + mPlayer.addListener(new Player.Listener() { + @Override + public void onPlayerError(@NonNull ExoPlaybackException error) { + switch (error.type) { + case ExoPlaybackException.TYPE_SOURCE: + Log.e(TAG, "PLAYER_SOURCE - TYPE_SOURCE: " + error.getSourceException().getMessage()); + (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); + break; + case ExoPlaybackException.TYPE_REMOTE: + Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getSourceException().getMessage()); + break; + case ExoPlaybackException.TYPE_RENDERER: + Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getSourceException().getMessage()); break; case ExoPlaybackException.TYPE_UNEXPECTED: Log.e(TAG, "PLAYER_SOURCE - TYPE_UNEXPECTED: " + error.getSourceException().getMessage()); @@ -117,39 +114,39 @@ public void onPlayerError(ExoPlaybackException error) { } } - @Override - public void onPlaybackStateChanged(@NonNullApi int state) { - switch (state) { - case Player.STATE_IDLE: - case Player.STATE_READY: - case Player.STATE_BUFFERING: - break; - case Player.STATE_ENDED: - Log.d(TAG, "PLAYER_STATE - ENDED"); - if (videoWaitingListener != null) - videoWaitingListener.onVideoWaiting(); // let MainActivity know so it can hide watermark/show settings button - (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); + @Override + public void onPlaybackStateChanged(int state) { + switch (state) { + case Player.STATE_IDLE: + case Player.STATE_READY: + case Player.STATE_BUFFERING: + break; + case Player.STATE_ENDED: + Log.d(TAG, "PLAYER_STATE - ENDED"); + if (videoWaitingListener != null) + videoWaitingListener.onVideoWaiting(); // let MainActivity know so it can hide watermark/show settings button + (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); break; } } }); - mPlayer.addVideoListener(new VideoListener() { - @Override - public void onRenderedFirstFrame() { - Log.d(TAG, "PLAYER_RENDER - FIRST FRAME"); - if (videoPlayingListener != null) - videoPlayingListener.onVideoPlaying(); // let MainActivity know so it can hide watermark/show settings button - } + mPlayer.addVideoListener(new Player.Listener() { + @Override + public void onRenderedFirstFrame() { + Log.d(TAG, "PLAYER_RENDER - FIRST FRAME"); + if (videoPlayingListener != null) + videoPlayingListener.onVideoPlaying(); // let MainActivity know so it can hide watermark/show settings button + } - @Override - public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { - if (!zoomedIn) { - ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) surfaceView.getLayoutParams(); - params.dimensionRatio = width + ":" + height; - surfaceView.setLayoutParams(params); - } + @Override + public void onVideoSizeChanged(@NonNull VideoSize videosize) { + if (!zoomedIn) { + ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) surfaceView.getLayoutParams(); + params.dimensionRatio = videosize.width + ":" + videosize.height; + surfaceView.setLayoutParams(params); } + } }); } diff --git a/app/src/main/java/usb/AndroidUSBInputStream.java b/app/src/main/java/usb/AndroidUSBInputStream.java index 5d74542..571531d 100644 --- a/app/src/main/java/usb/AndroidUSBInputStream.java +++ b/app/src/main/java/usb/AndroidUSBInputStream.java @@ -2,14 +2,12 @@ import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; -import android.util.Log; import java.io.IOException; import java.io.InputStream; public class AndroidUSBInputStream extends InputStream { - private final String TAG = "USBInputStream"; private static final int READ_TIMEOUT = 100; private final UsbDeviceConnection usbConnection; @@ -43,7 +41,7 @@ public int read(byte[] buffer, int offset, int length) { int receivedBytes = usbConnection.bulkTransfer(receiveEndPoint, buffer, buffer.length, READ_TIMEOUT); if (receivedBytes <= 0) { // send magic packet again; Would be great to handle this in UsbMaskConnection directly... - Log.d(TAG, "received buffer empty, sending magic packet again..."); + //Log.d(TAG, "received buffer empty, sending magic packet again..."); usbConnection.bulkTransfer(sendEndPoint, "RMVT".getBytes(), "RMVT".getBytes().length, 2000); receivedBytes = usbConnection.bulkTransfer(receiveEndPoint, buffer, buffer.length, READ_TIMEOUT); } diff --git a/app/src/main/java/usb/AndroidUSBOutputStream.java b/app/src/main/java/usb/AndroidUSBOutputStream.java index 31a410a..da7f042 100644 --- a/app/src/main/java/usb/AndroidUSBOutputStream.java +++ b/app/src/main/java/usb/AndroidUSBOutputStream.java @@ -18,8 +18,6 @@ public class AndroidUSBOutputStream extends OutputStream { private final UsbDeviceConnection usbConnection; private final UsbEndpoint sendEndPoint; - private final boolean streamOpen = true; - /** * Class constructor. Instantiates a new {@code AndroidUSBOutputStream} * object with the given parameters. diff --git a/app/src/main/java/usb/CircularByteBuffer.java b/app/src/main/java/usb/CircularByteBuffer.java index ba45a36..90f9109 100644 --- a/app/src/main/java/usb/CircularByteBuffer.java +++ b/app/src/main/java/usb/CircularByteBuffer.java @@ -1,18 +1,3 @@ -/* - * Copyright 2019, Digi International Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at http://mozilla.org/MPL/2.0/. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ package usb; /** @@ -21,7 +6,7 @@ public class CircularByteBuffer { // Variables. - private byte[] buffer; + private final byte[] buffer; private int readIndex; private int writeIndex; @@ -58,7 +43,7 @@ public CircularByteBuffer(int size) { * @throws NullPointerException if {@code data == null}. * * @see #read(byte[], int, int) - * @see #skip(int) + // * @see #skip(int) */ public synchronized int write(byte[] data, int offset, int numBytes) { if (data == null) @@ -105,8 +90,8 @@ public synchronized int write(byte[] data, int offset, int numBytes) { * @throws IllegalArgumentException if {@code offset < 0} or * if {@code numBytes < 1}. * @throws NullPointerException if {@code data == null}. - * - * @see #skip(int) + * + // * @see #skip(int) * @see #write(byte[], int, int) */ public synchronized int read(byte[] data, int offset, int numBytes) { @@ -135,54 +120,53 @@ public synchronized int read(byte[] data, int offset, int numBytes) { } else { System.arraycopy(buffer, getReadIndex(), data, offset, buffer.length - getReadIndex()); System.arraycopy(buffer, 0, data, offset + buffer.length - getReadIndex(), numBytes - (buffer.length - getReadIndex())); - readIndex = numBytes-(buffer.length - getReadIndex()); + readIndex = numBytes - (buffer.length - getReadIndex()); } - + // If we have read all bytes, set the buffer as empty. if (readIndex == writeIndex) empty = true; - - return numBytes; - } - /** - * Skips the given number of bytes from the circular byte buffer. - * - * @param numBytes Number of bytes to skip. - * @return The number of bytes actually skipped. - * - * @throws IllegalArgumentException if {@code numBytes < 1}. - * - * @see #read(byte[], int, int) - * @see #write(byte[], int, int) - */ - public synchronized int skip(int numBytes) { - if (numBytes < 1) - throw new IllegalArgumentException("Number of bytes to skip must be greater than 0."); - - // If we are empty, return 0. - if (empty) - return 0; - - if (availableToRead() < numBytes) - return skip(availableToRead()); - if (numBytes < buffer.length - getReadIndex()) - readIndex = getReadIndex() + numBytes; - else - readIndex = numBytes - (buffer.length - getReadIndex()); - - // If we have skipped all bytes, set the buffer as empty. - if (readIndex == writeIndex) - empty = true; - return numBytes; } +// /** +// * Skips the given number of bytes from the circular byte buffer. +// * +// * @param numBytes Number of bytes to skip. +// * @return The number of bytes actually skipped. +// * +// * @throws IllegalArgumentException if {@code numBytes < 1}. +// * +// * @see #read(byte[], int, int) +// * @see #write(byte[], int, int) +// */ +// public synchronized int skip(int numBytes) { +// if (numBytes < 1) +// throw new IllegalArgumentException("Number of bytes to skip must be greater than 0."); +// +// // If we are empty, return 0. +// if (empty) +// return 0; +// +// if (availableToRead() < numBytes) +// return skip(availableToRead()); +// if (numBytes < buffer.length - getReadIndex()) +// readIndex = getReadIndex() + numBytes; +// else +// readIndex = numBytes - (buffer.length - getReadIndex()); +// +// // If we have skipped all bytes, set the buffer as empty. +// if (readIndex == writeIndex) +// empty = true; +// +// return numBytes; +// } + /** * Returns the available number of bytes to read from the byte buffer. - * + * * @return The number of bytes in the buffer available for reading. - * * @see #getCapacity() * @see #read(byte[], int, int) */ @@ -212,22 +196,22 @@ private int getReadIndex() { private int getWriteIndex() { return writeIndex; } - + /** * Returns the circular byte buffer capacity. - * + * * @return The circular byte buffer capacity. */ public int getCapacity() { return buffer.length; } - - /** - * Clears the circular buffer. - */ - public void clearBuffer() { - empty = true; - readIndex = 0; - writeIndex = 0; - } + +// /** +// * Clears the circular buffer. +// */ +// public void clearBuffer() { +// empty = true; +// readIndex = 0; +// writeIndex = 0; +// } } \ No newline at end of file From 351892703a42b64d69cc398f177d1711fa3896d4 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Wed, 26 May 2021 00:19:23 +0200 Subject: [PATCH 04/16] continue cleanup --- app/src/main/AndroidManifest.xml | 29 +++++++++----- .../com/fpvout/digiview/H264Extractor.java | 4 +- .../com/fpvout/digiview/MainActivity.java | 12 +++--- .../fpvout/digiview/PerformancePreset.java | 28 ++++++------- .../digiview/UsbDeviceBroadcastReceiver.java | 2 +- .../fpvout/digiview/UsbMaskConnection.java | 4 +- .../fpvout/digiview/VideoReaderExoplayer.java | 40 +++++++++---------- build.gradle | 6 +-- gradle/wrapper/gradle-wrapper.properties | 2 +- 9 files changed, 65 insertions(+), 62 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7df7ca4..fd85baf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,21 +12,20 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.Digiview" - > + android:theme="@style/Theme.Digiview"> + android:theme="@style/Theme.AppCompat.Dialog" /> + android:label="@string/title_activity_settings" /> + android:configChanges="orientation|keyboardHidden" + android:launchMode="singleTask" + android:screenOrientation="sensorLandscape"> @@ -38,14 +37,22 @@ - - - - + + + + Date: Wed, 26 May 2021 03:23:27 +0200 Subject: [PATCH 05/16] introduce videoStreamService and VideoStreamServiceDataSource for exoplayer. (put it on a seperate preset for now, as it needs more testing) --- .../fpvout/digiview/PerformancePreset.java | 10 ++- .../fpvout/digiview/UsbMaskConnection.java | 13 ++- .../fpvout/digiview/VideoReaderExoplayer.java | 12 ++- .../fpvout/digiview/VideoStreamService.java | 71 ++++++++++++++++ .../VideoStreamServiceDataSource.java | 81 +++++++++++++++++++ app/src/main/res/values/arrays.xml | 3 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/fpvout/digiview/VideoStreamService.java create mode 100644 app/src/main/java/com/fpvout/digiview/VideoStreamServiceDataSource.java diff --git a/app/src/main/java/com/fpvout/digiview/PerformancePreset.java b/app/src/main/java/com/fpvout/digiview/PerformancePreset.java index 18aacc8..9dc7067 100644 --- a/app/src/main/java/com/fpvout/digiview/PerformancePreset.java +++ b/app/src/main/java/com/fpvout/digiview/PerformancePreset.java @@ -31,6 +31,8 @@ static PerformancePreset getPreset(PresetType p) { return new PerformancePreset(30720, 200, 32768, 65536, 0, 0, DataSourceType.BUFFERED_INPUT_STREAM); case LEGACY_BUFFERED: return new PerformancePreset(30720, 300, 32768, 65536, 34, 34, DataSourceType.BUFFERED_INPUT_STREAM); + case VIDEO_STREAM_SERVICE: + return new PerformancePreset(131072, 10000, 500, 2000, 17, 17, DataSourceType.VIDEO_STREAM_SERVICE); case DEFAULT: default: return new PerformancePreset(131072, 10000, 500, 2000, 17, 17, DataSourceType.INPUT_STREAM); @@ -47,6 +49,8 @@ static PerformancePreset getPreset(String p) { return getPreset(PresetType.LEGACY); case "new_legacy": return getPreset(PresetType.LEGACY_BUFFERED); + case "video_stream_service": + return getPreset(PresetType.VIDEO_STREAM_SERVICE); case "default": default: return getPreset(PresetType.DEFAULT); @@ -72,12 +76,14 @@ public enum PresetType { CONSERVATIVE, AGGRESSIVE, LEGACY, - LEGACY_BUFFERED + LEGACY_BUFFERED, + VIDEO_STREAM_SERVICE } public enum DataSourceType { INPUT_STREAM, - BUFFERED_INPUT_STREAM + BUFFERED_INPUT_STREAM, + VIDEO_STREAM_SERVICE } } diff --git a/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java b/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java index d130e31..3895245 100644 --- a/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java +++ b/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java @@ -25,6 +25,7 @@ public class UsbMaskConnection { private UsbDeviceConnection usbConnection; private UsbInterface usbInterface; private boolean ready = false; + private VideoStreamService videoStreamService; public UsbMaskConnection() { } @@ -50,17 +51,21 @@ public static UsbDevice searchDevice(UsbManager usbManager, Context c) { public void start(){ mOutputStream.write(magicPacket); + videoStreamService.start(); } public void stop() { ready = false; try { + if (videoStreamService != null) + videoStreamService.stop(); + if (mInputStream != null) mInputStream.close(); if (mOutputStream != null) mOutputStream.close(); - } catch (IOException e) { + } catch (IOException | InterruptedException e) { e.printStackTrace(); } @@ -74,6 +79,10 @@ public boolean isReady() { return ready; } + public VideoStreamService getVideoStreamService() { + return videoStreamService; + } + public void setUsbDevice(UsbManager usbManager, UsbDevice d) { usbConnection = usbManager.openDevice(d); usbInterface = d.getInterface(3); @@ -82,6 +91,8 @@ public void setUsbDevice(UsbManager usbManager, UsbDevice d) { mOutputStream = new AndroidUSBOutputStream(usbInterface.getEndpoint(0), usbConnection); mInputStream = new AndroidUSBInputStream(usbInterface.getEndpoint(1), usbInterface.getEndpoint(0), usbConnection); + videoStreamService = new VideoStreamService(mInputStream); + start(); ready = true; } } diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index 57f1234..4db9d76 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -81,10 +81,14 @@ public void start() { switch (performancePreset.dataSourceType) { case INPUT_STREAM: return (DataSource) new InputStreamDataSource(dataSpec, inputStream); - case BUFFERED_INPUT_STREAM: - default: - return (DataSource) new InputStreamBufferedDataSource(dataSpec, inputStream); - } + case BUFFERED_INPUT_STREAM: + return (DataSource) new InputStreamBufferedDataSource(dataSpec, inputStream); + case VIDEO_STREAM_SERVICE: + default: + VideoStreamServiceDataSource v = new VideoStreamServiceDataSource(dataSpec); + mUsbMaskConnection.getVideoStreamService().addVideoStreamListener(v::onVideoStreamData); + return v; + } }; ExtractorsFactory extractorsFactory = () ->new Extractor[] {new H264Extractor(performancePreset.h264ReaderMaxSyncFrameSize, performancePreset.h264ReaderSampleTime)}; diff --git a/app/src/main/java/com/fpvout/digiview/VideoStreamService.java b/app/src/main/java/com/fpvout/digiview/VideoStreamService.java new file mode 100644 index 0000000..0bd412a --- /dev/null +++ b/app/src/main/java/com/fpvout/digiview/VideoStreamService.java @@ -0,0 +1,71 @@ +package com.fpvout.digiview; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; + +/** + * Consume an inputStream and make it available to all the listeners + */ +public class VideoStreamService { + + private static final int READ_BUFFER_SIZE = 131072; + private static final String TAG = "VideoStreamService"; + private final ArrayList videoStreamListeners; + InputStream videoStream; + private boolean isRunning = false; + private Thread streamServiceThread; + + + public VideoStreamService(InputStream inputStream) { + videoStream = inputStream; + videoStreamListeners = new ArrayList<>(); + } + + public void start() { + Log.d(TAG, "streamServiceThread started"); + if (!isRunning) { + isRunning = true; + streamServiceThread = new Thread(() -> { + while (isRunning) { + try { + byte[] buffer = new byte[READ_BUFFER_SIZE]; + int bytesReceived = videoStream.read(buffer, 0, READ_BUFFER_SIZE); + if (bytesReceived >= 0) { + Log.d(TAG, "bytesReceived : " + bytesReceived); + for (VideoStreamListener v : videoStreamListeners) { + v.onVideoStreamData(buffer, bytesReceived); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + streamServiceThread.start(); + } + + } + + public void stop() throws InterruptedException { + Log.d(TAG, "streamServiceThread stopped"); + isRunning = false; + streamServiceThread.join(); + } + + public void addVideoStreamListener(VideoStreamListener listener) { + Log.d(TAG, "addVideoStreamListener"); + videoStreamListeners.add(listener); + } + + public void removeVideoStreamListener(VideoStreamListener listener) { + Log.d(TAG, "removeVideoStreamListener"); + videoStreamListeners.remove(listener); + } + + public interface VideoStreamListener { + void onVideoStreamData(byte[] buffer, int bytesReceived); + } +} diff --git a/app/src/main/java/com/fpvout/digiview/VideoStreamServiceDataSource.java b/app/src/main/java/com/fpvout/digiview/VideoStreamServiceDataSource.java new file mode 100644 index 0000000..150026b --- /dev/null +++ b/app/src/main/java/com/fpvout/digiview/VideoStreamServiceDataSource.java @@ -0,0 +1,81 @@ +package com.fpvout.digiview; + +import android.net.Uri; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.TransferListener; + +import usb.CircularByteBuffer; + +public class VideoStreamServiceDataSource implements DataSource { + private static final int READ_BUFFER_SIZE = 50 * 1024 * 1024; + private static final long READ_TIMEOUT = 200; + + private final DataSpec dataSpec; + private final CircularByteBuffer readBuffer; + private boolean opened; + + public VideoStreamServiceDataSource(DataSpec dataSpec) { + this.dataSpec = dataSpec; + readBuffer = new CircularByteBuffer(READ_BUFFER_SIZE); + } + + public VideoStreamServiceDataSource(DataSpec dataSpec, VideoStreamService v) { + this.dataSpec = dataSpec; + readBuffer = new CircularByteBuffer(READ_BUFFER_SIZE); + v.addVideoStreamListener(this::onVideoStreamData); + } + + @Override + public void addTransferListener(@NonNull TransferListener transferListener) { + + } + + @Override + public long open(DataSpec dataSpec) { + long bytesRemaining; + + if (dataSpec.length != C.LENGTH_UNSET) { + bytesRemaining = dataSpec.length; + } else { + bytesRemaining = C.LENGTH_UNSET; + } + + opened = true; + return bytesRemaining; + } + + @Override + public int read(@NonNull byte[] buffer, int offset, int readLength) { + long deadLine = System.currentTimeMillis() + READ_TIMEOUT; + int readBytes = 0; + while (System.currentTimeMillis() < deadLine && readBytes <= 0) + readBytes = readBuffer.read(buffer, offset, readLength); + return readBytes; + } + + @Override + public Uri getUri() { + return dataSpec.uri; + } + + @Override + public void close() { + if (opened) { + opened = false; + } + + } + + public void onVideoStreamData(byte[] buffer, int receivedBytes) { + if (receivedBytes > 0) { + readBuffer.write(buffer, 0, receivedBytes); + Log.d("onVideoStream", "onVideoStreamData: " + receivedBytes); + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index fd59273..68eefdd 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -6,6 +6,8 @@ @string/video_preset_aggressive @string/video_preset_legacy @string/video_preset_legacy_buffered + @string/video_preset_video_stream_service + @@ -14,5 +16,6 @@ aggressive legacy legacy_buffered + video_stream_service \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dba2270..2ee0237 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,5 +40,6 @@ Copyright Open-Source License MIT License + Video Stream Service \ No newline at end of file From 15a72ea3217f84f580eefaaca752481b1bf4bb92 Mon Sep 17 00:00:00 2001 From: Olivier Mouren Date: Mon, 31 May 2021 22:00:32 +0200 Subject: [PATCH 06/16] Better french translations --- app/src/main/res/values-fr/strings.xml | 30 +++++++++++------------ app/src/main/res/xml/root_preferences.xml | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index ccb60bf..642ccf2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,39 +1,39 @@ DigiView\nfeed - Attente de périphérique USB… + En attente du périphérique USB… Périphérique USB approuvé - Périphérique USB détaché\n\nAttente de périphérique USB… + Périphérique USB déconnecté\nEn attente du périphérique USB… Périphérique USB trouvé. En attente de la vidéo… - Collection des données - Partager les données de crashs avec l\'équipe de DigiView ? - Cette application crashera sûrement moins que votre drone. Mais si jamais l\'application plante, On aimerait bien le savoir ! L\'équipe utilise Sentry pour tracker les bugs et rendre cette application meilleur. + Collecte de données + Partager mes données et diagnostics de crashs avec l\'équipe de DigiView ? + Cette application crashera sûrement moins que votre drone. Mais si jamais elle plante, nous aimerions bien le savoir ! Nous utilisons Sentry pour tracker les erreurs et crashs de l\'application, contribuez à son amélioration en acceptant de partager ces données avec nous. Accepter Refuser Paramètres Performance - Vie Privée - Preset Vidéo + Vie privée + Preset vidéo Default Conservative Aggressive Legacy - Activer les Analytics - politique de confidentialité + Collecte de données + Politique de confidentialité Voyez quelles données nous recoltons et comment nous les utilisons. - Lecteur Vidéo - Plein Ecran - peut aussi être activer en double tapant ou en pincant le lecteur vidéo. + Lecteur vidéo + Plein écran + Peut aussi être activé en double tapant ou en pincant le l\'écran. Afficher le logo DigiView en filigrane Liens Notre site internet Venez discuter avec nous sur Discord L\'application web DigiView Fonctionne sur Chrome et les navigateurs basés sur Chrome, même sur Android ! - Tout notre code est open-source - À Propos - Version de l\'application + Tout notre code est Open-Source + À propos + Version Copyright License Open-Source License MIT diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index aafa7b5..828f778 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -51,7 +51,7 @@ + app:summary="https://fpvout.com"> From 40106f68c94eb631b096e42992cc0556c111da9a Mon Sep 17 00:00:00 2001 From: JesseFPV <42770753+JesseFPV@users.noreply.github.com> Date: Tue, 1 Jun 2021 07:57:45 +0200 Subject: [PATCH 07/16] Add Dutch Translation --- app/src/main/res/values-nl/strings.xml | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 app/src/main/res/values-nl/strings.xml diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000..c0640c6 --- /dev/null +++ b/app/src/main/res/values-nl/strings.xml @@ -0,0 +1,44 @@ + + + DigiView + DigiView\nfeed + Verbind De Goggles + USB Apparaat Goedgekeurd + Goggles Verbinding Verbroken + Goggles Verbonden + Wachten Op Video… + Data Collection Agreement + Nee, bedankt + Ja hoor! + Deel crash-informatie met het DigiView team? + Deze app crashed waarschijnlijk minder vaak dan je drone, maar als het gebeurt willen we het graag weten! + Instellingen + + + Prestatie + Privacy + Video Preset + Standaard + Behouden + Aggresief + Legacy + Legacy Buffered + Analyse Inschakelen + Privacy Policy + Bekijk welke data we verzamelen en hoe we deze gebruiken + Video Speler + Volledig Scherm + Kan ook ingeschakeld worden door dubbel-tap of het pinchen van de videospeler. + DigiView watermerk weergeven + Links + Onze Website + Chat met ons en andere DigiView gebruikers + DigiView Web App + Werkt in Chrome en Chrome-based browsers, zelfs op Android! + Al onze code is open-source + Over + App Versie + Copyright + Open-Source License + MIT License + \ No newline at end of file From fd8e3eac483209c5515041557c2478ce1c4fe267 Mon Sep 17 00:00:00 2001 From: Olivier Mouren Date: Tue, 1 Jun 2021 09:30:46 +0200 Subject: [PATCH 08/16] Fix french translations --- app/src/main/res/values-fr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 642ccf2..e1b4fbb 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -24,7 +24,7 @@ Voyez quelles données nous recoltons et comment nous les utilisons. Lecteur vidéo Plein écran - Peut aussi être activé en double tapant ou en pincant le l\'écran. + Peut aussi être activé en double tapant ou en pinçant l\'écran. Afficher le logo DigiView en filigrane Liens Notre site internet From e797730fddfe90e8b043ecb003e8dbc8bfdbfc88 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sat, 5 Jun 2021 00:15:24 +0200 Subject: [PATCH 09/16] cleanup + add stream listener limit (won't throw error for now) --- .../java/com/fpvout/digiview/VideoReaderExoplayer.java | 4 +--- .../java/com/fpvout/digiview/VideoStreamService.java | 9 +++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index 4db9d76..8e340e6 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -85,9 +85,7 @@ public void start() { return (DataSource) new InputStreamBufferedDataSource(dataSpec, inputStream); case VIDEO_STREAM_SERVICE: default: - VideoStreamServiceDataSource v = new VideoStreamServiceDataSource(dataSpec); - mUsbMaskConnection.getVideoStreamService().addVideoStreamListener(v::onVideoStreamData); - return v; + return new VideoStreamServiceDataSource(dataSpec, mUsbMaskConnection.getVideoStreamService()); } }; diff --git a/app/src/main/java/com/fpvout/digiview/VideoStreamService.java b/app/src/main/java/com/fpvout/digiview/VideoStreamService.java index 0bd412a..ca2cfaf 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoStreamService.java +++ b/app/src/main/java/com/fpvout/digiview/VideoStreamService.java @@ -12,6 +12,7 @@ public class VideoStreamService { private static final int READ_BUFFER_SIZE = 131072; + private static final int MAX_VIDEO_STREAM_LISTENER = 10; private static final String TAG = "VideoStreamService"; private final ArrayList videoStreamListeners; InputStream videoStream; @@ -56,8 +57,12 @@ public void stop() throws InterruptedException { } public void addVideoStreamListener(VideoStreamListener listener) { - Log.d(TAG, "addVideoStreamListener"); - videoStreamListeners.add(listener); + if (videoStreamListeners.size() < MAX_VIDEO_STREAM_LISTENER) { + Log.d(TAG, "addVideoStreamListener"); + videoStreamListeners.add(listener); + } else { + Log.d(TAG, "addVideoStreamListener: Limit reached !"); + } } public void removeVideoStreamListener(VideoStreamListener listener) { From 92f39b9e46bf0dc45f4012b06414cfca0afaf8b8 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sat, 5 Jun 2021 00:43:28 +0200 Subject: [PATCH 10/16] add a ribbon to debug / alpha / beta versions. --- app/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index f360733..6e56dff 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ plugins { id 'com.android.application' + id "com.starter.easylauncher" version "4.0.0" } android { @@ -11,7 +12,7 @@ android { keyPassword digiviewKeyPassword keyAlias digiviewKeyAlias } - catch (ex) { + catch (ignored) { println("You should define mStoreFile, mStorePassword, mKeyPassword and mKeyAlias in ~/.gradle/gradle.properties : "+ ex.message) } } From 8a463606f0fa1b0ee6abaf209b872607ef1fb720 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sat, 5 Jun 2021 00:47:01 +0200 Subject: [PATCH 11/16] update .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 293d9ae..a722239 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ .externalNativeBuild .cxx local.properties +app/debug +app/alpha +app/beta +app/release From cbfebb8058c63c1e0cd2af40852b8d53b7e5e092 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sat, 5 Jun 2021 01:04:59 +0200 Subject: [PATCH 12/16] rollback for CI --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6e56dff..9f09d10 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { keyPassword digiviewKeyPassword keyAlias digiviewKeyAlias } - catch (ignored) { + catch (ex) { println("You should define mStoreFile, mStorePassword, mKeyPassword and mKeyAlias in ~/.gradle/gradle.properties : "+ ex.message) } } From 5d5fc9ae82b77b9fd1fc6267273f021c115f57cf Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sat, 5 Jun 2021 01:38:57 +0200 Subject: [PATCH 13/16] fix app crash when usb was disconnected and screen was off. --- .../main/java/com/fpvout/digiview/MainActivity.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/fpvout/digiview/MainActivity.java b/app/src/main/java/com/fpvout/digiview/MainActivity.java index af99d9e..7f4c7a4 100644 --- a/app/src/main/java/com/fpvout/digiview/MainActivity.java +++ b/app/src/main/java/com/fpvout/digiview/MainActivity.java @@ -246,7 +246,7 @@ public void usbDeviceApproved(UsbDevice device) { public void usbDeviceDetached() { Log.i(TAG, "USB - usbDevice detached"); showOverlay(R.string.usb_device_detached_waiting, OverlayStatus.Disconnected); - this.onStop(); + disconnect(); } private boolean searchDevice() { @@ -339,14 +339,17 @@ private void hideOverlay() { autoHideSettingsButton(); } + private void disconnect() { + mUsbMaskConnection.stop(); + mVideoReader.stop(); + usbConnected = false; + } + @Override protected void onStop() { super.onStop(); Log.d(TAG, "APP - On Stop"); - - mUsbMaskConnection.stop(); - mVideoReader.stop(); - usbConnected = false; + disconnect(); } @Override From 12c082c858a20f02132ba4fe571efe55d2b93452 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sat, 5 Jun 2021 02:53:18 +0200 Subject: [PATCH 14/16] follow Exoplayer recommendation for error catching, fix crash ANDROID-14 on sentry. --- .../main/java/com/fpvout/digiview/VideoReaderExoplayer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index 7c18bd0..23c795e 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -101,13 +101,13 @@ public void onPlayerError(ExoPlaybackException error) { (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); break; case ExoPlaybackException.TYPE_REMOTE: - Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getSourceException().getMessage()); + Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getMessage()); break; case ExoPlaybackException.TYPE_RENDERER: - Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getSourceException().getMessage()); + Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getRendererException().getMessage()); break; case ExoPlaybackException.TYPE_UNEXPECTED: - Log.e(TAG, "PLAYER_SOURCE - TYPE_UNEXPECTED: " + error.getSourceException().getMessage()); + Log.e(TAG, "PLAYER_SOURCE - TYPE_UNEXPECTED: " + error.getUnexpectedException().getMessage()); break; } } From 01307b5b57900f13828a9aecb545ef1e20a73aa8 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Thu, 17 Jun 2021 22:50:44 +0200 Subject: [PATCH 15/16] fix depreciations with new exoplayer version --- app/build.gradle | 2 +- .../fpvout/digiview/VideoReaderExoplayer.java | 56 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9f09d10..91434fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -69,7 +69,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - implementation 'com.google.android.exoplayer:exoplayer:2.14.0' + implementation 'com.google.android.exoplayer:exoplayer:2.14.1' implementation 'io.sentry:sentry-android:4.3.0' implementation 'androidx.preference:preference:1.1.1' diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index ef8e497..372303c 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -26,7 +26,7 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.util.NonNullApi; -import com.google.android.exoplayer2.video.VideoListener; +import com.google.android.exoplayer2.video.VideoSize; import usb.AndroidUSBInputStream; @@ -88,34 +88,34 @@ public void start() { default: return new VideoStreamServiceDataSource(dataSpec, mUsbMaskConnection.getVideoStreamService()); } - }; - - ExtractorsFactory extractorsFactory = () ->new Extractor[] {new H264Extractor(performancePreset.h264ReaderMaxSyncFrameSize, performancePreset.h264ReaderSampleTime)}; - MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory).createMediaSource(MediaItem.fromUri(Uri.EMPTY)); - mPlayer.setMediaSource(mediaSource); - - mPlayer.prepare(); - mPlayer.play(); - mPlayer.addListener(new Player.EventListener() { - @Override - @NonNullApi - public void onPlayerError(ExoPlaybackException error) { - switch (error.type) { - case ExoPlaybackException.TYPE_SOURCE: - Log.e(TAG, "PLAYER_SOURCE - TYPE_SOURCE: " + error.getSourceException().getMessage()); - (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); - break; - case ExoPlaybackException.TYPE_REMOTE: - Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getMessage()); - break; - case ExoPlaybackException.TYPE_RENDERER: - Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getRendererException().getMessage()); - break; - case ExoPlaybackException.TYPE_UNEXPECTED: - Log.e(TAG, "PLAYER_SOURCE - TYPE_UNEXPECTED: " + error.getUnexpectedException().getMessage()); - break; - } + }; + + ExtractorsFactory extractorsFactory = () -> new Extractor[]{new H264Extractor(performancePreset.h264ReaderMaxSyncFrameSize, performancePreset.h264ReaderSampleTime)}; + MediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory).createMediaSource(MediaItem.fromUri(Uri.EMPTY)); + mPlayer.setMediaSource(mediaSource); + + mPlayer.prepare(); + mPlayer.play(); + mPlayer.addListener(new Player.Listener() { + @Override + @NonNullApi + public void onPlayerError(ExoPlaybackException error) { + switch (error.type) { + case ExoPlaybackException.TYPE_SOURCE: + Log.e(TAG, "PLAYER_SOURCE - TYPE_SOURCE: " + error.getSourceException().getMessage()); + (new Handler(Looper.getMainLooper())).postDelayed(() -> restart(), 1000); + break; + case ExoPlaybackException.TYPE_REMOTE: + Log.e(TAG, "PLAYER_SOURCE - TYPE_REMOTE: " + error.getMessage()); + break; + case ExoPlaybackException.TYPE_RENDERER: + Log.e(TAG, "PLAYER_SOURCE - TYPE_RENDERER: " + error.getRendererException().getMessage()); + break; + case ExoPlaybackException.TYPE_UNEXPECTED: + Log.e(TAG, "PLAYER_SOURCE - TYPE_UNEXPECTED: " + error.getUnexpectedException().getMessage()); + break; } + } @Override public void onPlaybackStateChanged(int state) { From bc91ce4a101a70dc25bbdc424d2d77cc6661c12e Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Fri, 18 Jun 2021 00:34:18 +0200 Subject: [PATCH 16/16] [WIP] keep video service launch seperate so we can still use old methods --- .../java/com/fpvout/digiview/UsbMaskConnection.java | 5 +---- .../java/com/fpvout/digiview/VideoReaderExoplayer.java | 4 +++- .../java/com/fpvout/digiview/VideoStreamService.java | 10 ++++++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java b/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java index 3895245..57e6245 100644 --- a/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java +++ b/app/src/main/java/com/fpvout/digiview/UsbMaskConnection.java @@ -19,7 +19,6 @@ public class UsbMaskConnection { public static final String ACTION_USB_PERMISSION = "com.fpvout.digiview.USB_PERMISSION"; private static final int VENDOR_ID = 11427; private static final int PRODUCT_ID = 31; - private final byte[] magicPacket = "RMVT".getBytes(); AndroidUSBInputStream mInputStream; AndroidUSBOutputStream mOutputStream; private UsbDeviceConnection usbConnection; @@ -50,7 +49,6 @@ public static UsbDevice searchDevice(UsbManager usbManager, Context c) { } public void start(){ - mOutputStream.write(magicPacket); videoStreamService.start(); } @@ -91,8 +89,7 @@ public void setUsbDevice(UsbManager usbManager, UsbDevice d) { mOutputStream = new AndroidUSBOutputStream(usbInterface.getEndpoint(0), usbConnection); mInputStream = new AndroidUSBInputStream(usbInterface.getEndpoint(1), usbInterface.getEndpoint(0), usbConnection); - videoStreamService = new VideoStreamService(mInputStream); - start(); + videoStreamService = new VideoStreamService(mInputStream, mOutputStream); ready = true; } } diff --git a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java index 372303c..7146039 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java +++ b/app/src/main/java/com/fpvout/digiview/VideoReaderExoplayer.java @@ -86,7 +86,9 @@ public void start() { return (DataSource) new InputStreamBufferedDataSource(dataSpec, inputStream); case VIDEO_STREAM_SERVICE: default: - return new VideoStreamServiceDataSource(dataSpec, mUsbMaskConnection.getVideoStreamService()); + VideoStreamServiceDataSource v = new VideoStreamServiceDataSource(dataSpec, mUsbMaskConnection.getVideoStreamService()); + mUsbMaskConnection.start(); + return v; } }; diff --git a/app/src/main/java/com/fpvout/digiview/VideoStreamService.java b/app/src/main/java/com/fpvout/digiview/VideoStreamService.java index ca2cfaf..49795a3 100644 --- a/app/src/main/java/com/fpvout/digiview/VideoStreamService.java +++ b/app/src/main/java/com/fpvout/digiview/VideoStreamService.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; /** @@ -16,12 +17,14 @@ public class VideoStreamService { private static final String TAG = "VideoStreamService"; private final ArrayList videoStreamListeners; InputStream videoStream; + private final byte[] magicPacket = "RMVT".getBytes(); private boolean isRunning = false; private Thread streamServiceThread; + OutputStream outStream; - - public VideoStreamService(InputStream inputStream) { + public VideoStreamService(InputStream inputStream, OutputStream outputStream) { videoStream = inputStream; + outStream = outputStream; videoStreamListeners = new ArrayList<>(); } @@ -32,6 +35,7 @@ public void start() { streamServiceThread = new Thread(() -> { while (isRunning) { try { + outStream.write(magicPacket); byte[] buffer = new byte[READ_BUFFER_SIZE]; int bytesReceived = videoStream.read(buffer, 0, READ_BUFFER_SIZE); if (bytesReceived >= 0) { @@ -39,6 +43,8 @@ public void start() { for (VideoStreamListener v : videoStreamListeners) { v.onVideoStreamData(buffer, bytesReceived); } + } else { + outStream.write(magicPacket); } } catch (IOException e) { e.printStackTrace();