2

I'm having trouble setting the subtitles location when starting the VideoPlayerActivity of VLC for Android. I am targeting API 27 and using a FileProvider to allow access to files.

According to the documentation here, if you set the, "subtitles_location" extra, then you can provide the path of the subtitles file. Unfortunately, I can't seem to get this to work.

I'm seeing that the, "Subtitles" menu item remains grayed out and downloading subtitles doesn't seem to change this state. When tapping, "Select subtitle file" VLC states there are no subtitle files in the directory and the URI is prefixed with, "content://" as expected.

Both the video file and subtitles file share the same parent directory and are provided using FileProvider.

How can I set the subtitles file when targeting API >= 24?

Update:

  • It seems that when tapping the subtitle selection menu item, it uses the parent directory of the video file. This is seen here.
  • Figured it out, see solution below.

Below are some code snippets of how I'm starting the VideoPlayerActivity through an intent.

Play Video Method:

/**
 * Play a video using VLC media player. This method will play media by using
 * the VLC VideoPlayerActivity.
 *
 * @param activity         The current activity.
 * @param authority        The FileProvider authority.
 * @param vlcPackageName   The VLC package name.
 * @param vlcActivityName  The VLC activity name.
 * @param videoFile        The video file to play.
 * @param subtitlesFile    The subtitles file to play.
 * @param playbackPosition The playback position.
 * @param requestCode      The activity for result request code.
 */
public static void playVideo(
        AppCompatActivity activity,
        String authority,
        String vlcPackageName,
        String vlcActivityName,
        String vlcTitleExtra,
        String vlcPositionExtra,
        String vlcFromStartExtra,
        String vlcSubtitlesExtra,
        File videoFile,
        File subtitlesFile,
        long playbackPosition,
        int requestCode
) {
    Intent intent = new Intent(Intent.ACTION_VIEW);

    intent.setPackage(vlcPackageName);

    // Set component to VLC video player activity.
    intent.setComponent(new ComponentName(
            vlcPackageName,
            vlcActivityName
    ));

    // Set intent permission flags.
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    // Set title and playback position.
    intent.putExtra(vlcTitleExtra, videoFile.getName());
    intent.putExtra(vlcPositionExtra, playbackPosition);
    intent.putExtra(vlcFromStartExtra, false);

    // Set video file location
    intent.setDataAndTypeAndNormalize(
            FileProvider.getUriForFile(
                    activity,
                    authority,
                    videoFile
            ),
            "video/*"
    );

    // Subtitles file provided, set location on intent.
    if (subtitlesFile != null) {
        intent.putExtra(
                vlcSubtitlesExtra,
                FileProvider.getUriForFile(
                        activity,
                        authority,
                        subtitlesFile
                )
        );
    }

    activity.startActivityForResult(intent, requestCode);
}

Provider Definition in Manifest

   <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.masterwok.bitcast.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true" >
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths" />
    </provider>

@xml/paths Definition:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="Download"
        path="Download/"/>
</paths>
masterwok
  • 4,868
  • 4
  • 34
  • 41

1 Answers1

1

I was able to solve this by using the undocumented, "item_location" extra defined: here.

This gets around the permission issue when targeting API >= 24. What was happening is the VideoPlayerActivity was trying to read the parent directory of the URI provided using the FileProvider. If I had to guess, I'd say Android doesn't allow this.

By passing the URI using the, "item_location" extra, we can bypass this restriction.

The method I use to play a video file is now the following:

/**
 * Play a video using VLC media player. This method will play media by using
 * the VLC VideoPlayerActivity.
 *
 * @param activity         The current activity.
 * @param vlcPackageName   The VLC package name.
 * @param vlcActivityName  The VLC activity name.
 * @param videoFile        The video file to play.
 * @param playbackPosition The playback position.
 * @param requestCode      The activity for result request code.
 */
public static void playVideo(
        AppCompatActivity activity,
        String vlcPackageName,
        String vlcActivityName,
        String vlcItemLocationExtra,
        String vlcTitleExtra,
        String vlcPositionExtra,
        String vlcFromStartExtra,
        File videoFile,
        long playbackPosition,
        int requestCode
) {
    Intent intent = new Intent(Intent.ACTION_VIEW);

    intent.setPackage(vlcPackageName);

    // Set component to VLC video player activity.
    intent.setComponent(new ComponentName(
            vlcPackageName,
            vlcActivityName
    ));

    // Set intent permission flags.
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    // Set title and playback position.
    intent.putExtra(vlcTitleExtra, videoFile.getName());
    intent.putExtra(vlcPositionExtra, playbackPosition);
    intent.putExtra(vlcFromStartExtra, false);

    // Set video file location (subtitles are found by VLC by using parent directory).
    intent.putExtra(vlcItemLocationExtra, Uri.fromFile(videoFile));

    activity.startActivityForResult(intent, requestCode);
}
masterwok
  • 4,868
  • 4
  • 34
  • 41
  • What if you want to pass the subtitle with the intent? In the solution you provided, user still need to select subtitle file inside VLC – mahdi Apr 01 '20 at 10:53
  • I couldn't make this work. what do you mean by VLC video player activity in `setComponent`? – mahdi Apr 01 '20 at 20:03
  • 1
    Hey @mahdi, it's been awhile since I've looked at this code. The use `setComponent` is used to start the media player activity directly. This is outlined in the documentation https://wiki.videolan.org/Android_Player_Intents#Extras_.28optional.29 – masterwok Apr 01 '20 at 20:08
  • 1
    As far as your first question, I don't see any way in the intent to specificity selecting the subtitles on behalf of the user. The possible intent values are under the `MediaPlayerActivity` comment here: https://code.videolan.org/videolan/vlc-android/-/blob/master/application/resources/src/main/java/org/videolan/resources/Constants.kt – masterwok Apr 01 '20 at 20:15
  • `item_location` may have only worked for my use case because my subtitles were in the same directory as the video file. I'm not sure though as I've been away from this code base for quite some time. Best of luck! – masterwok Apr 01 '20 at 20:17
  • @masterwork I've been really occupied by the problem. so I didn't notice that line in the documentation. Thank you for your help. It's really frustrating that subtitles don't work when you pass it like the documentation. I'll try to reach VLC dev team to find a way. Anyway, Thank you for your kind reply! – mahdi Apr 01 '20 at 20:26