Как стать автором
Поиск
Написать публикацию
Обновить

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

Время на прочтение4 мин
Количество просмотров7.7K
Недавно мне понадобилось реализовать небольшой аудио плеер. Я, по различным причинам, выбрал библиотеку 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

Всем спасибо.
Теги:
Хабы:
Всего голосов 17: ↑16 и ↓1+15
Комментарии5

Публикации

Ближайшие события