mpv-android
mpv-android copied to clipboard
Videos played from content uri stores and loads wrong progress position save file
Important information
Android version: 12
mpv-android version: 2022-03-23 release
Description
When loading a video document from a content provider, I often encounter situations where the player quits immediately or cannot properly read valid data from the provider because the current position exceeds the total video duration.
Or sometimes the video player automatically quits in the middle of playing, and it doesn't look like it crashes when it encounters an error. The postion when quitting is always as long as the total duration of the previous video.
- Use the following commands to generate 3 video files for testing
mkdir /sdcard/test
cd test
ffmpeg -f lavfi -i nullsrc=s=256x256:d=1800 test30min.mp4
ffmpeg -f lavfi -i nullsrc=s=256x256:d=600 test10min.mp4
ffmpeg -f lavfi -i nullsrc=s=256x256:d=180 test3min.mp4
Opening them through a traditional file browser, mpv handles the saving progress position normally, however, when opened by the content provider (ie. com.google.android.documentsui
) , mpv's handling of the save position is confusing.
Log output
Since there is too much output, I only filter key messages, and mpv doesn't output the open watch_later filename every time it plays. mpv has uid 10667 in my device
logcat -c && logcat --uid 10667|grep -C2 -E "Resolving|watch_later|'start'|Exiting"
I opened the 10-minute version first, dragged the progress to the end, exited and opened the 30-minute version, then exited and opened the 3-minute version, and finally reopened the 10-minute version.
- Below is the output
ar && logcat --uid 10667|grep -C1 -E "Resolving|watch_later|'start'|Exiting" <
05-29 04:34:27.211 17273 17273 E Toast : setGravity() shouldn't be called on text toasts, the values won't be used
05-29 04:34:27.212 17273 17273 V mpv : Resolving content URI: content://com.android.externalstorage.documents/document/primary%3Atest%2Ftest10min.mp4
05-29 04:34:27.239 17273 17273 V mpv : Display 0 reports FPS of 60.000004
--
05-29 04:34:32.033 17273 19054 V mpv : [cplayer:v] Set property: vo="null" -> 1
05-29 04:34:32.042 17273 17273 V mpv : Exiting.
05-29 04:34:37.432 17273 17688 D AppScoutStateMachine: 17273-ScoutStateMachinecreated
--
05-29 04:34:37.446 17273 17273 E Toast : setGravity() shouldn't be called on text toasts, the values won't be used
05-29 04:34:37.446 17273 17273 V mpv : Resolving content URI: content://com.android.externalstorage.documents/document/primary%3Atest%2Ftest30min.mp4
05-29 04:34:37.465 17273 17273 V mpv : Display 0 reports FPS of 60.000004
--
05-29 04:34:37.504 17273 19111 V mpv : [cplayer:info] Resuming playback. This behavior can be disabled with --no-resume-playback.
05-29 04:34:37.504 17273 19111 V mpv : [cplayer:v] Loading config '/data/user/0/is.xyz.mpv/files/watch_later/1D6DF7821937FF351A892E5977534E83'
05-29 04:34:37.504 17273 19111 V mpv : [cplayer:v] Reading config file /data/user/0/is.xyz.mpv/files/watch_later/1D6DF7821937FF351A892E5977534E83
05-29 04:34:37.504 17273 19111 V mpv : [cplayer:v] Applying profile 'default'...
05-29 04:34:37.506 17273 19111 V mpv : [cplayer:v] Setting option 'start' = '566.720000' (flags = 36)
05-29 04:34:37.506 17273 19111 V mpv : [cplayer:v] Running hook: ytdl_hook/on_load
--
05-29 04:34:39.912 17273 19111 V mpv : [cplayer:v] Set property: vo="null" -> 1
05-29 04:34:39.919 17273 17273 V mpv : Exiting.
05-29 04:34:41.370 17273 17879 D AppScoutStateMachine: 17273-ScoutStateMachinecreated
--
05-29 04:34:41.392 17273 17273 E Toast : setGravity() shouldn't be called on text toasts, the values won't be used
05-29 04:34:41.392 17273 17273 V mpv : Resolving content URI: content://com.android.externalstorage.documents/document/primary%3Atest%2Ftest3min.mp4
05-29 04:34:41.409 17273 17273 V mpv : Display 0 reports FPS of 60.000004
--
05-29 04:34:41.450 17273 19173 V mpv : [cplayer:info] Resuming playback. This behavior can be disabled with --no-resume-playback.
05-29 04:34:41.450 17273 19173 V mpv : [cplayer:v] Loading config '/data/user/0/is.xyz.mpv/files/watch_later/1D6DF7821937FF351A892E5977534E83'
05-29 04:34:41.450 17273 19173 V mpv : [cplayer:v] Reading config file /data/user/0/is.xyz.mpv/files/watch_later/1D6DF7821937FF351A892E5977534E83
05-29 04:34:41.450 17273 19173 V mpv : [cplayer:v] Applying profile 'default'...
05-29 04:34:41.450 17273 19173 V mpv : [cplayer:v] Setting option 'start' = '568.600000' (flags = 36)
05-29 04:34:41.450 17273 19173 V mpv : [cplayer:v] Running hook: ytdl_hook/on_load
--
05-29 04:34:41.931 17273 19173 V mpv : [cplayer:v] Set property: vo="null" -> 1
05-29 04:34:41.936 17273 17273 V mpv : Exiting.
05-29 04:34:43.847 17273 17879 D AppScoutStateMachine: 17273-ScoutStateMachinecreated
--
05-29 04:34:43.859 17273 17273 E Toast : setGravity() shouldn't be called on text toasts, the values won't be used
05-29 04:34:43.860 17273 17273 V mpv : Resolving content URI: content://com.android.externalstorage.documents/document/primary%3Atest%2Ftest10min.mp4
05-29 04:34:43.884 17273 17273 V mpv : Display 0 reports FPS of 60.000004
--
05-29 04:34:45.964 17273 19227 V mpv : [cplayer:v] Set property: vo="null" -> 1
05-29 04:34:45.973 17273 17273 V mpv : Exiting.
When the 30-minute version is opened, the progress automatically jumps to a position close to 10 minutes. It is not the playback progress of the 10-minute version, but very close. When the 3-minute version is opened, because the progress position exceeds the video duration, it immediately exits and resets the saved progress postion.
Additional information
When the document content provider obtains data from the network, mpv does not always exit immediately when attempting to read a video from a position that exceeds its total duration. You can see that mpv-android displays the total duration of the previous video in current interface in this situation. And if the previous video is shorter than the current one, then mpv may quit when it reaches the progress of the total duration of the previous video, but this is not easy to reproduce and report, and the source of the problem should be the same as above.
This should probably be broken down into two problems, namely that mpv does not correctly generate a watch_later file corresponding to the unique content uri, and the total duration displayed on the interface and something deeper will be preferentially affected by factors other than the metadata of the current video itself.
This is an issue but not easy to fix because mpv can't see the actual URIs.
This is an issue but not easy to fix because mpv can't see the actual URIs.
For mpv-android, although the file strict path itself cannot be parsed, at least we can see the whole picture of the content uri, which probably contains the file name, and is expected to be unique.
But this information is lost when the fd is submitted to libmpv for processing, leaving only the file descriptor.
The fd protocol of mpv does not contain a real file name, it‘s stream structure only contains a fd number cropped from url as path, which cannot be used to uniquely identify a video. It seems reasonable. So the question is, can we figure out a way to pass the real path to mpv to make it not only handle saving the playback progress correctly, but also display the filename as the title of the video?
It seems easy to deal with the latter only by send a set_property
command, but that doesn't help the former, and the latter will be solved naturally after the former is dealt with. I don't know, maybe a new private uri protocol support could be added to mpv's stream_file
?
It's possible to let mpv see the original URI via hooks. See here for a very old implementation: https://github.com/mpv-android/mpv-android/commit/d6af5eabd4ab35289f87b6a00a169c9a77c48e41
Yes, there may still be a way to see the path of the file pointed to by the content link, but this will only get harder as the Android system version is upgraded, not to mention the case where the file itself does not exist in the local storage, then see the original link doesn't help either.
Now that the mandatory popularization of content providers has become an inevitable trend, and there are indeed benefits in a sense, I think providing better support for content provider links rather than trying to bypass restrictions on conversions should also be considered.
At present, the processing of content link is to open the file to get the fd and then pass the fd to mpv. What I want to say is, why not pass the other information extracted from the uri at the same time?
For example, design a new uri protocol such like this:
contentfd://<fd>/<flags ie. close=8, with orgin extenstion=4, encoded=2, hashed=1>/<uri_decoded_without_protocol_not_after_last_:_or_/_in_base64_or md5>/<uri_docoded_after_last_:_or_/_as_filename>.<origin_extension_or_from_mimetype_if_need>
If a content link was opened in mpv-android as fd=85, with orginal extension and uri not need to be hasded (too long or be protected away form lua script), like this:
content://com.android.externalstorage.documents/document/primary%3Atest%2Ftest3min.mp4
Considering possible compatibility issues, the front part of the path was base64 encoded, Then libmpv will see this:
contentfd://85/14/Y29tLmFuZHJvaWQuZXh0ZXJuYWxzdG9yYWdlLmRvY3VtZW50cy9kb2N1bWVudC9wcmltYXJ5Og==/test/test3min.mp4
Then add code to stream_file
to process it, extract the path and filename, they were previously set to null, now they can make sense.
I'm not sure if this will work, just an idea. When mpv streams the link of the http protocol, it will take out the part after the protocol header as the path, and use the processed path when saving and restoring the playback status and options, and obviously the path of http does not really exist on the local storage, so I think just manually specifying the path to the stream will most likely work.
The above idea has not been carefully thought out, and there are many omissions, except that it is uncertain whether there is other logic coupled with the path and file name when mpv is streaming, it is also possible that there is no file name in the content uri. Perhaps the display name should be directly obtained as the file name, and then the whole original content uri should be encoded or hashed as the path.
Opening the legacy file picker will ask for those permissions, that it fixes this case is a coincidence (sort of).
The real solution is making mpv deal with content URI loaded paths correctly.
Wait, I've just read the mpv documentation and it looks like
watch-later
indexes are already based on file'smd5
checksum.
Are you sure? I didn't find such a description in the mpv manual. Also, this doesn’t seem to make sense. To calculate md5sum, you need to read the file completely first, which is a huge IO overhead. Moreover, the URI does not necessarily point to a local file, and may even point to a live stream that does not have a clear length.
As of the last time I read the MPV source code, MPV logs watch-later files based on file paths or URLs, and has special handling of http protocol URLs to ignore their host/domain name parts. Attach the filename to fdclose://
As for your mention of generating watch-later based on file md5, I think this may be a misunderstanding. The watch-later file name itself is the md5sum of the string path or URL of the video, not the md5sum of the video content itself.
Try clearing out all the orphaned watch later files. https://github.com/mpv-android/mpv-android/pull/715