MediaPlayer setDataSource to expansionFilePath doesn't work - android

I'm trying to load a movie from a file within a mounted Google Play APK expansion OBB file.
mMediaPlayer = new MediaPlayer();
StorageManager storageManager = (StorageManager)mParentActivity.getSystemService(Context.STORAGE_SERVICE);
String obbPath = ExpansionHelper.getExpansionFilePath(mParentActivity);
File movie = new File(storageManager.getMountedObbPath(obbPath), filename);
Log.d(Constants.TAG, "Movie exists is " + movie.exists());
mMediaPlayer.setDataSource(obbPath);
Note: movie exists logs 'true'
E/MediaPlayer(27155): Error (1,-2147483648) Error while opening the
file. Unloading the media player (Unspecified media player error,
-2147483648) E/MediaPlayer(27155): stop called in state 0 E/MediaPlayer(27155): error (-38, 0)
How can I play a movie from an APK OBB expansion file (not the zip kind)?

I'm not exactly sure why this method does work but if you provide a FileDescriptor from FileInputStream is work like a charm!
FileInputStream fis = new FileInputStream(movie);
mMediaPlayer.setDataSource(fis.getFD());
fis.close();

Oh, just seen this question regards non-zipped files, well here is zipped version anyway:
private static void setMediaPlayerDataSourceFromZip(MediaPlayer mediaPlayer,
String zipFileName, String fileNameInZip) throws IOException,
FileNotFoundException {
ZipResourceFile zip = new ZipResourceFile(zipFileName);
FileInputStream fis = new FileInputStream(zipFileName);
try {
FileDescriptor zipfd = fis.getFD();
ZipEntryRO entry = zipFindFile(zip, fileNameInZip);
mediaPlayer.setDataSource(zipfd, entry.mOffset,
entry.mUncompressedLength);
} finally {
fis.close();
}
}
private static ZipEntryRO zipFindFile(ZipResourceFile zip, String fileNameInZip) {
for (ZipEntryRO entry : zip.getAllEntries()) {
if (entry.mFileName.equals(fileNameInZip))
return entry;
}
throw new RuntimeException(String.format("File \"%s\"not found in zip", fileNameInZip));
}
Usage:
setMediaPlayerDataSourceFromZip(mediaPlayer,
"/Some/zip/obb/withoutCompression.zip",
"path/within/zip/mymovie.mp4");

Related

Gluon mobile VideoService not working for media files stored locally on android device?

