get duration of audio file
Asked Answered
A

13

39

I have made a voice recorder app, and I want to show the duration of the recordings in a listview. I save the recordings like this:

MediaRecorder recorder = new MediaRecorder();
recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
folder = new File(Environment.getExternalStorageDirectory()
            + File.separator + "Audio recordings");
String[] files = folder.list();
    int number = files.length + 1;
    String filename = "AudioSample" + number + ".mp3";
    File output = new File(Environment.getExternalStorageDirectory()
            + File.separator + "Audio recordings" + File.separator
            + filename);
    FileOutputStream writer = new FileOutputStream(output);
    FileDescriptor fd = writer.getFD();
    recorder.setOutputFile(fd);
    try {
        recorder.prepare();
        recorder.start();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        Log.e(LOG_TAG, "prepare() failed");
        e.printStackTrace();
    }

How can I get the duration in seconds of this file?

Thanks in advance

---EDIT I got it working, I called MediaPlayer.getduration() inside the MediaPlayer.setOnPreparedListener() method so it returned 0.

Africanist answered 13/3, 2013 at 19:26 Comment(0)
P
101

MediaMetadataRetriever is a lightweight and efficient way to do this. MediaPlayer is too heavy and could arise performance issue in high performance environment like scrolling, paging, listing, etc.

Furthermore, Error (100,0) could happen on MediaPlayer since it's a heavy and sometimes restart needs to be done again and again.

Uri uri = Uri.parse(pathStr);
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(AppContext.getAppContext(),uri);
String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
int millSecond = Integer.parseInt(durationStr);
Pocahontas answered 2/10, 2015 at 5:31 Comment(7)
This was nice to find since there seems to be a bug in MediaPlayer.getDuration() -- at least for mp4's. Thanks!Ignoramus
How do I get MediaMetadataCompat.METADATA_KEY_DURATION?Zonnya
I don't know about MediaMetadataCompat, but MediaMetadataRetriever.METADATA_KEY_DURATION is deprecated. Better to get the duration like this: String durationStr = mmr.ExtractMetadata(MetadataKey.Duration);Undress
@Undress Where do you get this info from? According to Android documentation it's not deprecated developer.android.com/reference/android/media/…Deliberate
@Deliberate Huh, that's weird. I don't know where I got that from. It was months ago, so I can't say what code I was looking at where I got a warning saying it was deprecated. (Doing a quick search thru some of my common code didn't turn up any results for METADATA_KEY_DURATION or ExtractMetadata.) I retract my comment and thank you correcting me.Undress
pub.dev/packages/media_metadata_retriever says this package is DISCONTINUED.Aflame
@Aflame that link suggests that the flutter package that uses MediaMetadataRetriever has been discontinued, not that MediaMetadataRetriever itself has been discontinued.Absurdity
M
41

The quickest way to do is via MediaMetadataRetriever. However, there is a catch

if you use URI and context to set data source you might encounter bug https://code.google.com/p/android/issues/detail?id=35794

Solution is use absolute path of file to retrieve metadata of media file.

Below is the code snippet to do so

 private static String getDuration(File file) {
                MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
                mediaMetadataRetriever.setDataSource(file.getAbsolutePath());
                String durationStr = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
                return Utils.formateMilliSeccond(Long.parseLong(durationStr));
            }

Now you can convert millisecond to human readable format using either of below formats

     /**
         * Function to convert milliseconds time to
         * Timer Format
         * Hours:Minutes:Seconds
         */
        public static String formateMilliSeccond(long milliseconds) {
            String finalTimerString = "";
            String secondsString = "";

            // Convert total duration into time
            int hours = (int) (milliseconds / (1000 * 60 * 60));
            int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
            int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);

            // Add hours if there
            if (hours > 0) {
                finalTimerString = hours + ":";
            }

            // Prepending 0 to seconds if it is one digit
            if (seconds < 10) {
                secondsString = "0" + seconds;
            } else {
                secondsString = "" + seconds;
            }

            finalTimerString = finalTimerString + minutes + ":" + secondsString;

    //      return  String.format("%02d Min, %02d Sec",
    //                TimeUnit.MILLISECONDS.toMinutes(milliseconds),
    //                TimeUnit.MILLISECONDS.toSeconds(milliseconds) -
    //                        TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));

            // return timer string
            return finalTimerString;
        }
Manning answered 15/2, 2017 at 13:2 Comment(1)
Wonderful solution! That worked for me big time. Just needed to extract Metadata from File without the tedious and heavy MediaPlayer.Hydrokinetic
L
24

Either try this to get duration in milliseconds:

MediaPlayer mp = MediaPlayer.create(yourActivity, Uri.parse(pathofyourrecording));
int duration = mp.getDuration();

Or measure the time elapsed from recorder.start() till recorder.stop() in nanoseconds:

long startTime = System.nanoTime();    
// ... do recording ...    
long estimatedTime = System.nanoTime() - startTime;
Lambart answered 13/3, 2013 at 19:35 Comment(3)
the first one returns 0 and if I want to use the second solution I have to play the file and I don't want thatAfricanist
You don't have to play the file for the second one. You set startTime when user starts recording and calculate estimatedTime when user stops the recording. For the first option, make sure you release your MediaRecorder before you initialize MediaPlayer. If it still returning 0, there is likely to be a bug. I would try different audio formats.Lambart
This solution works on some devices but not on others. Any idea why? Works on the nexus5 but not the hudl. Might it be supported media types? The file I'm trying to read is a h264 mp4.Cattleya
K
14

Try use

long totalDuration = mediaPlayer.getDuration(); // to get total duration in milliseconds

long currentDuration = mediaPlayer.getCurrentPosition(); // to Gets the current playback position in milliseconds

Division on 1000 to convert to seconds.

Hope this helped you.

Kismet answered 13/3, 2013 at 19:35 Comment(2)
MediaPlayer.getduration() returns 0 for some strange reason do you know why?Africanist
@simon you have to get duration when the media starting ..any sooner and it will return -1. Also ~@Kismet unfortunately this is not consistent all the time as there are issues with wrong durations being returned for mp4's especially those of m3u8 container.Chlortetracycline
O
5

Kotlin Extension Solution

You can add this to reliably and safely get your audio file's duration. If it doesn't exist or there is an error, you'll get back 0.

myAudioFile.getMediaDuration(context)

/**
 * If file is a Video or Audio file, return the duration of the content in ms
 */
fun File.getMediaDuration(context: Context): Long {
    if (!exists()) return 0
    val retriever = MediaMetadataRetriever()
    return try {
        retriever.setDataSource(context, uri)
        val duration = retriever.extractMetadata(METADATA_KEY_DURATION)
        retriever.release()
        duration.toLongOrNull() ?: 0
    } catch (exception: Exception) {
        0
    }
}

If you are regularly working with String or Uri for files, I'd suggest also adding these useful helpers

fun Uri.asFile(): File = File(toString())

fun String?.asUri(): Uri? {
    try {
        return Uri.parse(this)
    } catch (e: Exception) {
        Sentry.captureException(e)
    }
    return null
}

fun String.asFile() = File(this)
Omidyar answered 6/4, 2020 at 9:57 Comment(1)
MediaMetadataRetriever package has been discontinuedAflame
S
4

If the audio is from url, just wait for on prepared:

mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
             length = mp.getDuration();
        }
});
Shanteshantee answered 15/10, 2018 at 7:45 Comment(0)
V
2

You can use this readyMade method, hope this helps someone.

Example 1 : getAudioFileLength(address, true); // if you want in stringFormat Example 2 : getAudioFileLength(address, false); // if you want in milliseconds

public String getAudioFileLength(String path, boolean stringFormat) {

            Uri uri = Uri.parse(path);
            MediaMetadataRetriever mmr = new MediaMetadataRetriever();
            mmr.setDataSource(Filter_Journals.this, uri);
            String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
            int millSecond = Integer.parseInt(duration);

            if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero

            if (!stringFormat) return String.valueOf(millSecond);

            int hours, minutes, seconds = millSecond / 1000;

            hours = (seconds / 3600);
            minutes = (seconds / 60) % 60;
            seconds = seconds % 60;

            StringBuilder stringBuilder = new StringBuilder();
            if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
            else if (hours > 0) stringBuilder.append(hours).append(":");

            if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
            else stringBuilder.append(minutes).append(":");

            if (seconds < 10) stringBuilder.append("0").append(seconds);
            else stringBuilder.append(seconds);

            return stringBuilder.toString();
        }
Vacuole answered 8/10, 2020 at 12:58 Comment(0)
J
2

According to Vijay's answer, The function gives us the duration of the audio/video file but unfortunately, there is an issue of a run time exception so I sorted out and below function work properly and return the exact duration of the audio or video file.

