В прошлом посте я передал картинку с камеры на html-страницу с помощью html-тега <video> и утилиты gst-launch, которая поставляется вместе с GStreamer. Однако gst-launch не очень удобно интегировать в свои программы, поэтому я написал на Си++ свой плеер на GStreamer, который использует тот же пайплайн (то есть берет картинку из /dev/video и передает на tcp порт).
За основу был взят пример из оффициальной документации GStreamer: Your first application.
Что я сделал:
Кода получилось немного, но достаточно, чтоб здесь это выглядело не очень удобно. Поэтому весь код я залил на github.
Здесь приведу только код класса Player:
За основу был взят пример из оффициальной документации GStreamer: Your first application.
Что я сделал:
- Изменил пайплайн на тот, который был нужен мне (v4l2src ! theoraenc ! oggmux ! tcpserversink).
- На основе кода примера создал отдельный класс Player, в котором инкапсулировал все данные, связаные с GStreamer.
- Обернул сырые указатели std::shared_ptr с кастомными функциями удаления, чтоб не вызывать вручную функции освобождения объектов GStreamer типа gst_object_unref.
- Создал класс СonfigManager, который используется в классе Player, чтобы читать настройки из файла (разрешение, номер порта и т.д.). Настройки хранятся в XML файле, который парсится с помощью TinyXML2.
- Ну и написал makefile (на самом деле, почти полностью скопировал отсюда).
Кода получилось немного, но достаточно, чтоб здесь это выглядело не очень удобно. Поэтому весь код я залил на github.
Здесь приведу только код класса Player:
Файл player.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #ifndef PLAYER_H_ #define PLAYER_H_ #include <gst/gst.h> #include <glib.h> #include <memory> #include <string> #include "config-manager.h" class Player { public : Player(); ~Player(); void startPlayer(); void loadConfig( const std::string & filename = "" ); private : static gboolean busCallHandlerWrapper (GstBus *bus, GstMessage *msg, gpointer data); gboolean busCallHandler(GstBus *bus, GstMessage *msg, gpointer data); std::shared_ptr<GMainLoop> loopSp; std::shared_ptr<GstElement> pipelineSp; std::shared_ptr<GstElement> sourceSp; std::shared_ptr<GstElement> encoderSp; std::shared_ptr<GstElement> muxerSp; std::shared_ptr<GstElement> sinkSp; std::shared_ptr<GstElement> filterSp; guint busWatchId; ConfigManager cm; }; |
Файл player.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #include "player.h" #include "debug/debug.h" #include <exception> #include <memory> void Player::startPlayer() { /* Set up the pipeline */ g_object_set (G_OBJECT (sinkSp.get()), "host" , cm.getHost().c_str(), NULL); g_object_set (G_OBJECT (sinkSp.get()), "port" , cm.getPort(), NULL); g_object_set (G_OBJECT (sourceSp.get()), "device" , cm.getVideodev().c_str(), NULL); std::shared_ptr<GstCaps> capsSp( gst_caps_new_simple ( "video/x-raw-yuv" , "width" , G_TYPE_INT, cm.getWidth(), "height" , G_TYPE_INT, cm.getHeight(), NULL), gst_caps_unref); g_object_set(G_OBJECT(filterSp.get()), "caps" , capsSp.get(), NULL); /* we add a message handler */ std::shared_ptr<GstBus> busSp( gst_pipeline_get_bus(GST_PIPELINE (pipelineSp.get())), gst_object_unref); busWatchId = gst_bus_add_watch ( busSp.get(), busCallHandlerWrapper, static_cast <gpointer>( this )); /* we add all elements into the pipeline */ gst_bin_add_many(GST_BIN (pipelineSp.get()), sourceSp.get(), filterSp.get(), encoderSp.get(), muxerSp.get(), sinkSp.get(), NULL); gst_element_link_many( sourceSp.get(), filterSp.get(), encoderSp.get(), muxerSp.get(), sinkSp.get(), NULL); gst_element_set_state (pipelineSp.get(), GST_STATE_PLAYING); DEBUG_PRINT(DL_INFO, "Starting playback\n" ); g_main_loop_run (loopSp.get()); } void Player::loadConfig( const std::string & filename) { if (!filename.empty()) { cm.loadConfig(filename); } } Player::Player() { gst_init (NULL, NULL); loopSp.reset(g_main_loop_new(NULL, FALSE), g_main_loop_unref); pipelineSp.reset( gst_pipeline_new( "video-player" ), gst_object_unref); sourceSp.reset( gst_element_factory_make( "v4l2src" , "camera-source" ), gst_object_unref); filterSp.reset( gst_element_factory_make ( "capsfilter" , "filter" ), gst_object_unref); encoderSp.reset( gst_element_factory_make ( "theoraenc" , "encoder" ), gst_object_unref); muxerSp.reset( gst_element_factory_make ( "oggmux" , "muxer" ), gst_object_unref); sinkSp.reset( gst_element_factory_make ( "tcpserversink" , "tcp-sink" ), gst_object_unref); if (!loopSp || !pipelineSp || !sourceSp || !filterSp || !encoderSp || !muxerSp || !sinkSp) { DEBUG_PRINT(DL_ERROR, "One of pipeline elements could not \ be created. Player not started.\n"); throw ; } } Player::~Player() { gst_element_set_state (pipelineSp.get(), GST_STATE_NULL); g_source_remove (busWatchId); } gboolean Player::busCallHandlerWrapper(GstBus* bus, GstMessage* msg, gpointer data) { Player * player = static_cast <Player *>(data); return player->busCallHandler(bus, msg, player->loopSp.get()); } gboolean Player::busCallHandler(GstBus* bus, GstMessage* msg, gpointer data) { GMainLoop *loop = static_cast <GMainLoop *>(data); switch (GST_MESSAGE_TYPE (msg)) { case GST_MESSAGE_EOS: DEBUG_PRINT(DL_INFO, "End of stream\n" ); g_main_loop_quit (loopSp.get()); break ; case GST_MESSAGE_ERROR: gchar *debug; GError *error; gst_message_parse_error (msg, &error, &debug); g_free (debug); DEBUG_PRINT(DL_ERROR, "Error: %s\n" , error->message); g_error_free (error); g_main_loop_quit (loopSp.get()); break ; default : break ; } return TRUE; } |
Файл main.cpp
1 2 3 4 5 6 7 8 9 10 11 | #include "player.h" #include "debug/debug.h" #include <pthread.h> #include <exception> int main( void ) { Player player; player.loadConfig( "config.xml" ); player.startPlayer(); return 0; } |
Комментариев нет :
Отправить комментарий