From 518a325583b511b6533b26c7437bfa1c05a31817 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sun, 23 May 2021 00:33:19 +0200 Subject: [PATCH 01/11] 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/11] 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/11] 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/11] 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/11] 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 e797730fddfe90e8b043ecb003e8dbc8bfdbfc88 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Sat, 5 Jun 2021 00:15:24 +0200 Subject: [PATCH 06/11] 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 07/11] 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 08/11] 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 09/11] 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 01307b5b57900f13828a9aecb545ef1e20a73aa8 Mon Sep 17 00:00:00 2001 From: Jimmy Lucidarme Date: Thu, 17 Jun 2021 22:50:44 +0200 Subject: [PATCH 10/11] 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 11/11] [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();