Android - программно получить продолжительность аудиофайла AMR

Как получить продолжительность файла AMR?

mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

Я хочу получить продолжительность файла после остановки записи БЕЗ создания какого-либо MediaPlayer и получить от него продолжительность. Для обычного файла Wav я просто делаю это:

fileLength / byteRate

но для AMR я не знал byteRate, и я не уверен, что это будет нормально, поскольку WAV - это необработанные данные PCM (несжатые), а AMR сжат.


person Alexandru Circus    schedule 03.07.2013    source источник


Ответы (2)


Может быть, контейнер 3GP содержит информацию о длине содержимого? Доступна спецификация формата файла 3GPP, если вы хотите ее прочитать.


Для необработанного файла .amr вам придется просмотреть все кадры, чтобы найти длину звука, поскольку каждый кадр может быть закодирован с разным битрейтом.

Процесс для этого будет следующим:

  • Пропустить первые 6 байт файла (подпись AMR).

  • Остальная часть файла будет звуковыми кадрами, каждый из которых начинается с однобайтового заголовка. Прочитайте этот байт и посмотрите на биты 3..6 (режим кодека). Для AMR-NB действительные режимы кодека — 0..7, которые можно сопоставить с размером кадра в байтах, используя приведенную ниже таблицу.

  • Как только вы узнаете размер текущего кадра, пропустите его и проанализируйте следующий кадр. Повторяйте, пока не дойдете до конца файла.

Если вы подсчитали количество кадров в файле, вы можете умножить это число на 20, чтобы получить длину звука в миллисекундах.

Таблица размеров кадра:

Codec mode  Frame size
----------------------
0             13
1             14
2             16
3             18
4             20
5             21
6             27
7             32

(Источник)

person Michael    schedule 04.07.2013
comment
Спасибо за ваш ответ, он мне очень помогает. Думаю, я могу даже добавить 2 файла amr, просто получив все байты, начиная с 7-го из файла, и присоединив их к концу второго. Просто добавление кадров... Я блуждаю, если мне нужно что-то изменить в добавленном заголовке файла, но я думаю, что нет... - person Alexandru Circus; 04.07.2013
comment
Заголовок файла должен быть просто подписью, идентифицирующей содержимое как данные AMR-NB, поэтому объединение должно работать так, как вы это описали. Однако я не проверял это на себе. - person Michael; 04.07.2013
comment
@AlexandruCircus: используя биты 3,4,5,6, я получаю 9 (1-0-0-1), таблица отображается только до 7, сталкивались ли вы с такими случаями? Если да, то что вы сделали, чтобы решить эту проблему? - person SRB; 01.11.2019
comment
@SRB: это, вероятно, кадр SID (дескриптор вставки тишины - используется для создания комфортного шума). - person Michael; 01.11.2019
comment
Привет @Michael, так какой размер кадра для такого кадра? Будем ли мы по-прежнему использовать биты с 3 по 5 и определять размер кадра на основе таблицы в приведенном выше ответе? - person SRB; 01.11.2019
comment
Я не уверен, какой размер у кадров SID. Вы можете найти документацию для этого в Интернете, если будете искать. - person Michael; 01.11.2019

Код Java: https://blog.csdn.net/fjh658/article/details/12869073

Код С# (из MemoryStream):

private double getAmrDuration(MemoryStream originalAudio)
    {
            double duration = -1;
            int[] packedSize = new int[] { 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 };

            long length = originalAudio.Length;
            int pos = 6;

            int frameCount = 0;

            int packedPos = -1;

            byte[] datas = new byte[1];

            while ((pos <= length))
            {
            originalAudio.Seek(pos, SeekOrigin.Begin);
                if ((originalAudio.Read(datas, 0, 1) != 1))
                {
                    duration = length > 0 ? ((length - 6) / 650) : 0;
                    break;
                }


                packedPos = (datas[0] >> 3) & 0x0F;
                pos += packedSize[packedPos] + 1;
                frameCount++;
            }

            /// //////////////////////////////////////////////////
            duration = (duration + (frameCount * 20));
            // 'p*20

        return duration/1000;
    }
person Cinue Leonel Arce Orozco    schedule 16.01.2020
comment
Незначительный отзыв: было бы полезно, если бы вы добавили контекст вместе с этим ответом, а не просто копировали/вставляли код. Спасибо за ответ! - person Mark Han; 17.01.2020