Неисправность тройника элемента Haskell GStreamer (1-N) - PullRequest
9 голосов
/ 19 декабря 2011

Проблема, с которой я столкнулся, связана со следующим фрагментом кода:

module Main(main) where

import qualified Media.Streaming.GStreamer as GS
import Data.Maybe
import System.IO
import System.Exit
import System.Glib.MainLoop as Glib
import System.Glib.Signals as Glib
import System.Glib.Properties as Glib


makeElement:: String → String → IO GS.Element
makeElement elementType elementName = do
    element ← GS.elementFactoryMake elementType (Just elementName)
    case element of
        Just element' → return element'
        Nothing → do
            hPutStrLn stdout ("Cannot create element!")
            hFlush stdout
            exitFailure

player =  do
    GS.init

    pipeline ← GS.pipelineNew "video-stream"

    source  ← makeElement "v4l2src" "video-source"
    color   ← makeElement "ffmpegcolorspace" "video-color"
    tee     ← makeElement "tee" "stream-tee"
    rQ      ← makeElement "queue" "record-queue"
    vQ      ← makeElement "queue" "video-queue"
    encoder ← makeElement "y4menc" "video-encoder"
    rSink   ← makeElement "filesink" "record-sink"
    sink    ← makeElement "ximagesink" "video-sink"

    let elements = [source,color,encoder,rSink,vQ,rQ,sink,tee]

    Glib.objectSetPropertyString "location" rSink "rec"

    mapM_ (GS.binAdd (GS.castToBin pipeline)) elements

    -- Request Pads from tee
    dPad ← GS.elementGetRequestPad tee "src%d"
    rPad ← GS.elementGetRequestPad tee "src%d"
    -- Request Static Pads from queue
    sDPad ← GS.elementGetStaticPad vQ "sink"
    sRPad ← GS.elementGetStaticPad rQ "sink"
    -- Link tee source to queue sink
    GS.padLink (fromJust dPad) (fromJust sDPad)
    GS.padLink (fromJust rPad) (fromJust sRPad)

    GS.elementReleaseRequestPad tee $ fromJust dPad
    GS.elementReleaseRequestPad tee $ fromJust rPad

    GS.elementLink source color
    GS.elementLink color tee
    GS.elementLink vQ sink
    GS.elementLink rQ encoder
    GS.elementLink encoder rSink


    GS.elementSetState pipeline GS.StatePlaying

main = do
    loop ← Glib.mainLoopNew Nothing False
    player
    Glib.mainLoopRun loop

Код компилируется нормально, светодиод камеры включается и файл создается, но затем НИЧЕГО.Без элементов tee и queue отдельная настройка для записи / отображения видео работает просто отлично. Кроме того, тот же конвейер работает отлично, если я тестирую его с помощью gst-launch.Здесь я что-то упускаю из-за того, как работает gstreamer, но я не могу понять, что.

Кроме того, если это помогает, я собираюсь использовать ArchLinux, используя:
- GHC 7.0.3;
- gstreamer-bindings 0.12.1;
- gtk2hs 0,12,2;
- gstreamer 0.10.35-1;
- бой 1.2.10-9.

1 Ответ

10 голосов
/ 19 декабря 2011

РАЗРЕШЕНО

Я нашел свое решение, и далее следует длинный пост, но, пожалуйста, потерпите меня.Я должен поделиться своим разочарованием с кем-то.

После многих попыток с ошибками я решил вернуться к тестированию некоторых настроек с помощью gst-launch.Это помогло мне выяснить, что после элемента очереди, который буферизует часть, которая отправляется в файловую систему, мне понадобился еще один элемент ffmpegcolorspace для настройки правильного формата видео, как мне кажется.В этот момент я не собирался снова пробовать это на Хаскеле, я подумал, что мне нужно «приблизиться», поэтому я решил попробовать это на C. Как примечание, я не знаю C, я могу понятьсинтаксис, но это все ... и ради бога, я только сейчас пытаюсь выучить Haskell.Чтобы продолжить, я решил также попробовать использовать «GS.elementGetCompatiblePad» на элементе tee, чтобы я мог быть уверен, что пэды будут связываться с очередью.

Код C, который я сшил вместе, таков:

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