In my application I'd like to play videos from URL or files stored locally on the android device using Gluon-mobile VideoService.
This works fine for URLs, but (in my environment) it does not work for files stored on the android device,
e.g. /sdcard/DCIM/tmp/1834.mp4.
The LogCat shows
V/MediaPlayerService(2431): Create new media retriever from pid 10868
W/AndroidVideoService(10868): Invalid video file: /sdcard/DCIM/tmp/1834.mp4
V/MediaPlayer(10868): resetDrmState: mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
I can play the file in that location with the standalone Android Video-Player.
I also tried to copy the file programmatically to the directories delivered by
StorageService.getPublicStorage("Movies") (->
/storage/emulated/0/Movies) or
StorageService.getPrivateStorage() (-> /data/user/0/mypackage/files)
plus "/tmp/" + "1834.mp4"
and play it via the application from there, but the LogCat message again shows
W/AndroidVideoService(...): Invalid video file ...
The javadoc of VideoService.getPlaylist() says
The media files (video and audio) can either be a valid URL or they
can be provided in the resources folder.
So is it not possible to play media files stored locally on android device ?
Here are the relevant parts of my build.gradle file:
buildscript {
// ...
dependencies {
classpath 'org.javafxports:jfxmobile-plugin:1.3.16'
}
}
dependencies {
compile 'com.gluonhq:charm:5.0.1'
// ...
}
// ...
jfxmobile {
downConfig {
version = '3.8.0'
// Do not edit the line below. Use Gluon Mobile Settings in your project context menu instead
plugins 'display', 'lifecycle', 'statusbar', 'storage', 'video'
}
// ...
}
My phone has Android 8.1.
Added source code for testing purposes:
int i = 0;
try {
File privateAppStorage = Services.get(StorageService.class)
.flatMap(StorageService::getPrivateStorage)
.orElseThrow(() -> new FileNotFoundException("Could not access private storage."));
String outputfilename = privateAppStorage + "/1834.mp4";
if(i == 0) { // to skip copying set i != 0 on 2nd run
// copy video
File input = new File("/sdcard/DCIM/tmp/1834.mp4");
int li = (int) input.length();
byte[] bFile = new byte[li];
FileInputStream fis = new FileInputStream(input);
fis.read(bFile);
fis.close();
File output = new File(outputfilename);
FileOutputStream fos = new FileOutputStream(output);
fos.write(bFile);
fos.flush();
fos.close();
li = (int) output.length();
/* test copying routine
File testoutput = new File("/sdcard/DCIM/tmp/1834_2.mp4");
FileOutputStream tfos = new FileOutputStream(testoutput);
tfos.write(bFile);
tfos.flush();
tfos.close();
li = (int) testoutput.length();
*/ // end test copying routine
}
// play video
Optional<VideoService> service = Services.get(VideoService.class);
if(service.isPresent()){
VideoService videoService = service.get();
videoService.setControlsVisible(true);
videoService.setFullScreen(true);
Status status = videoService.statusProperty().get();
ObservableList<String> sl = videoService.getPlaylist();
if(sl.size() > 0)
sl.set(0, outputfilename);
else
videoService.getPlaylist().add(outputfilename);
videoService.show();
}
} catch ( IOException e) {
e.printStackTrace();
}
Edit 2019-06-05
More debug inormation regarding the DefaultVideoService internal copying process:
I/DefaultVideoService(10544): Copying video file: /data/user/0/mypackage/files/1834.mp4, from resources to /data/user/0/mypackage/files/assets/_data_user_0_com.hp_files_1834.mp4
I/DefaultVideoService(10544): Copying video file /data/user/0/mypackage/files/1834.mp4 finished with result: failed
I/AndroidVideoService(10544): Creating new MediaPlayer for /data/user/0/mypackage/files/1834.mp4
Debugging into DefaultVideoService.copyFile(...) I found that statement DefaultVideoService.class.getResourceAsStream(pathIni) returns null and thus DefaultVideoService internal copying fails.
Why it returns null, I do not know since I do not have the appropriate java.lang.Class source.
Here is a workaround that I found for my environment.
Lets say the file to be played is
String filename = "/sdcard/DCIM/tmp/1834.mp4";
Before calling
VideoService.getPlaylist.add(filename);
copy the file with the statements:
File privateAppStorage = Services.get(StorageService.class)
.flatMap(StorageService::getPrivateStorage)
.orElseThrow(() -> new FileNotFoundException("Could not access private storage."));
File assets = new File(privateAppStorage.getAbsolutePath() + "/assets");
boolean assets_exists = assets.exists();
if(!assets.exists())
{
assets_exists = assets.mkdir();
}
if(assets_exists && assets.canWrite())
{
File input = new File(filename);
int li = (int) input.length();
byte[] bFile = new byte[li];
FileInputStream fis = new FileInputStream(input);
fis.read(bFile);
fis.close();
File copiedToAssets = new File(assets.getAbsolutePath() + "/" + filename.replaceAll("/", "_"));
FileOutputStream fos = new FileOutputStream(copiedToAssets);
fos.write(bFile);
fos.flush();
fos.close();
}
This bypasses DefaultVideoService.class.getResourceAsStream(pathIni) mentioned in my question and the video can be played.

Android APK Expansion File, Cannot read .wav files

I have been developing an App which need to play sounds and have large set of sounds files in .wav format that boast arounds near 1GB size. I have followed the official documentation for making the Expansion file.
First i made a .zip file with 0 compressions, all .wav files are under that files (without any subfolder. that is main.1.package contains all the sounds file).Then i renamed the .zip folder into .obb. After that i put that .obb file inside the apps folder under shared storage obb folder (com.moinul.app is my package name, so the folder name was it, also the expansion file were named main.1.com.moinul.app.obb )
Problem is when i try to read the files from my expansion files i get null pointer exceptions:
try
{
String param = (String) v.getTag();
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, 1, 0);
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("p25_1.wav");
MediaPlayer mPlayer = new MediaPlayer();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(fd.getFileDescriptor());
mPlayer.prepare();
mPlayer.start();
}
catch (Exception e)
{
Log.w(TAG, e.getMessage() + "");
e.printStackTrace();
}
I get exceptions saying that fd is null. Any help would be appreciated.
Thanks
Try use: AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("assets/p25_1.wav");
Try
`public class Test extends Activity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String TAG = "Test";
Context context = this;
try {
MediaPlayer mediaPlayer = new MediaPlayer();
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, 1, 0);
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor("assets/p25_1.wav");
if (assetFileDescriptor != null) {
final FileInputStream fis = assetFileDescriptor.createInputStream();
if (fis != null) {
mediaPlayer.setDataSource(fis.getFD());
fis.close();
mediaPlayer.prepare();
mediaPlayer.start();
} else {
Log.w(TAG, "/fis: null");
}
}
} catch (Exception e) {
Log.w(TAG, e.getMessage() + "");
}
}
}`
Ok so it worked finally. After changing into :
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("Sounds/p25_1.wav");
Its working now. Its weird that i had to add Sounds, which was the folder name before compress (i actually compressed this folder and all .wav are under it). I think the compressor made another subfolder inside the .zip.
Anyway thanks for the help.

