diff --git a/.idea/misc.xml b/.idea/misc.xml index 10e58bc..b82c61c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,6 +3,7 @@ diff --git a/app/build.gradle b/app/build.gradle index 63dacf8..8107eca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,6 +13,8 @@ android { versionCode 1 versionName "1.0" + vectorDrawables.useSupportLibrary = true + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1503e80..cfae129 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,7 @@ diff --git a/app/src/main/java/it/danieleverducci/ojo/ui/MainActivity.java b/app/src/main/java/it/danieleverducci/ojo/ui/MainActivity.java index 0ac34d9..4b9e923 100644 --- a/app/src/main/java/it/danieleverducci/ojo/ui/MainActivity.java +++ b/app/src/main/java/it/danieleverducci/ojo/ui/MainActivity.java @@ -21,7 +21,6 @@ import android.view.MenuItem; public class MainActivity extends AppCompatActivity { - private AppBarConfiguration appBarConfiguration; private ActivityMainBinding binding; @Override @@ -31,11 +30,7 @@ public class MainActivity extends AppCompatActivity { binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - setSupportActionBar(binding.toolbar); - NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); - appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build(); - NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); binding.fab.setOnClickListener(new View.OnClickListener() { @Override @@ -45,33 +40,4 @@ public class MainActivity extends AppCompatActivity { } }); } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_settings) { - return true; - } - - return super.onOptionsItemSelected(item); - } - - @Override - public boolean onSupportNavigateUp() { - NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main); - return NavigationUI.navigateUp(navController, appBarConfiguration) - || super.onSupportNavigateUp(); - } } \ No newline at end of file diff --git a/app/src/main/java/it/danieleverducci/ojo/ui/SurveillanceFragment.java b/app/src/main/java/it/danieleverducci/ojo/ui/SurveillanceFragment.java index fc232cc..3c8f974 100644 --- a/app/src/main/java/it/danieleverducci/ojo/ui/SurveillanceFragment.java +++ b/app/src/main/java/it/danieleverducci/ojo/ui/SurveillanceFragment.java @@ -24,6 +24,8 @@ import org.videolan.libvlc.Media; import org.videolan.libvlc.MediaPlayer; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import it.danieleverducci.ojo.R; import it.danieleverducci.ojo.databinding.FragmentSurveillanceBinding; @@ -31,9 +33,18 @@ import it.danieleverducci.ojo.entities.Camera; public class SurveillanceFragment extends Fragment implements MediaPlayer.EventListener, IVLCVout.Callback { - private FragmentSurveillanceBinding binding; + final static private String[] VLC_OPTIONS = new String[]{ + "--aout=opensles", + "--audio-time-stretch", // time stretching + "-vvv", // verbosity + "--aout=opensles", + "--avcodec-codec=h264", + "--file-logging", + "--logfile=vlc-log.txt" + }; - private CameraView cameraView; + private FragmentSurveillanceBinding binding; + private List cameraViews = new ArrayList<>(); @Override public View onCreateView( @@ -59,30 +70,53 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL // Start stream on surface view 1 + + + List cc = new ArrayList<>(); String rtspUrl = "rtsp://admin:Du4gdtPmXGCWT29@192.168.1.200:554/Streaming/Channels/101"; Camera camera = new Camera("Camera 1", rtspUrl); + cc.add(camera); + rtspUrl = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"; + camera = new Camera("Bunny", rtspUrl); + cc.add(camera); + rtspUrl = "rtsp://demo:demo@ipvmdemo.dyndns.org:5541/onvif-media/media.amp?profile=profile_1_h264&sessiontimeout=60&streamtype=unicast"; + camera = new Camera("Ipvm Demo", rtspUrl); + cc.add(camera); - ArrayList options = new ArrayList(); - options.add("--aout=opensles"); - options.add("--audio-time-stretch"); // time stretching - options.add("-vvv"); // verbosity - options.add("--aout=opensles"); - options.add("--avcodec-codec=h264"); - options.add("--file-logging"); - options.add("--logfile=vlc-log.txt"); - LibVLC libvlc = new LibVLC(getContext(), options); + int elemsPerSide = calcGridSideElements(cc.size()); + int camIdx = 0; + for (int r = 0; r < elemsPerSide; r++) { + // Create row and add to row container + LinearLayout row = new LinearLayout(getContext()); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + 0, + 1.0f + ); + binding.gridRowContainer.addView(row, params); + // Add camera viewers to the row + for (int c = 0; c < elemsPerSide; c++) { + if ( camIdx < cc.size() ) { + Camera cam = cc.get(camIdx); + addCameraView(cam, row); + } else { + // Cameras are less than the maximum number of cells in grid: fill remaining cells with empty views + View ev = new View(getContext()); + ev.setBackgroundColor(Color.BLACK); + LinearLayout.LayoutParams evParams = new LinearLayout.LayoutParams( + 0, + LinearLayout.LayoutParams.MATCH_PARENT, + 1.0f + ); + row.addView(ev, evParams); + } + camIdx++; + } + } + - cameraView = new CameraView( - getContext(), - libvlc, - camera, - this - ); - // Add to layout - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(1000, 500); - binding.gridRowContainer.addView(cameraView.surfaceView, params); @@ -92,7 +126,27 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL public void onStart() { super.onStart(); - cameraView.startPlayback(); + for (CameraView cv : cameraViews) { + cv.startPlayback(); + } + } + + @Override + public void onPause() { + super.onPause(); + + for (CameraView cv : cameraViews) { + cv.pausePlayback(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + + for (CameraView cv : cameraViews) { + cv.destroy(); + } } @Override @@ -122,21 +176,50 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL public void onSurfacesDestroyed(IVLCVout vlcVout) { } - - private void addCameraView(Camera camera) { - + + private void addCameraView(Camera camera, LinearLayout rowContainer) { + CameraView cv = new CameraView( + getContext(), + camera, + this + ); + + // Add to layout + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + 0, + LinearLayout.LayoutParams.MATCH_PARENT, + 1.0f + ); + rowContainer.addView(cv.surfaceView, params); + + cameraViews.add(cv); } - + + /** + * Returns the number of elements per side needed to create a grid that can contain the provided elements number. + * Es: to display 3 elements is needed a 4-element grid, with 2 elements per side (a 2x2 grid) + * Es: to display 7 elements is needed a 9-element grid, with 3 elements per side (a 3x3 grid) + * @param elements + */ + private int calcGridSideElements(int elements) { + return (int)(Math.ceil(Math.sqrt(elements))); + } + + /** + * Contains all entities (views and java entities) related to a camera stream viewer + */ private class CameraView { protected SurfaceView surfaceView; protected MediaPlayer mediaPlayer; protected IVLCVout ivlcVout; protected Camera camera; protected LibVLC libvlc; + protected IVLCVout.Callback callback; - public CameraView(Context context, LibVLC libvlc, Camera camera, IVLCVout.Callback callback) { + public CameraView(Context context, Camera camera, IVLCVout.Callback callback) { this.camera = camera; - this.libvlc = libvlc; + this.callback = callback; + this.libvlc = new LibVLC(context, new ArrayList<>(Arrays.asList(VLC_OPTIONS))); surfaceView = new SurfaceView(context); SurfaceHolder holder = surfaceView.getHolder(); @@ -150,7 +233,7 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL // Set up video output ivlcVout = mediaPlayer.getVLCVout(); ivlcVout.setVideoView(surfaceView); - ivlcVout.addCallback(callback); + ivlcVout.addCallback(this.callback); ivlcVout.attachViews(); // Load media and start playing @@ -167,10 +250,28 @@ public class SurveillanceFragment extends Fragment implements MediaPlayer.EventL /** * Starts the playback. - * This must be called after the view has been laid out */ public void startPlayback() { mediaPlayer.play(); } + + /** + * Pauses the playback. + */ + public void pausePlayback() { + mediaPlayer.pause(); + } + + /** + * Destroys the object and frees the memory + */ + public void destroy() { + mediaPlayer.stop(); + final IVLCVout vout = mediaPlayer.getVLCVout(); + vout.removeCallback(this.callback); + vout.detachViews(); + libvlc.release(); + libvlc = null; + } } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add_camera.xml b/app/src/main/res/drawable/ic_add_camera.xml new file mode 100644 index 0000000..b4f90ff --- /dev/null +++ b/app/src/main/res/drawable/ic_add_camera.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index db42829..cdcd3af 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,20 +6,6 @@ android:layout_height="match_parent" tools:context=".ui.MainActivity"> - - - - - - + app:srcCompat="@drawable/ic_add_camera" /> \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml deleted file mode 100644 index 6d17fd8..0000000 --- a/app/src/main/res/menu/menu_main.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 146c89c..c412309 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,6 +1,8 @@ - - -