int
main (int argc,char *argv[])
{

    GstElement *pipeline, *source, *color, *color2 , *color3, *tee, *rQ, *vQ, *encoder,   *fSink , *sink;
    GMainLoop *loop;
    loop = g_main_loop_new (NULL,FALSE);
    /* initialize gstreamer */
    gst_init(&argc,&argv);

    /* creating elements */
    pipeline = gst_pipeline_new("stream-pipeline");

    source = gst_element_factory_make ("v4l2src","stream-source");
    color = gst_element_factory_make ("ffmpegcolorspace","video-color");
    tee = gst_element_factory_make ("tee","stream-tee");
    rQ = gst_element_factory_make ("queue","record-queue");
    vQ = gst_element_factory_make ("queue","video-queue");
    encoder = gst_element_factory_make ("theoraenc","video-encoder");
    fSink = gst_element_factory_make ("filesink","record-sink");
    sink = gst_element_factory_make ("ximagesink","video-sink");
    color2 = gst_element_factory_make ("ffmpegcolorspace","video-color2");
    color3 = gst_element_factory_make ("ffmpegcolorspace","video-color3");
    /*check that the elements were created */

    if (!source || !color || !tee || !rQ || !vQ || !encoder || !fSink || !sink){
        g_printerr("One element could not be created!");
        return -1;
    }
    /*set file output location */
    g_object_set(G_OBJECT (fSink),"location","rec",NULL);

    gst_bin_add_many (GST_BIN(pipeline),
                        source,color,color2,color3,tee,rQ,vQ,encoder,fSink,sink,NULL);

    /* get request pads */
    GstPad *dPad, *rPad, *sDPad, *sRPad;

    sDPad = gst_element_get_static_pad(vQ,"sink");
    sRPad = gst_element_get_static_pad(rQ,"sink");
    dPad = gst_element_get_compatible_pad(tee,sDPad,GST_CAPS_ANY);
    rPad = gst_element_get_compatible_pad(tee,sRPad,GST_CAPS_ANY);

    /*link pads*/
    gst_pad_link(dPad,sDPad);
    gst_pad_link(rPad,sRPad);

    /*unref pads */
    gst_object_unref(GST_OBJECT(dPad));
    gst_object_unref(GST_OBJECT(rPad));
    gst_object_unref(GST_OBJECT(sDPad));
    gst_object_unref(GST_OBJECT(sRPad));

    /*link elements */
    gst_element_link(source,tee);
    gst_element_link_many(rQ,color2,encoder,fSink,NULL);
    gst_element_link_many(vQ,color3,sink),NULL;

    /*set the pipeline state to playing */
    gst_element_set_state(pipeline,GST_STATE_PLAYING);

    g_main_loop_run (loop);

    gst_element_set_state(pipeline,GST_STATE_NULL);
    gst_object_unref(GST_OBJECT(pipeline));

    return 0;

}


Чтобы использовать «gst_element_get_compatible_pad», мне сначала нужно было получить статические пэды из элементов очереди, чтобы я мог переключить эти четыре связанные строки.Я пробую это, и Абракадабра ... о нет, подожди ... камера запускается, файл создается, и появляется окно с видео, но черное окно остается черным!


Нет проблем, я говорю, запустите программу с gst-debug-level = 5 (=))) да, правильно, попробуйте прочитать весь вывод. Я сдаюсь на данный момент, и я подумал, что может бытьэто связано с тем, что элементы в моем конвейере не работают вместе, поэтому я кодирую другой конвейер на C, но на этот раз что-то более простое только с аудио-файлами.
У меня был тот же результат, поэтому я решил снова выполнить отладку, на этот раз с уровнем запуска 3, и я начал читать все это построчно.


Где-то там я нашел это:


пытается связать поток-тройку: src0 и очередь записей: сток
пытается связать поток-тройку: src0 и видео-очередь: приемник


здесь происходит что-то неприятное


связанный поток-тройник: src0 и видео-очередь: сток, успешно
пытается связать поток-тройник:src0 и очередь записей: сток
src stream-tee: src0 уже был связан с видео-очередью: сток


И это сдается!
Полагаю, я должен вернуться, используя gst_element_get_request_pad, но разве я уже не пробовал это?Поэтому я переключаюсь обратно на vim и заменяю все вхождения 'gst_element_get_compatible_pad аналогом запроса следующим образом:

sDPad = gst_element_get_static_pad(vQ,"sink");
sRPad = gst_element_get_static_pad(rQ,"sink");
dPad = gst_element_get_request_pad(tee,"src%d");
rPad = gst_element_get_request_pad(tee,"src%d");


