라이브 스트림 및 EOS 처리에서 동적으로 녹화

라이브 스트림 및 EOS 처리에서 동적으로 녹화

여러분 안녕하세요,

그래서 한 달 전에 나는 ml에서 녹음에 대한 솔루션을 게시했습니다.
mp4/mkv용으로 많은 muxer에 필요한 EOS를 처리하기 위한 라이브 스트림
이전 솔루션은 오디오 또는
비디오 스트림, 작동하도록 하는 4가지 방법 모두
스레드가 차단되어 문제가 발생합니다. 최근에 도움을 받았습니다.
솔루션을 제공한 IRC의 thaytan에서...

파이프라인의 기본 다이어그램을 빠르게 그려보겠습니다.
한 번 보내면 너무 심하게 망가지지 않습니다.

-->비디오-->티/--> ㅋㅋㅋㅋㅋ --> xvimagesink
rtspsrc/      \--> |--------------------------------|
|-> 입력 --> muxer --> 파일 싱크 |
-->오디오-->티/--> |---------------------------------|
\--> ㅋㅋㅋㅋㅋ --> 자동오디오싱크

기본 아이디어는 소스가 보기/듣기로 분할된다는 것입니다.
및 파일 싱크로 인코딩/muxed되는 녹음 빈.
내가 할 필요가 있었던 모든 것을 디코딩하기 전에 분기된 mp4를 읽고 있는 경우
리먹스 및 저장이었습니다.

처음에 솔루션은
그 이유는 EOS를 녹음에만 보내면
bin 당신이 받기 만하기 때문에 버스 호출 핸들러에서 결코 수신되지 않습니다.
전체 파이프라인의 *모든* 요소가
이벤트 및 이벤트는 그렇지 않습니다.
1.0 계획이지만 그것이 무엇을 수반하는지 모르겠습니다.

신호를 보내기 위해 파이프라인을 따라 사용자 지정 이벤트를 보내는 문제
녹음을 중지하는 것은 약간의 누락된 코드입니다.

http://cgit.freedesktop.org/gstreamer/gstreamer/tree/libs/gst/base/gstbasesrc.c#n1639

구현 가능한지 확인하기 위해 개선 버그를 제출했습니다. [1]

이는 기본적으로 내 맞춤 이벤트가 도착하지 않고
예전과 같은 배... 타이탄이 큰 도움이 된 곳입니다.
솔루션은 내가 결코하지 않았을 사용자 정의 gst bin 요소를 구축하는 것이 었습니다.
처음에 할 전문 지식도 생각도 없었습니다.

아이디어는 패드를 비동기적으로 차단하고 콜백이
EOS는 사용자 정의 저장소로 다시 돌아가지 않기 때문에
모든 응용 프로그램의 버스 호출 처리기에는 _handle_message 기능이 있습니다.
EOS를 포착하고 맞춤형 메시지를 통해 애플리케이션에 신호를 보냅니다.
완벽합니다. testsrc를 사용하여 간단한 코드를 첨부했습니다.
비디오가 녹화보다 더 오래 표시된다는 것을 시뮬레이션합니다.
차단할 g_timeout 콜백으로 녹음 중지
패드를 붙이고 녹음의 끝을 선동합니다.

나는 많은 것을 발견했기 때문에 이것이 문서화되었는지 확인하고 싶었습니다.
Google에서 이와 유사한 솔루션을 찾고 있지만 솔루션이 없는 사람들.

[1] https://bugzilla.gnome.org/show_bug.cgi?id=635718

모든 도움과 gstreamer에 감사드립니다. 훌륭합니다!
--
나다나엘 D. 노블렛


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

#define INCLUDE_AUDIO 1

struct PipeData {
  GstElement *pipeline;
  GstPad *asrcpad;
  GstPad *vsrcpad;
};

typedef struct _RecordingBin
{
  GstBin parent;
} RecordingBin;

typedef struct _RecordingBinClass
{
  GstBinClass parent;
} RecordingBinClass;

#define GST_TYPE_RECORDING_BIN recording_bin_get_type()
GST_BOILERPLATE (RecordingBin, recording_bin, GstBin,
    GST_TYPE_BIN);