Play media from from zip file without unzipping

I know there are already a few questions like this on SO, but they relate to extracting the file before playing it.
In the Android docs here it explains that you can play files directly from a .zip file without extracting it.
Tip: If you're packaging media files into a ZIP, you can use media
playback calls on the files with offset and length controls (such as
MediaPlayer.setDataSource() and SoundPool.load()) without the need to
unpack your ZIP. In order for this to work, you must not perform
additional compression on the media files when creating the ZIP
packages. For example, when using the zip tool, you should use the -n
option to specify the file suffixes that should not be compressed:
zip -n .mp4;.ogg main_expansion media_files
I've made an (uncompressed) zip package, but I cannot figure out how to get from a ZipEntry to a FileDescriptor, and they don't explain any further. How do I get a FileDescriptor without unpacking the zip file?
You can use The APK Expansion Zip Library to do that even if you do not use APK Expansions in your app.
Follow this documentation to get the library: Using the APK Expansion Zip Library
Zip your sound files without compression using your favorite zip tool.
Use this code to load the music:
ZipResourceFile expansionFile = new ZipResourceFile("myZipFile.zip");
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor("myMusic.mp3");
try {
mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor());
mediaPlayer.prepare();
mediaPlayer.start();
}
catch (IOException e) {
// Handle exception
}
There aren't many recent solutions for this issue, so I was stuck on this, too – until I created a new instance of MediaPlayer in the function where I was trying to read the zip file. Suddenly playback started without problems. Now, instead, I'm passing my (global) MediaPlayer to the function like this: (Kotlin)
private fun preparePlayer(mp: MediaPlayer, position: Int) {
// Path of shared storage
val root: File = Environment.getExternalStorageDirectory()
Log.i("ROOT", root.toString())
// path of the zip file
val zipFilePath = File(root.absolutePath+"/Android/obb/MY_PACKAGE_NAME/MY_ZIP_FILE.zip")
// Is zip file recognized?
val zipFileExists = zipFilePath.exists()
Log.i("Does zip file exist?", zipFileExists.toString())
// Define the zip file as ZipResourceFile
val expansionFile = ZipResourceFile(zipFilePath.absolutePath)
// Your media in the zip file
val afd = expansionFile.getAssetFileDescriptor("track_01.mp3")
// val mp = MediaPlayer() // not necessary if you pass it as a function parameter
mp.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
mp.prepare()
mp.start() // Music should start playing automatically
Maybe this can help someone else. Good luck!
try {
ZipFile zf= new ZipFile(filename);
ZipEntry ze = zip.getEntry(fileName);
if (ze!= null) {
InputStream in = zf.getInputStream(ze);
File f = File.createTempFile("_AUDIO_", ".wav");
FileOutputStream out = new FileOutputStream(f);
IOUtils.copy(in, out);
// play f
}
} catch (IOException e) {
}
possible duplicate question
If you want to use a media file from a zip without unzipping you have to add also start offset and length to setDataSource.
ZipResourceFile expansionFile = new ZipResourceFile("myZipFile.zip");
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor("myMusic.mp3");
try {
mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(),
assetFileDescriptor.getStartOffset(),
assetFileDescriptor.getLength());
mediaPlayer.prepare();
mediaPlayer.start();
}
catch (IOException e) {
// Handle exception
}
You can create a custom MediaDataSource like this:
public class ZipMediaDataSource extends MediaDataSource {
private InputStream inputStream;
public ZipMediaDataSource(ZipFile file, ZipEntry zipEntry) throws IOException {
inputStream = file.getInputStream(zipEntry);
}
#Override
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
return inputStream.read(buffer, offset, size);
}
#Override
public long getSize() throws IOException {
return inputStream.available();
}
#Override
public void close() throws IOException {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
}
}
usage is:
ZipMediaDataSource zipMediaDataSource = new ZipMediaDataSource(zipFile, zipEntry);
mediaPlayer.setDataSource(zipMediaDataSource);