Я смотрю на этот код и говорю себе: "Ты дурак", вот гдеэто все началось;сделай глубокий вдох;В конце концов, это то, на что жалуется отладчик, поэтому я компилирую, запускаю и вуаля.Я нашел свое решение.


Эти четыре строки нужно было перевернуть, мне сначала нужно было получить ссылку на статические площадки, а затем запросить ссылку на панель запроса на элементе tee.
Я возвращаюсь к счастливому человеку в haskell. Я внедряю свое решение, компилирую, запускаю, камера запускается, файл создается и ... просто так ... ничего, даже черный экран.
Наполненный гневом, я просто комментирую строки, где я освобождаю планшеты с запросами и решаю снова скомпилировать и запустить, моя шея некоторое время назад начала болеть.
Опять же, по волшебству все это работает, у меня есть видео на экране и в файле.
Полагаю, Хаскеллу просто нравится держаться крепче, и иногда вам просто нужно пойти с чем-то бесполезным.Документ gstreamer четко указывает на выпуск, выпуск, выпуск.

Окончательный код haskell:

module Main(main) where

import qualified Media.Streaming.GStreamer as GS
import Data.Maybe
import System.Exit
import System.Glib.MainLoop as Glib
import System.Glib.Signals as Glib
import System.Glib.Properties as Glib

makeElement:: String → String → IO GS.Element
makeElement elementType elementName = do
        element ← GS.elementFactoryMake elementType (Just elementName)
        case element of
            Just element' → return element'
            Nothing → do
                    putStrLn "Cannot create element!"
                    exitFailure

linkSPadToStaticSink::(GS.ElementClass object, GS.ElementClass elementT) ⇒ object →     elementT → IO (Glib.ConnectId object)
linkSPadToStaticSink elSrc elSink = do
            Glib.on elSrc GS.elementPadAdded (λpad → do
                                                    sinkPad ← GS.elementGetStaticPad elSink "sink"
                                                    GS.padLink pad (fromJust sinkPad)
                                                    return ∅)

player =  do
        GS.init
        pipeline ← GS.pipelineNew "video-stream"
        source ← makeElement "v4l2src" "video-source"
        color ← makeElement "ffmpegcolorspace" "video-color"
        color2 ← makeElement "ffmpegcolorspace" "video-color2"
        tee ← makeElement "tee" "stream-tee"
        rQ ← makeElement "queue" "record-queue"
        vQ ← makeElement "queue" "video-queue"
        encoder ← makeElement "y4menc" "video-encoder"
        rSink ← makeElement "filesink" "record-sink"
        sink ← makeElement "ximagesink" "video-sink"

        let elements = [source,color,color2,encoder,rSink,vQ,rQ,sink,tee]

        Glib.objectSetPropertyString "location" rSink "rec"

        mapM_ (GS.binAdd (GS.castToBin pipeline)) elements

        -- Get static pads from queue elements
        sDPad ← GS.elementGetStaticPad vQ "sink"
        sRPad ← GS.elementGetStaticPad rQ "sink"
        -- Request pads from tee element
        dPad ← GS.elementGetRequestPad tee "src%d"
        rPad ← GS.elementGetRequestPad tee "src%d"
        -- Link tee source to queue sink
        GS.padLink (fromJust dPad) (fromJust sDPad) 
        GS.padLink (fromJust rPad) (fromJust sRPad)

        GS.elementLink source color
        GS.elementLink color tee
        GS.elementLink vQ sink
        GS.elementLink rQ color2
        GS.elementLink color2 encoder
        GS.elementLink encoder rSink

        GS.elementSetState pipeline GS.StatePlaying

main = do
    loop ← Glib.mainLoopNew Nothing False
    player
    Glib.mainLoopRun loop


Теперь я спрашиваю вас, следует /Мог ли я это увидеть?
Было ли это очевидно?


Я рад, что это заставит меня быть более осторожным и смотреть в не столь очевидные местано ... EWW.

В заключение всего этого я узнал об опциях отладки gstreamer, узнал, что он шепчет мне, и я ДОЛЖЕН слушать. Я узнал о том, что GDB вынуждены использовать, потому что когда я начал сшивать код C, все, что я получил, было «ошибкой сегмента».
Я научился любить lazy-eval и чистый код Haskell.
Немного Haskell, возможно немного C и больше опыта. «Потерял» примерно полдня, три урока и несколько часов сна, но в конце концов ... Так и происходит ...

...