Как я могу получить продолжительность файла MP3 (CBR или VBR) с очень маленькой библиотекой или собственным кодом c / c ++? - PullRequest
4 голосов
/ 17 августа 2010

Я не могу использовать любой код mp3, который запатентован Fraunhofer, поэтому нет кодировщиков ИЛИ декодеров (например, ffmpeg, lame, MAD и т. Д.), Плюс он слишком большой.

Я делаю это в Windows, но IMediaDet из DirectShow, кажется, со временем замедляет , вызывая его несколько сотен раз, приводит к ползанию моей системы, даже повторно используя тот же объект интерфейса и просто помещая имя файла и продолжительность получения!

Итак, есть ли какой-нибудь код, который может читать VBR-файлы с C / C ++ и получать длительность?

Здесь был еще один пост, посвященный CBR в C ++, но код делает кучу предположений и, конечно, не будет работать для VBR.

Ответы [ 4 ]

4 голосов
/ 19 августа 2010

Большинство файлов MP3 имеют заголовок ID3 .Это не сложно, чтобы расшифровать это и получить продолжительность.

Вот некоторый очень базовый и безобразный код, иллюстрирующий эту технику.

#include <iostream>
#include <iomanip>

size_t GetMP3Duration(const std::string sFileName);

int main(int argc, char* argv[])
{
    try
    {
        size_t nLen = GetMP3Duration(argv[1]);
        if (nLen==0)
        {
            std::cout << "Not Found" << std::endl;
        }
        else
        {
            std::cout << nLen << " miliseconds" << std::endl;
            std::cout << nLen/60000 << ":";
            nLen %= 60000;
            std::cout << nLen/1000 << ".";
            std::cout << std::setw(3) << std::setfill('0') << nLen%1000 << std::endl;
        }
    }
    catch (std::exception &e)
    {
        std::cout << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

#include <cstring>
#include <vector>
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstdlib>

unsigned DecodeMP3SafeInt(unsigned nVal)
{
    // nVal has 4 bytes (8-bits each)
    //  - discard most significant bit from each byte
    //  - reverse byte order
    //  - concatenate the 4 * 7-bit nibbles into a 24-bit size.
    unsigned char *pValParts = reinterpret_cast<unsigned char *>(&nVal);
    return (pValParts[3] & 0x7F)         |
            ((pValParts[2] & 0x7F) << 7)  | 
            ((pValParts[1] & 0x7F) << 14) | 
            ((pValParts[0] & 0x7F) << 21);
}

#pragma pack(1)
struct MP3Hdr {
    char tag[3];
    unsigned char maj_ver;
    unsigned char min_ver;
    unsigned char flags;
    unsigned int  size;
};
struct MP3ExtHdr {
    unsigned int  size;
    unsigned char num_flag_bytes;
    unsigned char extended_flags;
};
struct MP3FrameHdr {
    char frame_id[4];
    unsigned size;
    unsigned char flags[2];
};
#pragma pack()

size_t GetMP3Duration(const std::string sFileName)
{
    std::ifstream fin(sFileName.c_str(), std::ifstream::binary);
    if (!fin) 
        throw std::invalid_argument("Cannot open file");

    // Read Header
    MP3Hdr hdr = { 0 };
    fin.read(reinterpret_cast<char *>(&hdr), sizeof(hdr));
    if (!fin.good())
        throw std::invalid_argument("Error reading file");

    if (0 != ::memcmp(hdr.tag, "ID3", 3))
        throw std::invalid_argument("Not an MP3 File");

    // Read extended header, if present
    if (0 != (hdr.flags&0x40))
    {
        fin.seekg(sizeof(MP3ExtHdr), std::ifstream::cur);
        if (!fin.good())
            throw std::invalid_argument("Error reading file");
    }

    // read a chunk of file.
    const size_t nDefaultSize(2048);
    std::vector<char> vBuff(nDefaultSize);
    fin.read(&vBuff[0], vBuff.size());
    size_t nSize = fin.gcount();
    if (!nSize)
        throw std::invalid_argument("Error reading file");
    vBuff.resize(nSize);

    size_t nUsed = 0;
    while (nSize-nUsed > sizeof(MP3FrameHdr))
    {
        MP3FrameHdr *pFrame = reinterpret_cast<MP3FrameHdr *>(&vBuff[nUsed]);
        nUsed += sizeof(MP3FrameHdr);
        size_t nDataLen = DecodeMP3SafeInt(pFrame->size);
        if (nDataLen > (nSize-nUsed))
            throw std::invalid_argument("Corrupt file");

        if (!::isupper(pFrame->flags[0])) // past end of tags
            return 0;

        if (0 == ::memcmp(pFrame->frame_id, "TLEN", 4))
        {
            // skip an int
            nUsed += sizeof(int);
            // data is next
            return atol(&vBuff[nUsed]);
        }
        else
        {
            nUsed += nDataLen;
        }
    }
    return 0;
}
1 голос
/ 17 августа 2010

Я нашел библиотеку, которая делает это, LGPL v3: http://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx

0 голосов
/ 07 сентября 2010

Джеф,

единственный действительный способ - просмотреть весь mp3-файл, найти внутри него каждый mp3-кадр и вычислить для них общую продолжительность.

Основной характеристикой mp3-файла является то, что его плотность может отличаться, а также то, что в него может быть включено много других двоичных данных. Например, теги ID3, которые любой декодер пропустит при чтении.

В любом случае - посмотрите здесь информацию о заголовке mp3-кадра:

http://www.mp3 -converter.com / mp3codec / mp3_anatomy.htm

попытайтесь создать код, который будет правильно анализировать заголовок за заголовком, вычислять их длительность (из частоты дискретизации), а затем суммировать длительности для всех кадров.

Вам не нужно декодировать кадры, просто используйте заголовки из них.

Если вы не против LGPL, попробуйте http://sourceforge.net/projects/mpg123net/

0 голосов
/ 17 августа 2010

Как насчет tagLib или id3lib ?

Они не являются декодерами сами по себе, они скорее извлекают трек / исполнителя / альбом и множество другой информации, которая позволит вам делать то, что вам нужно ...

...