void GStreamerReader::PlayBinSourceSetup(GstAppSrc* aSource) { mSource = GST_APP_SRC(aSource); gst_app_src_set_callbacks(mSource, &mSrcCallbacks, (gpointer) this, nullptr); MediaResource* resource = mDecoder->GetResource(); /* do a short read to trigger a network request so that GetLength() below * returns something meaningful and not -1 */ char buf[512]; unsigned int size = 0; resource->Read(buf, sizeof(buf), &size); resource->Seek(SEEK_SET, 0); /* now we should have a length */ int64_t resourceLength = resource->GetLength(); gst_app_src_set_size(mSource, resourceLength); if (resource->IsDataCachedToEndOfResource(0) || (resourceLength != -1 && resourceLength <= SHORT_FILE_SIZE)) { /* let the demuxer work in pull mode for local files (or very short files) * so that we get optimal seeking accuracy/performance */ LOG(PR_LOG_DEBUG, "configuring random access, len %lld", resourceLength); gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_RANDOM_ACCESS); } else { /* make the demuxer work in push mode so that seeking is kept to a minimum */ LOG(PR_LOG_DEBUG, "configuring push mode, len %lld", resourceLength); gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_SEEKABLE); } // Set the source MIME type to stop typefind trying every. single. format. GstCaps *caps = GStreamerFormatHelper::ConvertFormatsToCaps(mDecoder->GetResource()->GetContentType().get(), nullptr); gst_app_src_set_caps(aSource, caps); gst_caps_unref(caps); }
static gint create_encoder_pipeline (Encoder *encoder) { GstElement *pipeline, *element; Bin *bin; Link *link; GSList *bins, *links, *elements; GstElementFactory *element_factory; GType type; EncoderStream *stream; GstAppSrcCallbacks callbacks = { need_data_callback, NULL, NULL }; GstAppSinkCallbacks encoder_appsink_callbacks = { NULL, NULL, new_sample_callback }; GstCaps *caps; GstBus *bus; pipeline = gst_pipeline_new (NULL); /* add element to pipeline first. */ bins = encoder->bins; while (bins != NULL) { bin = bins->data; elements = bin->elements; while (elements != NULL) { element = elements->data; if (!gst_bin_add (GST_BIN (pipeline), element)) { GST_ERROR ("add element %s to bin %s error.", gst_element_get_name (element), bin->name); return 1; } elements = g_slist_next (elements); } bins = g_slist_next (bins); } /* then links element. */ bins = encoder->bins; while (bins != NULL) { bin = bins->data; element = bin->first; element_factory = gst_element_get_factory (element); type = gst_element_factory_get_element_type (element_factory); stream = NULL; if (g_strcmp0 ("GstAppSrc", g_type_name (type)) == 0) { GST_INFO ("Encoder appsrc found."); stream = encoder_get_stream (encoder, bin->name); gst_app_src_set_callbacks (GST_APP_SRC (element), &callbacks, stream, NULL); } element = bin->last; element_factory = gst_element_get_factory (element); type = gst_element_factory_get_element_type (element_factory); if ((g_strcmp0 ("GstAppSink", g_type_name (type)) == 0) || (g_strcmp0 ("GstHlsSink", g_type_name (type)) == 0) || (g_strcmp0 ("GstFileSink", g_type_name (type)) == 0)) { GstPad *pad; if (g_strcmp0 ("GstAppSink", g_type_name (type)) == 0) { GST_INFO ("Encoder appsink found."); gst_app_sink_set_callbacks (GST_APP_SINK (element), &encoder_appsink_callbacks, encoder, NULL); } pad = gst_element_get_static_pad (element, "sink"); gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, encoder_appsink_event_probe, encoder, NULL); } links = bin->links; while (links != NULL) { link = links->data; GST_INFO ("link element: %s -> %s", link->src_name, link->sink_name); if (link->caps != NULL) { caps = gst_caps_from_string (link->caps); gst_element_link_filtered (link->src, link->sink, caps); gst_caps_unref (caps); } else { gst_element_link (link->src, link->sink); } links = g_slist_next (links); } bins = g_slist_next (bins); } bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); gst_bus_add_watch (bus, bus_callback, encoder); g_object_unref (bus); encoder->pipeline = pipeline; return 0; }
// A call to this callback indicates that this interface module will be // a listener. Any listener initialization can be done in this function. void openavbIntfMpeg2tsGstRxInitCB(media_q_t *pMediaQ) { AVB_TRACE_ENTRY(AVB_TRACE_INTF_DETAIL); if (pMediaQ) { pvt_data_t *pPvtData = pMediaQ->pPvtIntfInfo; if (!pPvtData) { AVB_LOG_ERROR("Private interface module data not allocated."); return; } pPvtData->pipe = (GstElement*)NULL; pPvtData->appsink = (GstAppSink*)NULL; pPvtData->appsrc = (GstAppSrc*)NULL; pPvtData->bus = (GstBus*)NULL; pPvtData->srcPaused = FALSE; GError *error = NULL; pPvtData->pipe = gst_parse_launch(pPvtData->pPipelineStr, &error); if (error) { AVB_LOGF_ERROR("Error creating pipeline: %s", error->message); return; } AVB_LOGF_INFO("Pipeline: %s", pPvtData->pPipelineStr); pPvtData->appsrc = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(pPvtData->pipe), APPSRC_NAME)); if (!pPvtData->appsrc) { AVB_LOG_ERROR("Failed to find appsrc element"); return; } // Make appsrc non-blocking g_object_set(G_OBJECT(pPvtData->appsrc), "block", FALSE, NULL); // create bus pPvtData->bus = gst_pipeline_get_bus(GST_PIPELINE(pPvtData->pipe)); if (!pPvtData->bus) { AVB_LOG_ERROR("Failed to create bus"); return; } /* add callback for bus messages */ gst_bus_add_watch(pPvtData->bus, (GstBusFunc)bus_message, pMediaQ); // Setup callback function to handle request from src to pause/start data flow GstAppSrcCallbacks cbfns; memset(&cbfns, 0, sizeof(GstAppSrcCallbacks)); cbfns.enough_data = srcStopFeed; cbfns.need_data = srcStartFeed; gst_app_src_set_callbacks(pPvtData->appsrc, &cbfns, (gpointer)(pMediaQ), NULL); // Set most capabilities in pipeline (config), not code // Don't block g_object_set(pPvtData->appsrc, "block", 0, NULL); // PLAY gst_element_set_state(pPvtData->pipe, GST_STATE_PLAYING); } AVB_TRACE_EXIT(AVB_TRACE_INTF_DETAIL); }
static gboolean byzanz_encoder_gstreamer_run (ByzanzEncoder * encoder, GInputStream * input, GOutputStream * output, gboolean record_audio, GCancellable * cancellable, GError ** error) { ByzanzEncoderGStreamer *gstreamer = BYZANZ_ENCODER_GSTREAMER (encoder); ByzanzEncoderGStreamerClass *klass = BYZANZ_ENCODER_GSTREAMER_GET_CLASS (encoder); GstElement *sink; guint width, height; GstMessage *message; GstBus *bus; if (!byzanz_deserialize_header (input, &width, &height, cancellable, error)) return FALSE; gstreamer->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); g_assert (klass->pipeline_string); if (record_audio) { if (klass->audio_pipeline_string == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("This format does not support recording audio.")); return FALSE; } gstreamer->pipeline = gst_parse_launch (klass->audio_pipeline_string, error); gstreamer->audiosrc = gst_bin_get_by_name (GST_BIN (gstreamer->pipeline), "audiosrc"); g_assert (gstreamer->audiosrc); } else { gstreamer->pipeline = gst_parse_launch (klass->pipeline_string, error); } if (gstreamer->pipeline == NULL) return FALSE; g_assert (GST_IS_PIPELINE (gstreamer->pipeline)); gstreamer->src = GST_APP_SRC (gst_bin_get_by_name (GST_BIN (gstreamer->pipeline), "src")); g_assert (GST_IS_APP_SRC (gstreamer->src)); sink = gst_bin_get_by_name (GST_BIN (gstreamer->pipeline), "sink"); g_assert (sink); g_object_set (sink, "stream", output, NULL); g_object_unref (sink); gstreamer->caps = gst_caps_new_simple ("video/x-raw", #if G_BYTE_ORDER == G_LITTLE_ENDIAN "format", G_TYPE_STRING, "BGRx", #elif G_BYTE_ORDER == G_BIG_ENDIAN "format", G_TYPE_STRING, "xRGB", #else #error "Please add the Cairo caps format here" #endif "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, "framerate", GST_TYPE_FRACTION, 0, 1, NULL); g_assert (gst_caps_is_fixed (gstreamer->caps)); gst_app_src_set_caps (gstreamer->src, gstreamer->caps); gst_app_src_set_callbacks (gstreamer->src, &callbacks, gstreamer, NULL); gst_app_src_set_stream_type (gstreamer->src, GST_APP_STREAM_TYPE_STREAM); gst_app_src_set_max_bytes (gstreamer->src, 0); g_object_set (gstreamer->src, "format", GST_FORMAT_TIME, NULL); if (!gst_element_set_state (gstreamer->pipeline, GST_STATE_PLAYING)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to start GStreamer pipeline")); return FALSE; } bus = gst_pipeline_get_bus (GST_PIPELINE (gstreamer->pipeline)); message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS); g_object_unref (bus); gst_element_set_state (gstreamer->pipeline, GST_STATE_NULL); if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { gst_message_parse_error (message, error, NULL); gst_message_unref (message); return FALSE; } gst_message_unref (message); return TRUE; }
BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder) { const char* appsrc = "appsrc name=source ! decodebin name=decoder !"; const char* video = "autovideoconvert ! videoscale !"; const char* audio = "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !"; char pipeline[1024]; if (!mdecoder) return FALSE; /* TODO: Construction of the pipeline from a string allows easy overwrite with arguments. * The only fixed elements necessary are appsrc and the volume element for audio streams. * The rest could easily be provided in gstreamer pipeline notation from command line. */ if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO) snprintf(pipeline, sizeof(pipeline), "%s %s %s name=outsink", appsrc, video, tsmf_platform_get_video_sink()); else snprintf(pipeline, sizeof(pipeline), "%s %s %s name=outsink", appsrc, audio, tsmf_platform_get_audio_sink()); DEBUG_TSMF("pipeline=%s", pipeline); mdecoder->pipe = gst_parse_launch(pipeline, NULL); if (!mdecoder->pipe) { WLog_ERR(TAG, "Failed to create new pipe"); return FALSE; } mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "source"); if (!mdecoder->src) { WLog_ERR(TAG, "Failed to get appsrc"); return FALSE; } mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "outsink"); if (!mdecoder->outsink) { WLog_ERR(TAG, "Failed to get sink"); return FALSE; } if (mdecoder->media_type != TSMF_MAJOR_TYPE_VIDEO) { mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume"); if (!mdecoder->volume) { WLog_ERR(TAG, "Failed to get volume"); return FALSE; } } tsmf_platform_register_handler(mdecoder); /* AppSrc settings */ GstAppSrcCallbacks callbacks = { tsmf_gstreamer_need_data, tsmf_gstreamer_enough_data, tsmf_gstreamer_seek_data }; g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL); g_object_set(mdecoder->src, "is-live", TRUE, NULL); g_object_set(mdecoder->src, "block", TRUE, NULL); gst_app_src_set_caps((GstAppSrc *) mdecoder->src, mdecoder->gst_caps); gst_app_src_set_callbacks((GstAppSrc *)mdecoder->src, &callbacks, mdecoder, NULL); gst_app_src_set_stream_type((GstAppSrc *) mdecoder->src, GST_APP_STREAM_TYPE_SEEKABLE); tsmf_window_create(mdecoder); tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY); tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING); mdecoder->pipeline_start_time_valid = 0; mdecoder->shutdown = 0; GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL, get_type(mdecoder)); return TRUE; }
/** * Set up the Gstreamer pipeline. Appsrc gets raw frames, and appsink takes * encoded frames. * * The pipeline looks like this: * * <pre> * .--------. .-----------. .----------. * | appsrc | | x264enc | | appsink | * | .----| |----. .---| |----. | * | |src |-->|sink| |src|-->|sink|-----+-->handoff * | '----| |----' '---| |----' | handler * '--------' '-----------' '----------' * </pre> */ static int pipeline_init(struct videnc_state *st, const struct vidsz *size) { GstAppSrc *source; GstAppSink *sink; GstBus *bus; GError* gerror = NULL; char pipeline[1024]; GstStateChangeReturn ret; int err = 0; if (!st || !size) return EINVAL; re_printf(" ~~~~ pipeline_init (%d x %d)\n", size->w, size->h); snprintf(pipeline, sizeof(pipeline), "appsrc name=source is-live=TRUE block=TRUE " "do-timestamp=TRUE max-bytes=1000000 ! " "videoparse width=%d height=%d format=i420 framerate=%d/1 ! " "x264enc byte-stream=TRUE rc-lookahead=0 " "sync-lookahead=0 bitrate=%d ! " "appsink name=sink emit-signals=TRUE drop=TRUE", size->w, size->h, st->encoder.fps, st->encoder.bitrate / 1000 /* kbit/s */); re_printf( "------------------------------------------------\n" "%s\n" "------------------------------------------------\n" , pipeline); /* Initialize pipeline. */ st->streamer.pipeline = gst_parse_launch(pipeline, &gerror); if (gerror) { warning("gst_video: launch error: %d: %s: %s\n", gerror->code, gerror->message, pipeline); err = gerror->code; g_error_free(gerror); return err; } /* Configure appsource */ source = GST_APP_SRC(gst_bin_get_by_name( GST_BIN(st->streamer.pipeline), "source")); gst_app_src_set_callbacks(source, &(st->streamer.appsrcCallbacks), st, (GDestroyNotify)appsrc_destroy_notify_cb); /* Configure appsink. */ sink = GST_APP_SINK(gst_bin_get_by_name( GST_BIN(st->streamer.pipeline), "sink")); gst_app_sink_set_callbacks(sink, &(st->streamer.appsinkCallbacks), st, (GDestroyNotify)appsink_destroy_notify_cb); gst_object_unref(GST_OBJECT(sink)); /* Bus watch */ bus = gst_pipeline_get_bus(GST_PIPELINE(st->streamer.pipeline)); gst_bus_set_sync_handler(bus, (GstBusSyncHandler)bus_sync_handler_cb, st, (GDestroyNotify)bus_destroy_notify_cb); gst_object_unref(GST_OBJECT(bus)); /* Set start values of locks */ pthread_mutex_lock(&st->streamer.wait.mutex); st->streamer.wait.flag = 0; pthread_mutex_unlock(&st->streamer.wait.mutex); pthread_mutex_lock(&st->streamer.eos.mutex); st->streamer.eos.flag = 0; pthread_mutex_unlock(&st->streamer.eos.mutex); /* Start pipeline */ re_printf(" ~~~~ pipeline_init -- starting pipeline\n"); ret = gst_element_set_state(st->streamer.pipeline, GST_STATE_PLAYING); if (GST_STATE_CHANGE_FAILURE == ret) { g_warning("set state returned GST_STATE_CHANGE_FAILURE\n"); err = EPROTO; goto out; } st->streamer.source = source; /* Mark pipeline as working */ st->streamer.valid = true; re_printf(" ~~~~ pipeline_init OK (source=%p, sink=%p)\n", source, sink); out: return err; }
BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder) { #if GST_VERSION_MAJOR > 0 const char* video = "appsrc name=videosource ! queue2 name=videoqueue ! decodebin name=videodecoder !"; const char* audio = "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin name=audiodecoder ! audioconvert ! audiorate ! audioresample ! volume name=audiovolume !"; #else const char* video = "appsrc name=videosource ! queue2 name=videoqueue ! decodebin2 name=videodecoder !"; const char* audio = "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin2 name=audiodecoder ! audioconvert ! audiorate ! audioresample ! volume name=audiovolume !"; #endif char pipeline[1024]; if (!mdecoder) return FALSE; /* TODO: Construction of the pipeline from a string allows easy overwrite with arguments. * The only fixed elements necessary are appsrc and the volume element for audio streams. * The rest could easily be provided in gstreamer pipeline notation from command line. */ if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO) sprintf_s(pipeline, sizeof(pipeline), "%s %s name=videosink", video, tsmf_platform_get_video_sink()); else sprintf_s(pipeline, sizeof(pipeline), "%s %s name=audiosink", audio, tsmf_platform_get_audio_sink()); DEBUG_TSMF("pipeline=%s", pipeline); mdecoder->pipe = gst_parse_launch(pipeline, NULL); if (!mdecoder->pipe) { WLog_ERR(TAG, "Failed to create new pipe"); return FALSE; } if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO) mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosource"); else mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosource"); if (!mdecoder->src) { WLog_ERR(TAG, "Failed to get appsrc"); return FALSE; } if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO) mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videoqueue"); else mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audioqueue"); if (!mdecoder->queue) { WLog_ERR(TAG, "Failed to get queue"); return FALSE; } if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO) mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosink"); else mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosink"); if (!mdecoder->outsink) { WLog_ERR(TAG, "Failed to get sink"); return FALSE; } g_signal_connect(mdecoder->outsink, "child-added", G_CALLBACK(cb_child_added), mdecoder); if (mdecoder->media_type == TSMF_MAJOR_TYPE_AUDIO) { mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume"); if (!mdecoder->volume) { WLog_ERR(TAG, "Failed to get volume"); return FALSE; } tsmf_gstreamer_change_volume((ITSMFDecoder*)mdecoder, mdecoder->gstVolume*((double) 10000), mdecoder->gstMuted); } tsmf_platform_register_handler(mdecoder); /* AppSrc settings */ GstAppSrcCallbacks callbacks = { tsmf_gstreamer_need_data, tsmf_gstreamer_enough_data, tsmf_gstreamer_seek_data }; g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL); g_object_set(mdecoder->src, "is-live", FALSE, NULL); g_object_set(mdecoder->src, "block", FALSE, NULL); g_object_set(mdecoder->src, "blocksize", 1024, NULL); gst_app_src_set_caps((GstAppSrc *) mdecoder->src, mdecoder->gst_caps); gst_app_src_set_callbacks((GstAppSrc *)mdecoder->src, &callbacks, mdecoder, NULL); gst_app_src_set_stream_type((GstAppSrc *) mdecoder->src, GST_APP_STREAM_TYPE_SEEKABLE); gst_app_src_set_latency((GstAppSrc *) mdecoder->src, 0, -1); gst_app_src_set_max_bytes((GstAppSrc *) mdecoder->src, (guint64) 0);//unlimited g_object_set(G_OBJECT(mdecoder->queue), "use-buffering", FALSE, NULL); g_object_set(G_OBJECT(mdecoder->queue), "use-rate-estimate", FALSE, NULL); g_object_set(G_OBJECT(mdecoder->queue), "max-size-buffers", 0, NULL); g_object_set(G_OBJECT(mdecoder->queue), "max-size-bytes", 0, NULL); g_object_set(G_OBJECT(mdecoder->queue), "max-size-time", (guint64) 0, NULL); /* Only set these properties if not an autosink, otherwise we will set properties when real sinks are added */ if (!g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoVideoSink") && !g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoAudioSink")) { if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO) { gst_base_sink_set_max_lateness((GstBaseSink *) mdecoder->outsink, 10000000); /* nanoseconds */ } else { gst_base_sink_set_max_lateness((GstBaseSink *) mdecoder->outsink, 10000000); /* nanoseconds */ g_object_set(G_OBJECT(mdecoder->outsink), "buffer-time", (gint64) 20000, NULL); /* microseconds */ g_object_set(G_OBJECT(mdecoder->outsink), "drift-tolerance", (gint64) 20000, NULL); /* microseconds */ g_object_set(G_OBJECT(mdecoder->outsink), "latency-time", (gint64) 10000, NULL); /* microseconds */ g_object_set(G_OBJECT(mdecoder->outsink), "slave-method", 1, NULL); } g_object_set(G_OBJECT(mdecoder->outsink), "sync", TRUE, NULL); /* synchronize on the clock */ g_object_set(G_OBJECT(mdecoder->outsink), "async", TRUE, NULL); /* no async state changes */ } tsmf_window_create(mdecoder); tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY); tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING); mdecoder->pipeline_start_time_valid = 0; mdecoder->shutdown = 0; mdecoder->paused = FALSE; GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL, get_type(mdecoder)); return TRUE; }
/***************************************************************************** * OpenDecoder: probe the decoder and return score *****************************************************************************/ static int OpenDecoder( vlc_object_t *p_this ) { decoder_t *p_dec = ( decoder_t* )p_this; decoder_sys_t *p_sys; GstStateChangeReturn i_ret; gboolean b_ret; sink_src_caps_t caps = { NULL, NULL }; GstStructure *p_str; GstAppSrcCallbacks cb; int i_rval = VLC_SUCCESS; GList *p_list; bool dbin; #define VLC_GST_CHECK( r, v, s, t ) \ { if( r == v ){ msg_Err( p_dec, s ); i_rval = t; goto fail; } } if( !vlc_gst_init( )) { msg_Err( p_dec, "failed to register vlcvideosink" ); return VLC_EGENERIC; } p_str = vlc_to_gst_fmt( &p_dec->fmt_in ); if( !p_str ) return VLC_EGENERIC; /* Allocate the memory needed to store the decoder's structure */ p_sys = p_dec->p_sys = calloc( 1, sizeof( *p_sys ) ); if( p_sys == NULL ) { gst_structure_free( p_str ); return VLC_ENOMEM; } dbin = var_CreateGetBool( p_dec, "use-decodebin" ); msg_Dbg( p_dec, "Using decodebin? %s", dbin ? "yes ":"no" ); caps.p_sinkcaps = gst_caps_new_empty( ); gst_caps_append_structure( caps.p_sinkcaps, p_str ); /* Currently supports only system memory raw output format */ caps.p_srccaps = gst_caps_new_empty_simple( "video/x-raw" ); /* Get the list of all the available gstreamer decoders */ p_list = gst_element_factory_list_get_elements( GST_ELEMENT_FACTORY_TYPE_DECODER, GST_RANK_MARGINAL ); VLC_GST_CHECK( p_list, NULL, "no decoder list found", VLC_ENOMOD ); if( !dbin ) { GList *p_l; /* Sort them as per ranks */ p_list = g_list_sort( p_list, gst_plugin_feature_rank_compare_func ); VLC_GST_CHECK( p_list, NULL, "failed to sort decoders list", VLC_ENOMOD ); p_l = g_list_find_custom( p_list, &caps, find_decoder_func ); VLC_GST_CHECK( p_l, NULL, "no suitable decoder found", VLC_ENOMOD ); /* create the decoder with highest rank */ p_sys->p_decode_in = gst_element_factory_create( ( GstElementFactory* )p_l->data, NULL ); VLC_GST_CHECK( p_sys->p_decode_in, NULL, "failed to create decoder", VLC_ENOMOD ); } else { GList *p_l; /* Just check if any suitable decoder exists, rest will be * handled by decodebin */ p_l = g_list_find_custom( p_list, &caps, find_decoder_func ); VLC_GST_CHECK( p_l, NULL, "no suitable decoder found", VLC_ENOMOD ); } gst_plugin_feature_list_free( p_list ); p_list = NULL; gst_caps_unref( caps.p_srccaps ); caps.p_srccaps = NULL; p_sys->b_prerolled = false; p_sys->b_running = false; /* Queue: GStreamer thread will dump buffers into this queue, * DecodeBlock() will pop out the buffers from the queue */ p_sys->p_que = gst_atomic_queue_new( 0 ); VLC_GST_CHECK( p_sys->p_que, NULL, "failed to create queue", VLC_ENOMEM ); p_sys->p_decode_src = gst_element_factory_make( "appsrc", NULL ); VLC_GST_CHECK( p_sys->p_decode_src, NULL, "appsrc not found", VLC_ENOMOD ); g_object_set( G_OBJECT( p_sys->p_decode_src ), "caps", caps.p_sinkcaps, "emit-signals", TRUE, "format", GST_FORMAT_BYTES, "stream-type", GST_APP_STREAM_TYPE_SEEKABLE, /* Making DecodeBlock() to block on appsrc with max queue size of 1 byte. * This will make the push_buffer() tightly coupled with the buffer * flow from appsrc -> decoder. push_buffer() will only return when * the same buffer it just fed to appsrc has also been fed to the * decoder element as well */ "block", TRUE, "max-bytes", ( guint64 )1, NULL ); gst_caps_unref( caps.p_sinkcaps ); caps.p_sinkcaps = NULL; cb.enough_data = NULL; cb.need_data = NULL; cb.seek_data = seek_data_cb; gst_app_src_set_callbacks( GST_APP_SRC( p_sys->p_decode_src ), &cb, p_dec, NULL ); if( dbin ) { p_sys->p_decode_in = gst_element_factory_make( "decodebin", NULL ); VLC_GST_CHECK( p_sys->p_decode_in, NULL, "decodebin not found", VLC_ENOMOD ); //g_object_set( G_OBJECT( p_sys->p_decode_in ), //"max-size-buffers", 2, NULL ); //g_signal_connect( G_OBJECT( p_sys->p_decode_in ), "no-more-pads", //G_CALLBACK( no_more_pads_cb ), p_dec ); g_signal_connect( G_OBJECT( p_sys->p_decode_in ), "pad-added", G_CALLBACK( pad_added_cb ), p_dec ); } /* videosink: will emit signal for every available buffer */ p_sys->p_decode_out = gst_element_factory_make( "vlcvideosink", NULL ); VLC_GST_CHECK( p_sys->p_decode_out, NULL, "vlcvideosink not found", VLC_ENOMOD ); p_sys->p_allocator = gst_vlc_picture_plane_allocator_new( (gpointer) p_dec ); g_object_set( G_OBJECT( p_sys->p_decode_out ), "sync", FALSE, "allocator", p_sys->p_allocator, "id", (gpointer) p_dec, NULL ); g_signal_connect( G_OBJECT( p_sys->p_decode_out ), "new-buffer", G_CALLBACK( frame_handoff_cb ), p_dec ); //FIXME: caps_signal #if 0 g_signal_connect( G_OBJECT( p_sys->p_decode_out ), "new-caps", G_CALLBACK( caps_handoff_cb ), p_dec ); #else GST_VLC_VIDEO_SINK( p_sys->p_decode_out )->new_caps = caps_handoff_cb; #endif p_sys->p_decoder = GST_ELEMENT( gst_bin_new( "decoder" ) ); VLC_GST_CHECK( p_sys->p_decoder, NULL, "bin not found", VLC_ENOMOD ); p_sys->p_bus = gst_bus_new( ); VLC_GST_CHECK( p_sys->p_bus, NULL, "failed to create bus", VLC_ENOMOD ); gst_element_set_bus( p_sys->p_decoder, p_sys->p_bus ); gst_bin_add_many( GST_BIN( p_sys->p_decoder ), p_sys->p_decode_src, p_sys->p_decode_in, p_sys->p_decode_out, NULL ); gst_object_ref( p_sys->p_decode_src ); gst_object_ref( p_sys->p_decode_in ); gst_object_ref( p_sys->p_decode_out ); b_ret = gst_element_link( p_sys->p_decode_src, p_sys->p_decode_in ); VLC_GST_CHECK( b_ret, FALSE, "failed to link src <-> in", VLC_EGENERIC ); if( !dbin ) { b_ret = gst_element_link( p_sys->p_decode_in, p_sys->p_decode_out ); VLC_GST_CHECK( b_ret, FALSE, "failed to link in <-> out", VLC_EGENERIC ); } p_dec->fmt_out.i_cat = p_dec->fmt_in.i_cat; /* set the pipeline to playing */ i_ret = gst_element_set_state( p_sys->p_decoder, GST_STATE_PLAYING ); VLC_GST_CHECK( i_ret, GST_STATE_CHANGE_FAILURE, "set state failure", VLC_EGENERIC ); p_sys->b_running = true; /* Set callbacks */ p_dec->pf_decode_video = DecodeBlock; p_dec->pf_flush = Flush; return VLC_SUCCESS; fail: if( caps.p_sinkcaps ) gst_caps_unref( caps.p_sinkcaps ); if( caps.p_srccaps ) gst_caps_unref( caps.p_srccaps ); if( p_list ) gst_plugin_feature_list_free( p_list ); CloseDecoder( ( vlc_object_t* )p_dec ); return i_rval; }