Hello, I try to play video from Android filesystem.
My video file is situated here:
/storage/emulated/0/Download/myfile.mp4
How to set this path to source of Video component?
The README only tells us how to play videos from bundled assets with "require()" but not how to play files from local filesystem beyond bundled files. I tried to play a video file that was downloaded from the Internet with react-native-fs into the application caches directory (and also document directory and also external storage).
While it works pretty fine on iOS, it constantly fails on Android in every possible location.
I've used the plain path as is ("/data/data/..../file.mp4") as well as with the prefix "file:///data/data...."
While debugging the native android code i found that "mPlayer.prepare()" will raise an exception "Prepare failed: status 0x1"
On Stackoverflow is an interesting answer for that issue stating that it will fail on missing permissions because the MediaPlayer for Android comes from another application that is not allowed to access the folders of our applications.
Maybe i could give some hints. I will investigate it further, but i am not that familiar with android. Would be fine if others would join.
"adb logcat" confirms that this is an permission error:
E/ ( 2578): Failed to open file '/storage/emulated/0/e4b11695dbca0e8368d20029d77b1c44c36484cfd10300302c203b498d4d67ffh.mp4'. (Permission denied)
I've finally managed it, using the prefix "file://" (only on android, not on iOS)
It worked for me @itinance!
I was only able to get local files working on IOS using the master branch (1.1) rather than the released (1.0) version. Something evidently got fixed since then but has not as of this date been published. Oddly this worked in 0.90.
Using
<Video source={{ uri: 'file:///Users/altay/Library/Developer/CoreSimulator/Devices/5341ED49-5A69-4C2D-A0EB-E6E37B0942F3/data/Containers/Data/Application/148A9DF5-C2D4-4254-96D9-6461C8FA68CD/Documents/DEokxwCVwAQ5VFC.mp4' }} />
results with
{
error: {
code: -11800,
domain: AVFoundationErrorDomain,
},
target: 308,
}
on iOS simulator, iPhone 6s (10.3)
Im getting this error on my iPhone 7 using "react-native-video": "^2.0.0"
<Video source={{ uri: 'file:///var/mobile/Containers/Data/Application/1DJDK38329/Library/Caches/video-cache' }} />
{
error: {
code: -11828, domain: 'AVFoundationErrorDomain'
},
target: 6
}
same error here
@freddiecabrera, @tonyzonghui are you guys using react-native-fetch-blob for downloading these files? switching to react-native-fs resolved this problem for me.
@altayaydemir Thanks for the reply! I am using react-native-fetch-blob, so I will give react-native-fs a try now and I will confirm.
@altayaydemir I still get the same error using react-native-fs do you have an example I can look at? Maybe I am doing something wrong.
I have the same error too, with rn-video and rn-fetch-blob.
With my path:
file:///Users/monkey/Library/Developer/CoreSimulator/Devices/14656CF0-2A1F-4341-9B2A-786A2A5CB8B2/data/Containers/Data/Application/E94BDB21-2D64-4311-8C33-1F5CA62D86E0/Documents/voicebooks/471/3788.mp3
And error:
javascript
{ error: { code: -1100, domain: 'NSURLErrorDomain' }, target: 369 }
3788.mp3?
mp3 as far as I understand is an audio format.
Yes, I'm very surprised about it :))
I tried using react-native-video for playing music, and it's awesome.
+1 Please write an example. Thank you
<Video
source={{ uri: '/Users/monkey/Library/Developer/CoreSimulator/Devices/14656CF0-2A1F-4341-9B2A-786A2A5CB8B2/data/Containers/Data/Application/41BF6C2C-F56C-476C-822B-059B5740E5A3/Documents/voicebooks/402/2808.mp3' }} // Can be a URL or a local file.
ref={(ref) => {
this.player = ref;
}} // Store reference
paused={!this.state.play} // Pauses playback entirely.
repeat = {false} // Repeat forever.
playInBackground // Audio continues to play when app entering background.
playWhenInactive // [iOS] Video continues to play when control or notification center are shown.
progressUpdateInterval={1000} // [iOS] Interval to fire onProgress (default to ~250ms)
onLoad={this.onLoad} // Callback when video loads
onProgress={this.onProgress} // Callback every ~250ms with currentTime
onEnd={this.onEnd} // Callback when playback finishes
onError={this.onError} // Callback when video cannot be loaded
onBuffer={this.onBuffer} // Callback when remote video is buffering
onTimedMetadata={this.onTimedMetadata} // Callback when the stream receive some metadata
style={PlayerStyle.backgroundVideo}
/>
work well with: file:///path both device and genymotion.
@Donhv are you using downloaded mp4 files with react-native-fs? If so an example would be greatly appreciated
Just a reminder since it was overwritten by many people here:
You need to test on real device at least on android. In simulators (both the AVD and Genymotion) it might fail playing local stored video-files for unknown reason. I don't if this is always the rule or dependent from platform, Android-version and so on.
But before reopening new issues for this you should at least try it on a real device! Thank you.
Have the same issue with playing the video on device. But i do not use uri, i import it from a folder like
import strafe from '../assets/strafe.mp4';
and set it like
It works fine in simulator, but not on my device.
Any idea to fix it?
@freddiecabrera Were you able to get it working with react-native-fetch-blob? I am in the same predicament, and would rather not switch to react-native-fs if possible.
@TylerNoblett I get it working with react-native-fetch-blob. All I did is rename the file cached by react-native-fetch-blob to its original extension e.g. '.mp4'.
@chiubo Thanks for the reply! I ended up moving to react-native-fs and was able to get the same result.
@TylerNoblett No I wan't able to get it I moved on and created a hack for my problem that works pretty well haha
I couldn't make it work with 3.1.0 version, with 2.0.0 it works out the box
@TylerNoblett can you share how you are passing RNFS.readFile to react-native-video's source? I can write and read my file but all i can do is log it. I can't get rn-video to play
@kenkotch I set this up a while back, so my memory is a little fuzzy on why I did it this way, but first I set up a utility method to create the proper filename:
export function createPathForAppResource(resource) {
let extensionType = resource.mimeType == 'audio/mpeg' ? 'mp3' : resource.mimeType.split('/')[1];
return 'file://' + RNFS.DocumentDirectoryPath + '/' + resource.id + '.' + extensionType
}
I am storing the mimeType (audio/mpeg image/jpeg, video/mp4) for each resource, so I grab that the appropriate extension based on that, and then add the RNFS path, the resource name, and the extension together to create a filePath.
For my video, I specify that it needs to be a uri, and use the method above on a resource that I got from the redux state.
<Video
source={{ uri: createPathForAppResource(backgroundResource) }}
/>
Does that all make sense? Let me know if you have any questions!
Thank you @TylerNoblett
Yes, that makes sense. The part I can't get right is reading from fs and playing from there.
originally I was just saving the blob that is the actual file from our API. I realized I could stick the API call right in source and it works fine (but I need offline access) so I thought I'd save the entire api response w/ react-native-fs. Of course it is an object so I JSON.stringify() it and parse it on the other end.
I imagine the issue comes there as I dont know much about stringifying/parsing a video file.
So my question is are you saving an object or just a blob and how?
Thanks again
@kenkotch Ah I see. I'm saving the files individually, because of some issue we ran into with streaming the whole blob (I can't remember why we did it that way), so I'm not sure I can be of much help in getting it out of the blob.
I'm saving the files with SQLite; I'm not sure how much of the db file would be helpful, so I've included a stripped down example of what the full db, but you can go to saveResources at the bottom for the specific bit you're looking for.
import SQLite from 'react-native-sqlite-storage';
import * as RNFS from 'react-native-fs';
import { createPathForTransportResource } from '../util';
const db = SQLite.openDatabase(
{
name: 'db',
location: 'default'
},
() => console.log("Successfully opened database"),
(err) => console.log("Failed to create database", err)
);
var tablesSuccessfullyCreated = new Promise((resolve, reject) => {
db.transaction(tx => {
var createAllMetadata = new Promise((resolve, reject) => {
tx.executeSql('create table if not exists AllMetadata (id integer primary key not null, contents text not null);',
null, (newTx, result) => {
resolve(result);
}, (newTx, err) => {
reject(err);
});
});
var createResources = new Promise((resolve, reject) => {
tx.executeSql('create table if not exists Resources (id integer primary key not null, mimeType varchar(10), data varchar(256), resource_type varchar(10));',
null, (newTx, result) => {
resolve(result);
}, (newTx, err) => {
reject(err);
});
});
Promise.all([createAllMetadata, createResources,])
.then((result) => resolve(result))
.catch((err) => reject(err));
});
}).catch(err => {
return Promise.reject("Error somewhere within tablesSuccessfullyCreated")
});
function saveAllMetadata(allMetadata) {
return tablesSuccessfullyCreated.then(() => {
return new Promise((resolve, reject) => {
var JSONifiedAllMetadata = JSON.stringify(allMetadata);
db.transaction(tx => {
tx.executeSql(
'insert or replace into AllMetadata (id, contents) values (1, ?)', [JSONifiedAllMetadata], (newTx, result) => {
resolve(result);
}, (newTX, err) => {
reject(err);
}
);
});
})
});
}
function saveResource(resource) {
let resourcePath = createPathForTransportResource(resource);
return tablesSuccessfullyCreated.then(() => {
return RNFS.writeFile(resourcePath, resource.base64, 'base64');
}).then((success) => {
return new Promise((resolve, reject) => {
db.transaction(tx => {
tx.executeSql(
'insert or replace into Resources (id, mimeType, data, resource_type) values (?, ?, ?, ?)',
[resource.id, resource.mime_type, resourcePath, resource.resource_type]
, (newTx, result) => {
resolve(result);
}, (newTX, err) => {
reject(err);
}
);
});
})
}).catch(err => {
console.log("Failed to save", createPathForTransportResource(resource), err);
return Promise.reject(err);
})
}
in the saveResource func, I create a resource path in a similar way to how I did in my comment above, and then write the files using RNFS. Does that help at all?
Thanks @TylerNoblett . I have the same question as @kenkotch . We are able to use the Video component without issue when passing a URL for a .wav file (the MIME type of the GET response is audio/wav) to the source: { uri: URL } prop. However, we cannot seem to successfully pass a local file to the uri attribute of the source prop. We are trying to read the file and pass the blob directly. We've also tried passing relative/ absolute paths of the file to the uri. We've confirmed the file is indeed on the device before mounting the Video component, too. Can you please advise?
@mdaronco I'm not sure what to tell you, unfortunately, other than what I've posted above, especially if you're passing the blob, and not the individual file (I'm guessing that what you meant by "We are trying to read the file and pass the blob directly")?
Just to clarify, for local files on the video component, I use
<Video
source={{ uri: filepath }}
/>
instead of what you put ->source: { uri: filepath }, though I'm not sure if that was your actual code or if you were just giving a quick example.
@TylerNoblett Thank you very much for the help, I actually didn't see your last response before posting my question!
Thank you all for starting this thread, I finally managed to play local files (iOS device and emulator, have not yet tried Android). No need to prefix with file:// as this is inserted automatically when the uri starts with /, BUT what I found that if the filename has spaces in it, it just does not work.
@mindfulnessforever how did you get it to work? I am trying to download file to Documents folder and the play it with <Video source={{uri: "/absolute/path/to/file"}} />, but still get AVFoundationErrorDomain on real ios device (but Android works great)
@Strate I have not seen AVFoundationErrorDomain so I do not know what is causing. Looking at your code, have you tried adding file:// at the start of the uri, as in <Video source={{uri: "files:///absolute/path/to/file"}} />?
This is my code and it works in the emulator and on a real ios device:
this.media = {uri:file://${RootPath}/${localFilename}}; ... <Video source={this.media}....
Where ${RootPath} is export const RootPath = firebase.storage.Native.DOCUMENT_DIRECTORY_PATH; with import firebase from "react-native-firebase";. I think I used RNFS.MainBundlePath at some point as my ${RootPath} and that worked as well on iOS.
@mindfulnessforever how did you get it to work? I am trying to download file to Documents folder and the play it with
<Video source={{uri: "/absolute/path/to/file"}} />, but still getAVFoundationErrorDomainon real ios device (but Android works great)
This is exactly happening with me , it work on android but wont on iOS . Note i am testing on real devices.Anybody have solution so far? Please share.Thanks
@mindfulnessforever您是如何工作的?我正在尝试将文件下载到Documents文件夹并使用进行播放
<Video source={{uri: "/absolute/path/to/file"}} />,但仍然可以AVFoundationErrorDomain在真正的ios设备上使用(但Android效果很好)这确实是我发生的,它在android上有效,但在iOS上不行。注意我正在真实设备上进行测试。到目前为止,有人解决方案吗?请分享。谢谢
+1
@alitele @gdoudeng The path seems ok to me. I found that if the filename contained spaces, it just did not work with
still unable to play video from file system downloaded with rn-fetch-blob on ios. Anyone found a fix for this?
I solved this problem by requesting permission by using react native PermissionsAndroid API.
I previously just placed the permission snippet in the manifest file direct without the API and thought the permission was granted but it was actually not...
So after using the Permission API...I granted the permission and it worked...
Most helpful comment
I've finally managed it, using the prefix "file://" (only on android, not on iOS)