Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot play local files with hash character ('#') in filename #6470

Closed
bueeler opened this issue Sep 25, 2019 · 4 comments
Closed

Cannot play local files with hash character ('#') in filename #6470

bueeler opened this issue Sep 25, 2019 · 4 comments
Assignees

Comments

@bueeler
Copy link

bueeler commented Sep 25, 2019

When a media source is created with a file containing hash character ('#') in filename, this file cannot be played back by ExoPlayer, as the URI Object will not provide full path in the function "uri.getPath()" used by FileDataSource.

Media source is created as follows:
MediaSource videoSource = new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse("/storage/emulated/0/Movies/Movie#01.mp4"));

then exception is thrown as follows:

2019-09-25 11:04:51.325 5322-5322/com.anonymous E/EventLogger: internalError [1.04, 0.00, window=0, period=0, loadError]
    com.google.android.exoplayer2.upstream.FileDataSource$FileDataSourceException: java.io.FileNotFoundException: /storage/emulated/0/Movies/Movie : open failed: ENOENT (No such file or directory)
        at com.google.android.exoplayer2.upstream.FileDataSource.open(FileDataSource.java:73)
        at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:257)
        at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:83)
        at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:938)
        at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:394)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)
     Caused by: java.io.FileNotFoundException: /storage/emulated/0/Movies/Movie : open failed: ENOENT (No such file or directory)
        at libcore.io.IoBridge.open(IoBridge.java:485)
        at java.io.RandomAccessFile.<init>(RandomAccessFile.java:288)
        at java.io.RandomAccessFile.<init>(RandomAccessFile.java:151)
        at com.google.android.exoplayer2.upstream.FileDataSource.open(FileDataSource.java:65)
        at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:257) 
        at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:83) 
        at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:938) 
        at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:394) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 
     Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
        at libcore.io.Linux.open(Native Method)
        at libcore.io.BlockGuardOs.open(BlockGuardOs.java:210)
        at libcore.io.IoBridge.open(IoBridge.java:471)
        at java.io.RandomAccessFile.<init>(RandomAccessFile.java:288) 
        at java.io.RandomAccessFile.<init>(RandomAccessFile.java:151) 
        at com.google.android.exoplayer2.upstream.FileDataSource.open(FileDataSource.java:65) 
        at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:257) 
        at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:83) 
        at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:938) 
        at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:394) 
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
        at java.lang.Thread.run(Thread.java:764) 

Can this be fixed by ExoPlayer or should I avoid filenames having "# character?

@icbaker icbaker self-assigned this Sep 25, 2019
@icbaker
Copy link
Collaborator

icbaker commented Sep 25, 2019

Looks like the '#' is being interpreted as the start of the fragment of the Uri, so when we call getPath here we only get everything before the #.

I've reproduced this. I'm not sure exactly the best way to fix it, but I've got some ideas. In the mean-time, I found that percent-encoding the '#' worked. So in your case you could do:

Uri.parse("/storage/emulated/0/Movies/Movie%2301.mp4")

And I think you can automate this with URLEncoder (not tested):

Uri.parse(URLEncoder.encode(
    "/storage/emulated/0/Movies/Movie#01.mp4"), 
    StandardCharsets.UTF_8.name())

@icbaker
Copy link
Collaborator

icbaker commented Sep 25, 2019

Actually I think either of these are better (though neither are tested):

createMediaSource(
    (new Uri.Builder()).path("/storage/emulated/0/Movies/Movie#01.mp4").build());

Or:

createMediaSource(Uri.fromFile(new File("/storage/emulated/0/Movies/Movie#01.mp4")));

I'll look into:

  • Making the error message clearer in this case ("can't use a local-file Uri with a fragment")
  • Documenting more clearly how to open local files more safely using Uri.Builder or File as above

@bueeler
Copy link
Author

bueeler commented Sep 26, 2019

Thanks a lot for having a look at this issue.
Both proposed solutions work for me. So improved error message and/or updated documentation would help others not running into this issue.

@ojw28
Copy link
Contributor

ojw28 commented Sep 26, 2019

@icbaker - WDYT about having FileDataSource reconstruct the correct file path from the Uri (by looking at the fragment) to handle this case?

I don't think documenting something that's come up once in the project's existence (and is a general Android question rather than anything specific to ExoPlayer) is ideal. If we documented all such questions, our documentation would probably be a mess and it would be hard to find the actual useful bits anyway. Throwing an improved error message seems preferable, but if we can detect the case accurately enough to do that, can we not just transparently "fix" it?

ojw28 pushed a commit that referenced this issue Oct 2, 2019
…r query

This doesn't change the current behaviour, just adds a clear error message
to the developer with instructions on how to avoid it.

Issue:#6470
PiperOrigin-RevId: 272405556
@ojw28 ojw28 closed this as completed Oct 2, 2019
@google google locked and limited conversation to collaborators Dec 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants