【问题标题】:get duration of audio file获取音频文件的持续时间
【发布时间】:2013-03-01 22:31:12
【问题描述】:

我制作了一个录音机应用程序,我想在列表视图中显示录音的持续时间。我这样保存录音:

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();
    }

如何获取此文件的持续时间(以秒为单位)?

提前致谢

---编辑 我搞定了,我在 MediaPlayer.setOnPreparedListener() 方法中调用了 MediaPlayer.getduration(),所以它返回了 0。

【问题讨论】:

    标签: android audio audio-recording duration


    【解决方案1】:

    MediaMetadataRetriever 是一种轻量级且高效的方法。 MediaPlayer 太重了,在滚动、分页、列表等高性能环境中可能会出现性能问题。

    此外,Error (100,0) 可能会发生在 MediaPlayer 上,因为它很繁重,有时需要一次又一次地重新启动。

    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);
    

    【讨论】:

    • 这很好发现,因为 MediaPlayer.getDuration() 中似乎有一个错误——至少对于 mp4 来说是这样。谢谢!
    • 如何获取 MediaMetadataCompat.METADATA_KEY_DURATION?
    • 我不知道MediaMetadataCompat,但MediaMetadataRetriever.METADATA_KEY_DURATION 已被弃用。最好得到这样的持续时间:String durationStr = mmr.ExtractMetadata(MetadataKey.Duration);
    • @dstrube 你从哪里得到这些信息?根据 Android 文档,它没有被弃用 developer.android.com/reference/android/media/…
    • @ka3ak 嗯,这很奇怪。我不知道我从哪里得到的。那是几个月前的事了,所以我不能说我在看什么代码,我在哪里收到警告说它已被弃用。 (通过我的一些常用代码进行快速搜索并没有找到 METADATA_KEY_DURATION 或 ExtractMetadata 的任何结果。)我收回我的评论并感谢您纠正我。
    【解决方案2】:

    最快的方法是通过MediaMetadataRetriever。但是,有一个catch

    如果您使用 URI 和上下文来设置数据源,您可能会遇到错误 https://code.google.com/p/android/issues/detail?id=35794

    解决方案是使用文件的绝对路径来检索媒体文件的元数据。

    下面是sn-p的代码

     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));
                }
    

    现在您可以使用以下任一格式将毫秒转换为人类可读的格式

         /**
             * 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;
            }
    

    【讨论】:

    • 绝妙的解决方案!这对我很有效。只需从文件中提取元数据,无需繁琐的 MediaPlayer。
    【解决方案3】:

    尝试这个以毫秒为单位获取持续时间:

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

    或者以纳秒为单位测量从recorder.start()recorder.stop() 所经过的时间:

    long startTime = System.nanoTime();    
    // ... do recording ...    
    long estimatedTime = System.nanoTime() - startTime;
    

    【讨论】:

    • 第一个返回 0,如果我想使用第二个解决方案,我必须播放文件,我不想这样做
    • 您不必播放第二个文件。您在用户开始录制时设置 startTime 并在用户停止录制时计算estimateTime。对于第一个选项,请确保在初始化 MediaPlayer 之前释放 MediaRecorder。如果它仍然返回 0,则可能存在错误。我会尝试不同的音频格式。
    • 此解决方案适用于某些设备,但不适用于其他设备。知道为什么吗?适用于nexus5,但不适用于hudl。可能是支持的媒体类型吗?我要读取的文件是 h264 mp4。
    【解决方案4】:

    尝试使用

    long totalDuration = mediaPlayer.getDuration(); // to get total duration in milliseconds
    
    long currentDuration = mediaPlayer.getCurrentPosition(); // to Gets the current playback position in milliseconds
    

    除以 1000 以转换为秒。

    希望这对您有所帮助。

    【讨论】:

    • MediaPlayer.getduration() 出于某种奇怪的原因返回 0 你知道为什么吗?
    • @simon 你必须在媒体开始时获得持续时间......任何时候它都会返回-1。此外 ~@AwadKab 不幸的是,这一直不一致,因为 mp4 特别是 m3u8 容器返回错误的持续时间存在问题。
    【解决方案5】:

    根据 Vijay 的回答,该函数为我们提供了音频/视频文件的持续时间,但不幸的是,存在运行时异常的问题,因此我整理出以下函数正常工作并返回音频的确切持续时间或视频文件。

    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();
    }
    

    :)

    【讨论】:

    • 感谢您改进答案并为其他开发人员节省时间
    【解决方案6】:

    Kotlin 扩展解决方案

    您可以添加它以可靠且安全地获取音频文件的持续时间。如果不存在或有错误,则返回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
        }
    }
    

    如果您经常使用 String 或 Uri 处理文件,我建议您也添加这些有用的帮助程序

    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)
    

    【讨论】:

    • MediaMetadataRetriever 包已停产
    【解决方案7】:

    如果音频来自 url,则等待准备就绪:

    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                 length = mp.getDuration();
            }
    });
    

    【讨论】:

      【解决方案8】:

      你可以使用这个现成的方法,希望这对某人有帮助。

      示例 1:getAudioFileLength(address, true); // if you want in stringFormat 示例 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();
              }
      

      【讨论】:

        【解决方案9】:

        对我来说,AudioGraph 类来拯救:

        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;
                }
        

        【讨论】:

          【解决方案10】:

          写入文件后,在 MediaPlayer 中打开它,然后对其调用 getDuration。

          【讨论】:

          • 试过了,但由于某种原因它总是返回 0
          • 没关系,我让它工作了!我在 MediaPlayer.setOnPreparedListener 方法中调用了 MediaPlayer.getDuration(),然后它返回 0
          【解决方案11】:

          你看过Ringdroid吗?它的重量很轻,集成很简单。它也适用于 VBR 媒体文件。

          对于获取持续时间的问题,您可能需要使用 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;
              }
          }
          

          希望有帮助

          【讨论】:

            【解决方案12】:

            这很简单。使用 RandomAccessFile 下面是代码 sn -p 这样做

             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;
                }
            }
            

            当然,你可以根据自己的需要做得更完整

            【讨论】:

              猜你喜欢
              • 2014-02-08
              • 2010-11-17
              • 2020-06-02
              • 1970-01-01
              • 1970-01-01
              • 2016-04-23
              • 2015-10-13
              • 2013-10-27
              • 1970-01-01
              相关资源
              最近更新 更多