Мультимедиа

Работа с метаданными медиафайлов: теги, обложки, длительность

2 просмотров

Введение

Помимо самого аудио/видеоконтента, медиафайлы обычно содержат метаданные — название трека, исполнителя, альбом, обложку, длительность — в этой статье разберём чтение этих метаданных через QMediaPlayer/QMediaMetaData, что необходимо для построения полноценного, информативного интерфейса медиатеки или плеера.

Концепция

QMediaMetaData предоставляет унифицированный доступ к набору стандартных полей метаданных (название, исполнитель, альбом, длительность, обложка), независимо от конкретного, специфичного для формата файла способа их физического хранения (ID3-теги для MP3, Vorbis-комментарии для OGG, и так далее) — QMediaPlayer автоматически считывает доступные метаданные при загрузке источника и предоставляет их через единообразный интерфейс, абстрагирующий разработчика от деталей конкретного формата метаданных.

Пример кода

// Чтение метаданных после загрузки источника
void displayTrackInfo(QMediaPlayer *player, QLabel *titleLabel, QLabel *artistLabel, QLabel *coverArtLabel)
{
    QObject::connect(player, &QMediaPlayer::metaDataChanged, [=]() {
        QMediaMetaData metaData = player->metaData();

        QString title = metaData.value(QMediaMetaData::Title).toString();
        QString artist = metaData.value(QMediaMetaData::ContributingArtist).toString();

        titleLabel->setText(title.isEmpty() ? "Неизвестный трек" : title);
        artistLabel->setText(artist.isEmpty() ? "Неизвестный исполнитель" : artist);

        QVariant coverArt = metaData.value(QMediaMetaData::ThumbnailImage);
        if (coverArt.canConvert<QImage>()) {
            coverArtLabel->setPixmap(QPixmap::fromImage(coverArt.value<QImage>()));
        } else {
            coverArtLabel->setPixmap(QPixmap(":/icons/default_album_art.png")); // запасной вариант
        }
    });
}
// Построение медиатеки с предварительным сканированием метаданных файлов без полного воспроизведения
struct TrackInfo { QString filePath, title, artist; qint64 durationMs; };

QVector<TrackInfo> scanMusicLibrary(const QStringList &filePaths)
{
    QVector<TrackInfo> library;
    QMediaPlayer scannerPlayer; // один временный плеер, переиспользуемый для сканирования каждого файла

    for (const QString &path : filePaths) {
        scannerPlayer.setSource(QUrl::fromLocalFile(path));
        // Нужно дождаться готовности метаданных — в реальном коде через ожидание сигнала
        // metaDataChanged, здесь упрощённо для иллюстрации сути подхода

        QMediaMetaData metaData = scannerPlayer.metaData();
        TrackInfo info;
        info.filePath = path;
        info.title = metaData.value(QMediaMetaData::Title).toString();
        info.artist = metaData.value(QMediaMetaData::ContributingArtist).toString();
        info.durationMs = scannerPlayer.duration();
        library.append(info);
    }
    return library;
}

Пояснения к коду

displayTrackInfo показывает типичный паттерн отображения метаданных в интерфейсе плеера — подписка на metaDataChanged (а не попытка прочитать метаданные синхронно сразу после setSource(), поскольку их извлечение требует времени на анализ файла) и явная обработка случая отсутствия конкретного поля метаданных (запасной текст «Неизвестный трек», запасное изображение обложки), что важно, поскольку не каждый файл содержит полный набор метаданных. scanMusicLibrary показывает практический подход к построению медиатеки — переиспользование одного QMediaPlayer для последовательного сканирования множества файлов, хотя в реальном, production-коде это требовало бы явного, асинхронного ожидания готовности метаданных для каждого файла перед переходом к следующему, а не предположения о их немедленной доступности.

Подводные камни

  • Попытка синхронного чтения метаданных немедленно после setSource(), аналогично подводному камню статьи 468 про durationChanged — метаданные становятся доступны только после того, как плеер успевает проанализировать файл, что требует времени (особенно заметного при сканировании большого количества файлов подряд, как в примере медиатеки), и код, не дожидающийся сигнала metaDataChanged, рискует получить пустые или неполные данные.
  • Предположение о наличии всех «стандартных» полей метаданных у каждого файла без проверки — многие файлы (особенно полученные не из коммерческих источников, а самостоятельно записанные или сконвертированные) могут не содержать части или вовсе всех привычных полей метаданных (исполнитель, альбом, обложка), и интерфейс приложения должен корректно, не вызывающим путаницу образом обрабатывать отсутствие конкретных полей, а не предполагать их безусловное наличие.
  • Неэффективное, последовательное сканирование большого количества файлов медиатеки через полное создание/использование QMediaPlayer для каждого файла, что может быть заметно медленнее специализированных, более легковесных библиотек извлечения только метаданных без полноценной инициализации всего аппарата воспроизведения — для библиотек с очень большим количеством файлов (тысячи треков) стоит рассмотреть более специализированные, легковесные средства извлечения метаданных (отдельные библиотеки тегов, например TagLib), либо явно асинхронное, фоновое сканирование, не блокирующее остальной интерфейс приложения во время длительного процесса.
  • Игнорирование возможного изменения метаданных файла на диске после первоначального сканирования и кеширования этой информации в приложении (пользователь отредактировал теги трека во внешнем редакторе) — закешированные при первом сканировании метаданные могут со временем устареть относительно реального, изменившегося содержимого файла, и для приложений, важных с точки зрения актуальности отображаемой информации, стоит предусмотреть механизм обновления кеша (повторное сканирование при обнаружении изменения файла, например через отслеживание времени модификации файла).