Can't get media player to work when SD drive is not installed

First I make a call to
get_fullPathToAudio();
to determine the path of where an audio file was downloaded.
public File get_fullPathToAudioAsFile()
{
File storageFile = this.getExternalFilesDir(null);
if (storageFile == null)
{
// no external storage so store on private path
storageFile = this.getFilesDir();
}
return storageFile;
}
public String get_fullPathToAudio() {
return get_fullPathToAudioAsFile() + File.separator + this.get_currentArticleAudioFileName();
}
String filePath = this.myApp.get_fullPathToAudio();
mediaPlayer.setDataSource(filePath);
mediaPlayer.prepare();
mediaPlayer.start();
All works well if I have an SD drive on my emulator but when I don't have an emulator the function
get_fullPathToAudioAsFile();
uses
getFilesDir();
instead of
getExternalFilesDir(null);
which returns a path to my private drive where my app is installed. I can see the file in
data/data/com.myapp/files/myfile.m4a
for example. Is there a different way to play an audio file if it's not on the SD drive?
The error messsage I get is
error(-1, -2147483648)
Could not open file null for playback
I solved it this way
FileInputStream fileInputStream = new FileInputStream(mFile);
mPlayer.setDataSource(fileInputStream.getFD());
mPlayer.prepare();
MediaPlayer error -2147483648 when playing file on internal storage

Accessing to files inside obb expansion file

I'm following all official expansion files guides, but i can't find it. I'm unable to access the contained obb file i need.
I need 6 audio files (80Mb) that i "stored" (uncompressed) in a zip file and renamed as 'main.2001.test.expansion.proj.obb' and stored in '/mnt/sdcard/Android/obb/test.expansion.proj/'
I'll try to access to the files
String mainFileName = Helpers.getExpansionAPKFileName(this,true,2001);
if(!Helpers.doesFileExist(this, mainFileName, 27959282L, false))
{
//download
} else {
Log.d("test_file","file exist");
}
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,2001,2001);
if(expansionFile!=null)
{
ZipEntryRO[] ziro = expansionFile.getAllEntries();
for (ZipEntryRO entry : ziro) {
Log.d("test_files_zip", "fileZip filename: "+entry.getZipFileName());
try{
AssetFileDescriptor ro = entry.getAssetFileDescriptor();
Log.d("test_files_zip", "--fileZip getfiledescriptor.tostring: "+ro.getFileDescriptor().toString());
Log.d("test_files_zip", "--fileZip createinputstring.tostring: "+ro.createInputStream().toString());
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor(entry.getZipFileName()+"/audio02.mp3");
if(assetFileDescriptor!=null) {
Log.d("test_files_mp3", "length: "+assetFileDescriptor.getLength()); //checking it exists
}
}catch (IOException e){ Log.e("test_exp","IoExcp: "+e.getMessage()); }
}
}
In -> assetFileDescriptor = expansionFile.getAssetFileDescriptor(.....); i've tried all i figuret out and found in different places, but i was unable to take the file.
Is there any way to get the file from its name if it's inside the zip?
The app should play these files in an specificorder and we don't want to unzip the files and make them ""public"".
Edited. Answer to myself
Found, it was a line i didn't understand when i firstly read it or i simply miss it.
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,2001,2001);
if(expansionFile!=null){
FileDescriptor fd = expansionFile.getAssetFileDescriptor("audio_01.mp3");
//or
InputStream is = expansionFile.getInputStream("audio_01.mp3");
}
As Aarolama Bluenk suggested, here's the answer code (repited)
Found, it was a line i didn't understand when i firstly read it or i simply miss it.
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,2001,2001);
if(expansionFile!=null){
FileDescriptor fd = expansionFile.getAssetFileDescriptor("audio_01.mp3");
//or
InputStream is = expansionFile.getInputStream("audio_01.mp3");
}

Categories

Resources