Простой аудио плеер на Gstreamer

в 12:12, , рубрики: gstreamer, разработка, метки:

Недавно мне понадобилось реализовать небольшой аудио плеер. Я, по различным причинам, выбрал библиотеку Gstreamer. И вот решил поделиться полученными знаниями. Надеюсь, приведенная ниже информация кому-то будет полезна.

И так начнем

Для начала немного разберемся с основными понятиями в Gstreamer

Элемент — является самым важным классом объектов в Gstreamer. Они могут объединятся в цепочку и создавать так называемый канал (pipeline). Каждый элемент имеет строго определенную функцию: чтение файла, ввод или вывод данных и т. д.

Гнезда(pads) — используются для передачи данных между элементами. В каждом элементе может быть от одного и выше гнезд.

Контейнер элементов (Bin) — объединяет цепочку элементов. С помощью данного контейнера можно управлять элементами как одним целым.

Канал (pipeline) — похож на бины, за исключением того, что место элементов он содержит контейнеры элементов.

Подробнее обо всем этом можно узнать в документации.

Теперь перейдем к реализации нашего класса.

Вот так вот выглядит класс нашего аудио плеера:

audioengine.h
#ifndef AUDIOENGINE_H
#define AUDIOENGINE_H

#include <gst/gst.h>
#include <glib.h>
#include <QObject>

class AudioEngine : public QObject {
    Q_OBJECT
public:
    AudioEngine(QObject *parent = 0);
    ~AudioEngine();

    int Init();

    void MusicPlay();
    void MusicPaused();
    void MusicStop();
    void AddFile(char *file);
    void SetVolume(gdouble val);
    gint64 GetMusicPosition();
    gint64 GetMusicDuration();
    void SetMusicPosition(gint64 pos);

private:
    GstElement *pipeline;
    GstElement *source;
    GstElement *volume;
    gint64 pos;

    static void OnPadAdded(GstElement *element, GstPad *pad, gpointer data);

private slots:
};

#endif // AUDIOENGINE_H

В функции Init() мы инициализируем библиотеку Gstreamer:

gst_init(0, 0);

данная функция принимает аргументы командной строки argv и argc, в нашем случаи их можно опустить.

Далее создаем канал:

pipeline = gst_pipeline_new("audio-player");

и необходимые нам элементы:

source = gst_element_factory_make("filesrc", NULL);
demuxer = gst_element_factory_make("decodebin", NULL);
decoder = gst_element_factory_make("audioconvert", NULL);
volume = gst_element_factory_make("volume", NULL);
conv = gst_element_factory_make("audioconvert",  NULL);
sink = gst_element_factory_make("autoaudiosink", NULL);

Элемент source — предназначен для чтения аудио файлов.
demuxer — используется для декодирования аудио файла.
decoder и conv — для конвертирования аудио файла в другой формат.
volume — предназначен для регулирования громкости звука.
sinc — автоматически определяет аудио устройство и выводит данные на него…

Следует заметить что demuxer создает гнезда для каждого элемента потока и нам придется установить обработчик событий для связи demuxer с decoder. В этом нам поможет функция OnPadAdded().

Обработчик событий в нашем коде выглядит так:

g_signal_connect(demuxer, "pad-added", G_CALLBACK(OnPadAdded), decoder);

Добавляем все созданные элементы в канал:

gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, volume, conv, sink, NULL);

и линкуем элементы между собой.

gst_element_link (source, demuxer);
gst_element_link_many (decoder, volume, conv, sink, NULL);

Функция для добавления файла в плеер выглядит примерно так:

void AudioEngine::AddFile(char *file) {
    g_object_set(G_OBJECT(source), "location", file, NULL);
}

функция g_object_set() передает аргументы элементу source. В данном случае аргумент «location» говорит нам о том, что аудио-файл находится на локальной машине, file — путь к нашему файлу. Последний параметр NULL говорит нам о том, что элемент больше в никаких аргументах не нуждается.

Функции для запуска, остановки воспроизведения выглядят так:

void AudioEngine::AddFile(char *file) {
    g_object_set(G_OBJECT(source), "location", file, NULL);
}
void AudioEngine::MusicPlay() {
    gst_element_set_state(pipeline, GST_STATE_PLAYING);
}
void AudioEngine::MusicPaused() {
    gst_element_set_state(pipeline, GST_STATE_PAUSED);
}

тут вроде все понятно.

Функция для регулирование громкости:

void AudioEngine::SetVolume(gdouble val) {
    g_object_set(G_OBJECT(volume), "volume", val, NULL);
}

Функции для для получения продолжительности трека и текущего его положения:

gint64 AudioEngine::GetMusicDuration() {
    gint64 len;
    gst_element_query_duration(pipeline, GST_FORMAT_TIME, &len);
    return len;
}
gint64 AudioEngine::GetMusicPosition() {
    gint64 pos;
    gst_element_query_position(pipeline, GST_FORMAT_TIME, &pos);
    return pos;
}

следует иметь в виду что функции возвращают значения времени в наносекундах.

И наконец-то функция для смены позиции трека:

void AudioEngine::SetMusicPosition(gint64 pos) {
    gst_element_set_state(pipeline, GST_STATE_PAUSED);
    gst_element_seek_simple(pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, pos);
    gst_element_set_state(pipeline, GST_STATE_PLAYING);
}

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

Полный код приведен ниже по ссылке в месте с небольшим GUI реализованном на Qt.
Исходники на GitHab

Всем спасибо.

Автор: TriKrista

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js