Error with LibVLC on Android 13: Unable to open the MRL when playing MP4 video from internal storage

31 Views Asked by At

I'm currently developing an Android application using LibVLC for video playback. However, I'm encountering an issue where LibVLC is unable to open the media resource locator (MRL) when attempting to play an MP4 video from internal storage.

I'm using Android 13 as the target SDK. Here's the relevant code snippet:

// Code snippet

import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.view.WindowManager;

import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.MediaPlayer;
import org.videolan.libvlc.interfaces.IMedia;
import org.videolan.libvlc.interfaces.IVLCVout;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import org.videolan.libvlc.Media;


public class MainActivity extends AppCompatActivity {
    private SurfaceView surfaceView;

    private LibVLC libVLC;
    private org.videolan.libvlc.MediaPlayer mediaPlayer;
    private Button browseButton;

    private static final int PERMISSION_REQUEST_CODE = 1;
    private static final int PICK_VIDEO_REQUEST_CODE = 2;
    private static WeakReference<MainActivity> instance;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        instance = new WeakReference<>(this);

        setContentView(R.layout.activity_main);
        checkPermissions(); // Call checkPermissions() in onCreate()

        // Prevent screenshots while the video is playing
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);

        // Check if USB debugging is enabled
        // if (isUsbDebuggingEnabled()) {
        //     showUsbDebuggingDialog();
        //  }

        if (RootChecker.isDeviceRooted()) {
            showRootedDeviceDialog();
            return; // Exit the onCreate method if the device is rooted
        }

        // Check if the app is running on an emulator
        if (EmulatorDetector.isEmulator()) {
            showEmulatorDetectedDialog();
            return; // Exit the onCreate method if running on an emulator
        }

        // Check app integrity
        //   if (!AppIntegrityChecker.isSignatureValid(this)) {
        // Show error dialog and redirect to Play Store
        //      showAppCorruptDialog();
        //       return; // Exit the onCreate method if app integrity check fails
        //   }

        // Initialize VLC
        ArrayList<String> options = new ArrayList<>();
        options.add("--aout=opensles");
        options.add("--audio-time-stretch"); // time stretching
        options.add("-vvv"); // verbosity
        libVLC = new LibVLC(this, options);

        // Initialize media player
        mediaPlayer = new MediaPlayer(libVLC);
        mediaPlayer.setEventListener(event -> {
            switch (event.type) {
                case MediaPlayer.Event.EndReached:
                    // Handle the end of media playback
                    break;
            }
        });

        // Initialize player view
        surfaceView = findViewById(R.id.surface_view);
        IVLCVout vout = mediaPlayer.getVLCVout();
        vout.setVideoView(surfaceView);
        vout.attachViews();

        // Set up browse button click listener
        browseButton = findViewById(R.id.browse_button);
        browseButton.setOnClickListener(v -> pickVideo());
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_open:
                // Launch file picker activity to select video file
                pickVideo();
                return true;
            case R.id.action_add:
                // Handle "Add License" menu item click
                openAddLicenseActivity();
                return true;
            case R.id.action_edit:
                // Handle "Edit License" menu item click
                openEditLicenseActivity();
                return true;
            case R.id.action_exit:
                // Handle "Exit" menu item click
                finish();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    private void openAddLicenseActivity() {
        Intent intent = new Intent(this, AddLicense.class);
        startActivity(intent);
    }

    private void openEditLicenseActivity() {
        Intent intent = new Intent(this, EditLicense.class);
        startActivity(intent);
    }




    @Override
    protected void onResume() {
        super.onResume();
        checkPermissions(); // Call checkPermissions() in onResume() too
    }

    private void checkPermissions() {
        String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
        if (!arePermissionsGranted(permissions)) {
            ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
        }
    }

    private void showAppCorruptDialog() {
        new AlertDialog.Builder(this)
                .setTitle("App Corrupted")
                .setMessage("Please download the app from the Google Play Store.")
                .setPositiveButton("OK", (dialog, which) -> redirectToPlayStore())
                .setCancelable(false)
                .show();
    }

    private void redirectToPlayStore() {
        try {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.dedseec")));
        } catch (android.content.ActivityNotFoundException e) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.dedseec")));
        }
        finish(); // Close the current activity
    }


    private void showRootedDeviceDialog() {
        new AlertDialog.Builder(this)
                .setTitle("Rooted Device Detected")
                .setMessage("This app cannot be used on rooted devices.")
                .setPositiveButton("Exit", (dialog, which) -> finish())
                .setCancelable(false)
                .show();
    }


    private void showEmulatorDetectedDialog() {
        new AlertDialog.Builder(this)
                .setTitle("Emulator Detected")
                .setMessage("This app cannot be used on emulators.")
                .setPositiveButton("Exit", (dialog, which) -> finish())
                .setCancelable(false)
                .show();
    }
    private void pickVideo() {
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("video/*");
        startActivityForResult(intent, PICK_VIDEO_REQUEST_CODE);
    }

    private void onVideoPicked(Uri videoUri) {
        if (videoUri != null) {
            playVideo(videoUri);
        }
    }


    private void playVideo(Uri videoUri) {
        // Create media object
        Media media = new Media(libVLC, videoUri);

        // Set media to the media player
        mediaPlayer.setMedia(media);

        // Start playback
        mediaPlayer.play();
    }




    // Method to get the context of MainActivity
    public static Context getContext() {
        return instance().getApplicationContext();
    }

    // Method to read data from a file
    public static String readFromFile(File file) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            int size = fis.available();
            byte[] buffer = new byte[size];
            fis.read(buffer);
            return new String(buffer);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // Method to write data to a file
    public static void writeToFile(File file, String data) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            fos.write(data.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // Helper method to get the instance of MainActivity
    private static MainActivity instance() {
        return instance != null ? instance.get() : null;
    }



    private boolean isUsbDebuggingEnabled() {
        int adbEnabled = Settings.Global.getInt(getContentResolver(), Settings.Global.ADB_ENABLED, 0);
        return adbEnabled == 1;
    }

    private void showUsbDebuggingDialog() {
        new AlertDialog.Builder(this)
                .setTitle("USB Debugging Enabled")
                .setMessage("Please disable USB debugging to continue using the app.")
                .setPositiveButton("OK", (dialog, which) -> finish())
                .setCancelable(false)
                .show();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == PERMISSION_REQUEST_CODE) {
            for (int i = 0; i < permissions.length; i++) {
                String permission = permissions[i];
                if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                    // Permission granted, proceed with your action
                } else {
                    // Permission denied, handle accordingly (e.g., show a message)
                    Toast.makeText(this, permission + " permission denied", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PICK_VIDEO_REQUEST_CODE && resultCode == RESULT_OK && data != null) {
            Uri videoUri = data.getData();
            if (videoUri != null) {
                if (isDedFile(videoUri)) {
                    onVideoPicked(videoUri);
                } else {
                    // Show error message for invalid file format
                    Toast.makeText(this, "Invalid file format. Please select a .ded file.", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }

    private boolean isDedFile(Uri uri) {
        // Always return true to allow playing any video file
        return true;
    }

    private String getFileExtension(Uri uri) {
        // Get the file path from the URI
        String filePath = getFilePathFromUri(uri);
        // Extract the file extension
        if (filePath != null && !filePath.isEmpty()) {
            int lastDotIndex = filePath.lastIndexOf('.');
            if (lastDotIndex != -1) {
                return filePath.substring(lastDotIndex + 1);
            }
        }
        return null;
    }

    private String getFilePathFromUri(Uri uri) {
        String filePath = null;
        if (uri != null) {
            Cursor cursor = null;
            try {
                cursor = getContentResolver().query(uri, null, null, null, null);
                if (cursor != null && cursor.moveToFirst()) {
                    filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        return filePath;
    }

    private boolean arePermissionsGranted(String[] permissions) {
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    private byte[] decrypt(byte[] value, String key) {
        try {
            return Decryptor.decrypt(value, key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mediaPlayer.release();
        libVLC.release();
    }
}


The error message I'm receiving is:

libvlc input: VLC is unable to open the MRL 'content://com.android.externalstorage.documents/document/WA0033.mp4'.


0

There are 0 best solutions below