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.
Related
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");
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);
Reading mp3 file from expansion file
I have create and expansion file with name
"main.1.com.example.app.obb"
which contains and audio file name "about_eng.mp3"
Now the issue i have written the following code to read and play the mp3 file
private final static String EXP_PATH = "/Android/obb/";
static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
String packageName = ctx.getPackageName();
Vector<String> ret = new Vector<String>();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// Build the full path to the app's expansion files
File root = Environment.getExternalStorageDirectory();
File expPath = new File(root.toString() + EXP_PATH + packageName);
// Check that expansion file path exists
if (expPath.exists()) {
if ( mainVersion > 0 ) {
String strMainPath = expPath + File.separator + "main." +
mainVersion + "." + packageName + ".obb";
File main = new File(strMainPath);
if ( main.isFile() ) {
ret.add(strMainPath);
}
}
if ( patchVersion > 0 ) {
String strPatchPath = expPath + File.separator + "patch." +
mainVersion + "." + packageName + ".obb";
File main = new File(strPatchPath);
if ( main.isFile() ) {
ret.add(strPatchPath);
}
}
}
}
String[] retArray = new String[ret.size()];
ret.toArray(retArray);
return retArray;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// Get a ZipResourceFile representing a merger of both the main and patch files
try {
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,1,1);
if(expansionFile!=null){
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("about_eng.mp3");
//or
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource( fd.getFileDescriptor(),
fd.getStartOffset(),fd.getLength());
mediaPlayer.prepare();
mediaPlayer.start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Get an input stream for a known file inside the expansion file ZIPs
}
But it is always throwing exception at this line
mediaPlayer.setDataSource( fd.getFileDescriptor(),
fd.getStartOffset(),fd.getLength());
mediaPlayer.prepare();
because the variable fd is null.
Can any body help me to solve this
i have googled and found that we shold have to make .zip with 0% (No compression) that is mention in http://developer.android.com/google/play/expansion-files.html
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
so How to make 0% compression zip using winrar?
here see the compression method
so we should have to upload this zip in play store.
so you not need to use ZipHelper.java that is mentioned in other SO answer
just simply use
ZipResourceFile expansionFile=null;
try {
expansionFile = APKExpansionSupport.getAPKExpansionZipFile(getApplicationContext(),3,0);
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("test.mp3");
MediaPlayer mPlayer = new MediaPlayer();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(fd.getFileDescriptor(),fd.getStartOffset(),fd.getLength());
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Try to use expansionFile.getAssetFileDescriptor("main_expansion/about_eng.mp3").
If it doesn't work, call this:
ZipResourceFile.ZipEntryRO [] entries = expansionFile.getAllEntries();
and look at the file names:
entries[0].mFileName;.
That could be a hint how the path should look like.
I know that the question is old, but maybe it helps someone.
To read mp3, with mediaplayer, you can do it 3 ways
for example. my application package name is
com.bluewindsolution.android.sampleapp
then my expansion file should be
main.1.com.bluewindsolution.android.sampleapp.obb
in detail, you can followed in here
[main|patch].< expansion-version >.< package-name >.obb
From expansion you can get it via 3 ways:
via AssetFileDescriptor
// this one work with image file, media file
// Get a ZipResourceFile representing a specific expansion file
// mainContext, version no. of your main expansion, version no of your patch
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this, 1, 0);
AssetFileDescriptor afd_img1 = expansionFile.getAssetFileDescriptor("your_path/your_file.jpg");
AssetFileDescriptor afd_mpbg = expansionFile.getAssetFileDescriptor("your_path/your_file.mp3");
// to set image
ImageView imageView1 = (ImageView)findViewById(R.id.iv1);
imageView1.setImageBitmap(BitmapFactory.decodeFileDescriptor(afd_iv1.getFileDescriptor()));
// to set sound
try {
mpbg.setDataSource(afd_mpbg.getFileDescriptor(), afd_mpbg.getStartOffset(), afd_mpbg.getDeclaredLength());
mpbg.prepareAsync();
mpbg.start();
} catch (IllegalStateException e) {
Log.w("Error=====", "Failed to prepare media player", e);
}
via InputStream
// this one work with image file, media file
// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);
// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
via Uri
//this one can use in almost thing such as jpg, mp3, mp4, pdf, etc.
//you need to create new class to override APEZProvider
public class ZipFileContentProvider extends APEZProvider {
#Override
public String getAuthority() {
return "com.bluewindsolution.android.sampleapp";
}
}
//you need to add <provider> in AndroidManifest.xml
<provider
android:name="com.bluewindsolution.android.sampleapp.ZipFileContentProvider"
android:authorities="com.bluewindsolution.android.sampleapp"
android:exported="false"
android:multiprocess="true"
>
<meta-data android:name="mainVersion"
android:value="1"></meta-data>
</provider>
//after that you can use it any time by this code:
String filename2 = "image/i_commu_a_1_1_1_1.jpg"; // suppose you store put your file in directory in .obb
String uriString2 = "content://com.bluewindsolution.android.sampleapp" + File.separator + filename2;
Uri uri2 = Uri.parse(uriString2);
imageView2.setImageURI(uri2);
// Filename inside my expansion file
String filename = "video/v_do_1_1_1.mp4"; // suppose you store put your file in directory in .obb
String uriString = "content://com.bw.sample_extension_download_main" + File.separator + filename;
Uri uri = Uri.parse(uriString);
v1 = (VideoView)findViewById(R.id.videoView1);
v1.setVideoURI(uri);
v1.start();
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");
}
I currently have a set of media files in the raw folder of the android project that are loaded quickly and played when called using the mediaplayer class. I need to add more variations of these files and categorize them into folders, but apparently the raw folder does not support folders. Would I be able to quickly load these files from the assets folder and play them with mediaplayer? If so, how?
I've this method that returns the all files by extension in a folder inside asset folder:
public static String[] getAllFilesInAssetByExtension(Context context, String path, String extension){
Assert.assertNotNull(context);
try {
String[] files = context.getAssets().list(path);
if(StringHelper.isNullOrEmpty(extension)){
return files;
}
List<String> filesWithExtension = new ArrayList<String>();
for(String file : files){
if(file.endsWith(extension)){
filesWithExtension.add(file);
}
}
return filesWithExtension.toArray(new String[filesWithExtension.size()]);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
if you call it using:
getAllFilesInAssetByExtension(yourcontext, "", ".mp3");
this will return all my mp3 files in the root of assets folder.
if you call it using:
getAllFilesInAssetByExtension(yourcontext, "somefolder", ".mp3");
this will search in "somefolder" for mp3 files
Now that you have list all files to open you will need this:
AssetFileDescriptor descriptor = getAssets().openFd("myfile");
To play the file just do:
MediaPlayer player = new MediaPlayer();
long start = descriptor.getStartOffset();
long end = descriptor.getLength();
player.setDataSource(this.descriptor.getFileDescriptor(), start, end);
player.prepare();
player.setVolume(1.0f, 1.0f);
player.start();
Hope this helps
Here is a function that can play mediafiles from your asset folder. And you can use it with smth like play(this,"sounds/1/sound.mp3");
private void play(Context context, String file) {
try {
AssetFileDescriptor afd = context.getAssets().openFd(file);
meidaPlayer.setDataSource(
afd.getFileDescriptor(),
afd.getStartOffset(),
afd.getLength()
);
afd.close();
meidaPlayer.prepare();
meidaPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
You could put your mp3 files at : res/raw folder as myringtone.mp3 or as your wish.
MediaPlayer mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.myringtone);
mediaPlayer.start();