Given a path (on the SD card) to an audio file, what is the best way of determining the length of the audio in milliseconds and the file format (or Internet media type)?
(For the duration one could use MediaPlayer's getDuration-method, but this seems too slow/clumsy.)
For the length of the audio file:
File yourFile;
MediaPlayer mp = new MediaPlayer();
FileInputStream fs;
FileDescriptor fd;
fs = new FileInputStream(yourFile);
fd = fs.getFD();
mp.setDataSource(fd);
mp.prepare();
int length = mp.getDuration();
mp.release();
Check this for MimeType:
https://stackoverflow.com/a/8591230/3937699
I think the easiest way is:
MediaPlayer mp = MediaPlayer.create(this, Uri.parse(uriOfFile);
int duration = mp.getDuration();
mp.release();
/*convert millis to appropriate time*/
return String.format("%d min, %d sec",
TimeUnit.MILLISECONDS.toMinutes(duration),
TimeUnit.MILLISECONDS.toSeconds(duration) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration))
);
Just taking a stab at an answer for you, but you could probably determine the media type by the file extension - which I think MediaFile may be able to help you with. As for duration, I believe the getDuration() method is actually a native call, so I don't know if you will be able to do it much faster.
Related
I want to create a media player with multiple sounds and play one at random when a button is clicked.
I tried using the MediaPlayer.create() function but I can only set one audio source.
I also tried using the setDataSource() function but this only seems to work for resources that are stored outside the application (as far as I know).
String meowsounds[] = new String[]{
"R.raw.meowcat1",
"R.raw.meowcat2",
"R.raw.meowcat3",
"R.raw.meowcat4",
"R.raw.meowcat5",
};
MediaPlayer mp = new MediaPlayer();
meow.setOnClickListener((view) ->{
Random random = new Random();
int r = random.nextInt(6-1) + 1;
try{
mp.setDataSource(meowsounds[r]);
mp.prepare();
mp.start();
}catch(Exception e){e.printStackTrace();}
});
This code doesn't give any error while running but doesn't play anything on click
EDIT: Updated code which works on first click but not on subsequent clicks:
meow.setOnClickListener((view) ->{
Random random = new Random();
int r = random.nextInt(6-1) + 1;
Toast.makeText(MainActivity.this,Integer.valueOf(r).toString(),Toast.LENGTH_SHORT).show();
try{
if(r==1)
fileDescriptor = MainActivity.this.getResources().openRawResourceFd(R.raw.meowcat1);
else if (r==2)
fileDescriptor = MainActivity.this.getResources().openRawResourceFd(R.raw.meowcat2);
else if (r==3)
fileDescriptor = MainActivity.this.getResources().openRawResourceFd(R.raw.meowcat3);
else if (r==4)
fileDescriptor = MainActivity.this.getResources().openRawResourceFd(R.raw.meowcat4);
else
fileDescriptor = MainActivity.this.getResources().openRawResourceFd(R.raw.meowcat5);
mp.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength());
mp.prepare();
mp.start();
}catch(Exception e){e.printStackTrace();}
});
R - is the class with constants. "R.raw.meowcat1" - is the simple string and it is not constant which consider link to file.
First, you need to create int array of raw constants, but not string array of simple strings.
Second, If you want to play file from raw, you need to create AssetFileDescriptor for this raw file and use in setDataSource method. For example:
AssetFileDescriptor fileDescriptor = getContext().getResources().openRawResourceFd(R.raw.meowcat1);
mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength());
And don't forget please release codec in onDestroy method.
In Android, I can use MediaPlayer.create(context, R.raw.myFileName) to create an instance of the mediaPlayer, using a resource from the raw/ folder, and I can then use .start() to get that file to play. Later, I can use the various signatures for .setDataSource() to change the file that I want to play.
I can obtain the resourceId for a given file in the raw/ folder, using:
int resourceId = activity.getResources().getIdentifier("myFileName", "raw", activity.getPackageName());
Is it possible to use this integer resourceId to start playing that file instead of the current one? Or do I have to determine the full path to the file res/raw/myFileName.mid in order to change the track?
I am hoping that the solution will be something like this, with a real method instead of my invented equivalentToSetDataSourceUsingAResourceId() method name.
Resources resources = activity.getResources();
String packageName = activity.getPackageName();
int white = resources.getIdentifier("white", "raw", packageName);
int black = resources.getIdentifier("black", "raw", packageName);
MediaPlayer mediaPlayer = MediaPlayer.create(activity, white);
mediaPlayer.start();
// ... and some time later...
mediaPlayer.reset();
mediaPlayer.equivalentToSetDataSourceUsingAResourceId(black);
mediaPlayer.prepare();
mediaPlayer.start();
An alternative would be to destroy the current mediaPlayer instance and create a new one each time the sound file needs to change:
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
mediaPlayer = MediaPlayer.create(activity, black);
mediaPlayer.start();
This does not seem elegant to me.
According to MediaPlayer documentation setDataSource API's allowed only on fresh instance (IDLE state) of MediaPlayer. Which means you have to release() and recreate MediaPlayer from scratch if you wanna use only one instance at time.
Try do like this. Just pass id of your raw resource:
mMediaPlayer = MediaPlayer.create(this, R.raw.whatever);
I am having a weird issue reading the length/duration of a video file recorded with a device's camera by using MediaRecorder. The file is recorded into the application's private storage directory, which is set like so:
mMediaRecorder.setOutputFile(context.getFilesDir() + "/recordings/webcam.3gpp");
After recording is complete, I attempt to read the length of the video with these methods:
Method 1:
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource(context.getFilesDir() + "/recordings/webcam.3gpp");
String time = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
return Long.parseLong(time);
Method 2:
MediaPlayer mp = MediaPlayer.create(context, Uri.parse(context.getFilesDir() + "/recordings/webcam.3gpp"));
long duration = mp.getDuration();
mp.release();
return duration;
None of the methods work. mediaMetadataRetriever.extractMetadata returns null and MediaPlayer.create fails with an IOException. I have verified that the file exists.
Important note: This issue DOES NOT happen if I save the recording to "/sdcard/recordings/webcam.3gpp". For some reason, I just cannot read the duration when the file is in the private files directory that belongs to the application. Also, this issue ONLY happens on my Samsung Droid Charge, which runs Android 2.3. It DOES NOT happen on a Samsung Galaxy S4, which runs Android 4.2, and Asus Nexus 7, which runs Android 4.3.
Edit:
If I take the same file and copy it to the sdcard, then read the length of it there, everything works. What gives?
copy(new File(context.getFilesDir() + "/recordings/webcam.3gpp"), new File("/sdcard/wtfisthiscrap.3gpp"));
MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
mediaMetadataRetriever.setDataSource("/sdcard/wtfisthiscrap.3gpp");
String time = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
return Long.parseLong(time); // works!
What can I do to solve this issue?
I was able to solve my issue by setting a FileInputStream as the data source of MediaPlayer.
MediaPlayer mp = new MediaPlayer();
FileInputStream stream = new FileInputStream(context.getFilesDir() + "/recordings/webcam.3gpp");
mp.setDataSource(stream.getFD());
stream.close();
mp.prepare();
long duration = mp.getDuration();
mp.release();
return duration;
The source of my answer comes from https://stackoverflow.com/a/6383655/379245
I put in background of my android application a song. I don't know how much time the application is open. And I want to put this song to repeat. My code is:
MediaPlayer mySong;
mySong = MediaPlayer.create(X_0Activity.this, R.raw.tj);
mySong.start();
Uri mediaUri = createUri(context, R.raw.media); // Audiofile in raw folder
Mediaplayer mPlayer = new MediaPlayer();
mPlayer.setDataSource(context, mediaUri);
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.prepare();
mPlayer.setLooping(true); // for repeat song
mPlayer.start();
mySong.setLooping(true) // repeat Song
mySong.start(),
And now you are ready with repeat mode on.
Use SoundPool, you can easily loop it any time you want! Here is a very good example: Play sound with SoundPool
In the
spool.play(soundID, volume, volume, 1, 0, 1f);
the number 0 represents the number you want to repeat the song. For infinite loop the suitable value is -1.
As I understand it, Android will only play AAC format audio if it's encoded as MPEG-4 or 3GPP.
I'm able to play AAC audio encoded as M4A when it's local to the app, but it fails when obtaining it from a server.
The following works, as the m4a file is held locally in the res/raw directory.
MediaPlayer mp = MediaPlayer.create(this, R.raw.*file*);
mp.start();
The following doesn't work. (But does with MP3's).
Uri uri = Uri.parse("http://*example.com*/blah.m4a");
MediaPlayer mp = MediaPlayer.create(this, uri);
mp.start();
Can anyone shed any light on why it fails when the m4a audio file is not local?
Here's (some of) the error...
ERROR/PlayerDriver(542): Command PLAYER_INIT completed with an error or info UNKNOWN PVMFStatus
ERROR/MediaPlayer(769): error (200, -32)
WARN/PlayerDriver(542): PVMFInfoErrorHandlingComplete
DEBUG/MediaPlayer(769): create failed:
DEBUG/MediaPlayer(769): java.io.IOException: Prepare failed.: status=0xC8
DEBUG/MediaPlayer(769): at android.media.MediaPlayer.prepare(Native Method)
DEBUG/MediaPlayer(769): at android.media.MediaPlayer.create(MediaPlayer.java:530)
DEBUG/MediaPlayer(769): at android.media.MediaPlayer.create(MediaPlayer.java:507)
...
I'm targeting SDK 1.6.
This work-around allows you to play M4A files from the net (and AAC files in other containers such as MP4 & 3GP). It simply downloads the file and plays from the cache.
private File mediaFile;
private void playAudio(String mediaUrl) {
try {
URLConnection cn = new URL(mediaUrl).openConnection();
InputStream is = cn.getInputStream();
// create file to store audio
mediaFile = new File(this.getCacheDir(),"mediafile");
FileOutputStream fos = new FileOutputStream(mediaFile);
byte buf[] = new byte[16 * 1024];
Log.i("FileOutputStream", "Download");
// write to file until complete
do {
int numread = is.read(buf);
if (numread <= 0)
break;
fos.write(buf, 0, numread);
} while (true);
fos.flush();
fos.close();
Log.i("FileOutputStream", "Saved");
MediaPlayer mp = new MediaPlayer();
// create listener to tidy up after playback complete
MediaPlayer.OnCompletionListener listener = new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
// free up media player
mp.release();
Log.i("MediaPlayer.OnCompletionListener", "MediaPlayer Released");
}
};
mp.setOnCompletionListener(listener);
FileInputStream fis = new FileInputStream(mediaFile);
// set mediaplayer data source to file descriptor of input stream
mp.setDataSource(fis.getFD());
mp.prepare();
Log.i("MediaPlayer", "Start Player");
mp.start();
} catch (Exception e) {
e.printStackTrace();
}
}
I tried it too but I could not find out the solution!
At the last Google I/O I saw something that helped me a lot. It is Extending from MediaPlayer and improve a lot of things! Take a look.
EXOPLAYER CAN HELP YOU A LOT
Check this part of the example:
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 256;
...
// String with the url of the radio you want to play
String url = getRadioUrl();
Uri radioUri = Uri.parse(url);
// Settings for exoPlayer
Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
String userAgent = Util.getUserAgent(context, "ExoPlayerDemo");
DataSource dataSource = new DefaultUriDataSource(context, null, userAgent);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
radioUri, dataSource, allocator, BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT);
audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);
// Prepare ExoPlayer
exoPlayer.prepare(audioRenderer);
EXOPLAYER- I can play anything from streamings (video and audio)!
LET ME KNOW IF YOU NEED HELP TO IMPLEMENT IT! :)
This is a wild shot in the dark, but I have seen similar behavior with the flash player where it actually ignores the file name and only relies on the MIME type sent by the server. Any idea what headers are being sent down from example.com? You might want to try wrapping your blah.m4a in a page that can set the headers and then stream the binary data. Give these types a shot and the community would appreciate a confirmation of what works:
audio/mpeg
audio/mp4a
audio/mp4a-latm
audio/aac
audio/x-aac
I found that if you record the audio file on Android with the following properties, you are then able to play it on your server. It also plays well in the HTML Audio Element, however only on Firefox at the moment. This may change in the future.
Android (JAVA):
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC);
mediaRecorder.setAudioSamplingRate(44100);
mediaRecorder.setAudioChannels(1);
mediaRecorder.setOutputFile(filePath);
HTML:
<audio id="audioMediaControl" controls src="yourfile.m4a"> Your browser does not support the audio element. </audio>
try --
1) MP.prepareAsync()
2) onPrepared() { mp.start() }