Недавно мне понадобилось реализовать небольшой аудио плеер. Я, по различным причинам, выбрал библиотеку Gstreamer. И вот решил поделиться полученными знаниями. Надеюсь, приведенная ниже информация кому-то будет полезна.
И так начнем
Для начала немного разберемся с основными понятиями в Gstreamer
Элемент — является самым важным классом объектов в Gstreamer. Они могут объединятся в цепочку и создавать так называемый канал (pipeline). Каждый элемент имеет строго определенную функцию: чтение файла, ввод или вывод данных и т. д.
Гнезда(pads) — используются для передачи данных между элементами. В каждом элементе может быть от одного и выше гнезд.
Контейнер элементов (Bin) — объединяет цепочку элементов. С помощью данного контейнера можно управлять элементами как одним целым.
Канал (pipeline) — похож на бины, за исключением того, что место элементов он содержит контейнеры элементов.
Подробнее обо всем этом можно узнать в документации.
Теперь перейдем к реализации нашего класса.
Вот так вот выглядит класс нашего аудио плеера:
#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