static void
recording_bin_handle_message (GstBin * bin, GstMessage * message)
{
  RecordingBin *recording = (RecordingBin *)(bin);

  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_EOS:
        g_print ("Got EOS in the recording bin
"); /* FIXME: Remove the bin from the pipeline and dispose of it */ break; default: break; } GST_BIN_CLASS (parent_class)->handle_message (bin, message); } static void recording_bin_base_init (gpointer g_class) { } static void recording_bin_class_init (RecordingBinClass * klass) { GstBinClass *gstbin_class = GST_BIN_CLASS (klass); gstbin_class->handle_message = GST_DEBUG_FUNCPTR (recording_bin_handle_message); } static void recording_bin_init (RecordingBin * src, RecordingBinClass * klass) { } gboolean bus_call(GstBus *bus, GstMessage *msg, void *data) { gchar *debug; GError *err; GMainLoop *loop = (GMainLoop*)data; switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_APPLICATION: g_print("APP received on OBJ NAME %s
",GST_OBJECT_NAME(msg->src)); break; case GST_MESSAGE_EOS: g_print("EOS received on OBJ NAME %s
",GST_OBJECT_NAME(msg->src)); g_main_loop_quit (loop); break; case GST_MESSAGE_ERROR: gst_message_parse_error(msg, &err, &debug); g_free(debug); g_print("BUS CALL %s
", err->message); g_error_free(err); g_main_loop_quit (loop); break; default: break; } return TRUE; } static void handle_pad_block (GstPad *pad, gboolean blocked, gpointer user_data) { GstEvent *event; if (blocked) { GstPad *peer = gst_pad_get_peer (pad); gst_pad_unlink (pad, peer); event = gst_event_new_eos(); gst_pad_send_event (peer, event); gst_pad_set_blocked_async (pad, FALSE, handle_pad_block, NULL); gst_object_unref (peer); } else { /* Unblock is finished */ } } /** * stop_encoding: * @data: main pipeline * * This is a glib function that gets called every 2 seconds, once called 3 times it will issue an EOS to the bin's sink pad * * Returns: FALSE to stop the event loop calling again, TRUE otherwise */ gboolean stop_encoding(gpointer *data) { static int called = 0; called++; g_print("CALLED!! %d times
",called); if(called == 2) { struct PipeData *pipedata = (struct PipeData *)(data); /* Block the src pads of the 2 tee's leading to the recording bin. */ /* In the pad block callback, send EOS into the recording bin */ /* Catch EOS coming out of the recording-subbin and remove the bin */ gst_pad_set_blocked_async (pipedata->asrcpad, TRUE, handle_pad_block, NULL); gst_pad_set_blocked_async (pipedata->vsrcpad, TRUE, handle_pad_block, NULL); } return (called == 2)?FALSE:TRUE; } int main(int argc, char* argv[]) { GMainLoop *loop; gst_init(&argc,&argv); loop = g_main_loop_new (NULL, FALSE); GstElement *pipeline, *vsource, *vtee, *vqueue, *tover, *xvsink, *evqueue, *vencoder, *muxer, *filesink; GstBin *recording; GstBus *bus; GstPad *srcpad,*sinkpad; struct PipeData pipedata; // Create gstreamer elements pipedata.pipeline = pipeline = gst_pipeline_new ("eos-test-player"); vsource = gst_element_factory_make ("videotestsrc", "viewing-file-source"); vtee = gst_element_factory_make ("tee", "viewing-tee"); vqueue = gst_element_factory_make ("queue2", "viewing-queue"); tover = gst_element_factory_make ("timeoverlay", "viewing-overlay"); xvsink = gst_element_factory_make ("xvimagesink", "viewing-xvsink"); GstElement *asource, *atee, *aqueue, *aequeue, *aencoder, *asink; asource = gst_element_factory_make ("audiotestsrc", "viewing-audio-source"); g_object_set(G_OBJECT(asource), "num-buffers",300, NULL); atee = gst_element_factory_make ("tee", "viewing-audio-tee"); aqueue = gst_element_factory_make ("queue2", "viewing-audio-queue"); asink = gst_element_factory_make ("pulsesink", "viewing-audio-sink"); aequeue = gst_element_factory_make ("queue2", "encoding-audio-queue"); aencoder = gst_element_factory_make ("lamemp3enc", "encoding-audio-encoder"); recording = GST_BIN(g_object_new (GST_TYPE_RECORDING_BIN, "name", "recbin", NULL)); evqueue = gst_element_factory_make ("queue2", "encoding-queue"); vencoder = gst_element_factory_make ("ffenc_mpeg4", "encoding-encoder"); muxer = gst_element_factory_make ("mp4mux", "encoding-muxer"); filesink = gst_element_factory_make ("filesink", "encoding-filesink"); if(!pipeline || !vsource || !xvsink || !tover ) { g_print("Unable to create all necessary elements
"); return -1; } bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); gst_bus_add_watch (bus, bus_call, loop); gst_object_unref (bus); g_object_set(G_OBJECT(vsource), "num-buffers",300, NULL); g_object_set(G_OBJECT(filesink),"location","/tmp/output.mp4", NULL); g_object_set (G_OBJECT (tover), "halign", "right", NULL); g_object_set (G_OBJECT (tover), "valign", "top", NULL); g_object_set (G_OBJECT (tover), "shaded-background", TRUE, NULL); /* create the recording bin */ gst_bin_add_many (recording, aequeue, aencoder, evqueue, vencoder, muxer, filesink, NULL); sinkpad = gst_element_get_static_pad(evqueue,"sink"); GstPad *ghost = gst_ghost_pad_new("vsink",sinkpad); if(ghost == NULL) g_error("Unable to create ghostpad!
"); gst_element_add_pad(GST_ELEMENT(recording),ghost); gst_object_unref(GST_OBJECT(sinkpad)); gst_element_link_many(evqueue,vencoder,muxer,filesink,NULL); sinkpad = gst_element_get_static_pad(aequeue,"sink"); gst_element_add_pad(GST_ELEMENT(recording),gst_ghost_pad_new("asink",sinkpad)); gst_object_unref(GST_OBJECT(sinkpad)); gst_element_link_many(aequeue,aencoder,muxer,NULL); /* we add all elements into the pipeline */ gst_bin_add_many (GST_BIN (pipeline), asource, atee, aqueue, asink, vsource, vtee, vqueue, tover, xvsink, recording, NULL); /* link video elements */ gst_element_link_many(vsource,tover,vtee,NULL); srcpad = gst_element_get_request_pad(vtee,"src0"); sinkpad = gst_element_get_pad(vqueue,"sink"); gst_pad_link(srcpad,sinkpad); gst_object_unref (sinkpad); gst_object_unref (srcpad); gst_element_link(vqueue,xvsink); /* link the viewing pipeline into the bin */ pipedata.vsrcpad = gst_element_get_request_pad(vtee,"src1"); sinkpad = gst_element_get_pad(GST_ELEMENT(recording),"vsink"); gst_pad_link(pipedata.vsrcpad,sinkpad); gst_object_unref (sinkpad); /* link audio elements */ gst_element_link_many(asource,atee,NULL); srcpad = gst_element_get_request_pad(atee,"src0"); sinkpad = gst_element_get_pad(aqueue,"sink"); gst_object_unref (sinkpad); gst_object_unref (srcpad); gst_pad_link(srcpad,sinkpad); gst_element_link(aqueue,asink); /* link the viewing pipeline into the bin */ pipedata.asrcpad = gst_element_get_request_pad(atee,"src1"); sinkpad = gst_element_get_pad(GST_ELEMENT(recording),"asink"); gst_pad_link(pipedata.asrcpad,sinkpad); gst_object_unref (sinkpad); /* Iterate */ g_print ("Running...
"); gst_element_set_state(pipeline,GST_STATE_PLAYING); g_timeout_add_seconds(2,(GSourceFunc)stop_encoding, &pipedata); g_main_loop_run (loop); /* Out of the main loop, clean up nicely */ g_print ("Returned, stopping playback
"); gst_element_set_state (pipeline, GST_STATE_NULL); g_print ("Deleting pipeline
"); gst_object_unref (GST_OBJECT (pipeline)); return 0; }



-------------------------------------------------- --------------------------
3D 게임 앱의 가시성을 높이고 $500를 받을 수 있는 기회를 얻으십시오!
가장 큰 설치 PC 기반을 활용하고 다음을 통해 게임에 더 많은 관심을 가져보세요.
인텔(R) 그래픽 기술에 최적화되어 있습니다. 지금 시작하십시오.
인텔(R) 소프트웨어 파트너 프로그램. 5개의 $500 상금을 손에 넣을 수 있습니다.
http://p.sf.net/sfu/intelisp-dev2dev

__________________________________________________________
gstreamer-devel 메일링 리스트
gstreamer-devel 목록.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/gstreamer-devel

좋은 웹페이지 즐겨찾기