public String getAudioFileLength(String path, boolean stringFormat) {
    StringBuilder stringBuilder = new StringBuilder();
    try {
        Uri uri = Uri.parse(path);
        MediaMetadataRetriever mmr = new MediaMetadataRetriever();
        mmr.setDataSource(HomeActivity.this, uri);
        String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
        int millSecond = Integer.parseInt(duration);
        if (millSecond < 0) return String.valueOf(0); // if some error then we say duration is zero
        if (!stringFormat) return String.valueOf(millSecond);
        int hours, minutes, seconds = millSecond / 1000;
        hours = (seconds / 3600);
        minutes = (seconds / 60) % 60;
        seconds = seconds % 60;
        if (hours > 0 && hours < 10) stringBuilder.append("0").append(hours).append(":");
        else if (hours > 0) stringBuilder.append(hours).append(":");
        if (minutes < 10) stringBuilder.append("0").append(minutes).append(":");
        else stringBuilder.append(minutes).append(":");
        if (seconds < 10) stringBuilder.append("0").append(seconds);
        else stringBuilder.append(seconds);
    }catch (Exception e){
        e.printStackTrace();
    }
    return stringBuilder.toString();
}

:)

Jones answered 21/1, 2021 at 10:44 Comment(2)
Thanks for improving the answer and save time for the other developersRadiobroadcast
Thanks Anwar for improving the answer, yes, try/catch is necessary for using the function.Vacuole
E
2

Kotlin shortest way to do it (if it is an audiofile):

private fun getDuration(absolutePath: String): String {
    val retriever = MediaMetadataRetriever()
    retriever.setDataSource(absolutePath)
    //For format in string MM:SS
    val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
    val duration = rawDuration.milliseconds
    return format("%02d:%02d", duration.inWholeMinutes, duration.inWholeSeconds % 60)
}

private fun getDurationInSeconds(absolutePath: String): Long {
    val retriever = MediaMetadataRetriever()
    retriever.setDataSource(absolutePath)
    //Return only value in seconds
    val rawDuration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLong() ?: 0L
    return rawDuration.milliseconds.inWholeSeconds
}
Emsmus answered 27/9, 2022 at 12:27 Comment(0)
B
1

For me, the AudioGraph class came to the rescue:

public static async Task<double> AudioFileDuration(StorageFile file)
        {
            var result = await AudioGraph.CreateAsync(new AudioGraphSettings(Windows.Media.Render.AudioRenderCategory.Speech));
            if (result.Status == AudioGraphCreationStatus.Success)
            {
                AudioGraph audioGraph = result.Graph;
                var fileInputNodeResult = await audioGraph.CreateFileInputNodeAsync(file);
                return fileInputNodeResult.FileInputNode.Duration.TotalSeconds;
            }
            return -1;
        }
Biddy answered 18/1, 2021 at 4:7 Comment(0)
D
-1

After you write the file, open it up in a MediaPlayer, and call getDuration on it.

Diomedes answered 13/3, 2013 at 19:29 Comment(2)
Tried that but it always returns 0 for some reasonAfricanist
Never mind I got it working! I called MediaPlayer.getDuration() in the MediaPlayer.setOnPreparedListener method and than it returns 0Africanist
C
-1

Have you looked at Ringdroid?. It's pretty light weight and the integration is straight forward. It works well with VBR media files as well.

For your problem with getting the duration, you might want to do something like below using Ringdroid.

public class AudioUtils
{
    public static long getDuration(CheapSoundFile cheapSoundFile)
    {
        if( cheapSoundFile == null)
            return -1;
        int sampleRate = cheapSoundFile.getSampleRate();
        int samplesPerFrame = cheapSoundFile.getSamplesPerFrame();
        int frames = cheapSoundFile.getNumFrames();
        cheapSoundFile = null;
        return 1000 * ( frames * samplesPerFrame) / sampleRate;
    }

    public static long getDuration(String mediaPath)
    {
        if( mediaPath != null && mediaPath.length() > 0)
            try 
            {
                return getDuration(CheapSoundFile.create(mediaPath, null));
            }catch (FileNotFoundException e){} 
            catch (IOException e){}
        return -1;
    }
}

Hope that helps

Chinn answered 3/8, 2013 at 11:6 Comment(0)
M
-1

It's simply. use RandomAccessFile Below is the code snippet to do so

 public static int getAudioInfo(File file) {
    try {
        byte header[] = new byte[12];
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
        randomAccessFile.readFully(header, 0, 8);
        randomAccessFile.close();
        return (int) file.length() /1000;
    } catch (Exception e) {
        return 0;
    }
}

You can, of course, be more complete depending on your needs

Makeshift answered 16/4, 2018 at 23:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.