Unable to Set Subtitles with VLC for Android

2019-07-26 04:35发布

问题:

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>

回答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);
}