static void owr_local_media_source_finalize(GObject *object) { OwrLocalMediaSource *source = OWR_LOCAL_MEDIA_SOURCE(object); owr_message_origin_bus_set_free(source->priv->message_origin_bus_set); source->priv->message_origin_bus_set = NULL; }
static GList *get_capture_sources(OwrMediaType types) { static GList *cached_sources = NULL; OwrLocalMediaSource *source; OwrMediaType media_type; GList *result_list = NULL; GList *elem; if (g_once_init_enter(&cached_sources)) { GList *sources = NULL; /* FIXME: This code makes no sense at all, we shouldn't hardcode * capture sources but check what is available. Not everybody has * /dev/video0 and /dev/video1, and not always are they camera * sources... * Use GstDeviceMonitor here! */ source = _owr_local_media_source_new("Audio capture source", OWR_MEDIA_TYPE_AUDIO, OWR_SOURCE_TYPE_CAPTURE); sources = g_list_append(sources, OWR_MEDIA_SOURCE(source)); source = _owr_local_media_source_new("Video capture source", OWR_MEDIA_TYPE_VIDEO, OWR_SOURCE_TYPE_CAPTURE); _owr_local_media_source_set_capture_device_index(source, PRIMARY_VIDEO_DEVICE_INDEX); sources = g_list_append(sources, OWR_MEDIA_SOURCE(source)); source = _owr_local_media_source_new("Video capture source", OWR_MEDIA_TYPE_VIDEO, OWR_SOURCE_TYPE_CAPTURE); _owr_local_media_source_set_capture_device_index(source, SECONDARY_VIDEO_DEVICE_INDEX); sources = g_list_append(sources, OWR_MEDIA_SOURCE(source)); if (g_getenv("OWR_USE_TEST_SOURCES")) { source = _owr_local_media_source_new("Video test source", OWR_MEDIA_TYPE_VIDEO, OWR_SOURCE_TYPE_TEST); sources = g_list_append(sources, OWR_MEDIA_SOURCE(source)); source = _owr_local_media_source_new("Audio test source", OWR_MEDIA_TYPE_AUDIO, OWR_SOURCE_TYPE_TEST); sources = g_list_append(sources, OWR_MEDIA_SOURCE(source)); } g_once_init_leave(&cached_sources, sources); } for (elem = cached_sources; elem; elem = elem->next) { source = OWR_LOCAL_MEDIA_SOURCE(elem->data); g_object_get(source, "media-type", &media_type, NULL); if (types & media_type) { g_object_ref(source); result_list = g_list_append(result_list, source); } } return result_list; }
static void owr_local_media_source_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { OwrLocalMediaSource *source = OWR_LOCAL_MEDIA_SOURCE(object); switch (property_id) { case PROP_DEVICE_INDEX: g_value_set_int(value, source->priv->device_index); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); break; } }
/* * owr_local_media_source_get_pad * * The beginning of a media source chain in the pipeline looks like this: * +------------+ * /---+ fakesink | * +--------+ +------------+ +-----+ / +------------+ * | source +---+ capsfilter +---+ tee +---/ * +--------+ +------------+ +-----+ \ * \ +------------+ * \---+ inter*sink | * +------------+ * * For each newly requested pad a new inter*sink is added to the tee. * Note that this is a completely independent pipeline, and the complete * pipeline is only created once for a specific media source. * * Then for each newly requested pad another bin with a inter*src is * created, which is then going to be part of the transport agent * pipeline. The ghostpad of it is what we return here. * * +-----------+ +-------------------------------+ +----------+ * | inter*src +---+ converters/queues/capsfilters +---+ ghostpad | * +-----------+ +-------------------------------+ +----------+ * */ static GstElement *owr_local_media_source_request_source(OwrMediaSource *media_source, GstCaps *caps) { OwrLocalMediaSource *local_source; OwrLocalMediaSourcePrivate *priv; GstElement *source_element = NULL; GstElement *source_pipeline; #if defined(__linux__) && !defined(__ANDROID__) gchar *tmp; #endif g_assert(media_source); local_source = OWR_LOCAL_MEDIA_SOURCE(media_source); priv = local_source->priv; /* only create the source bin for this media source once */ if ((source_pipeline = _owr_media_source_get_source_bin(media_source))) { GST_DEBUG_OBJECT(media_source, "Re-using existing source element/bin"); } else { OwrMediaType media_type = OWR_MEDIA_TYPE_UNKNOWN; OwrSourceType source_type = OWR_SOURCE_TYPE_UNKNOWN; GstElement *source, *capsfilter = NULL, *tee; GstElement *queue, *fakesink; GstPad *sinkpad; GEnumClass *media_enum_class, *source_enum_class; GEnumValue *media_enum_value, *source_enum_value; gchar *bin_name; GstCaps *source_caps; GstStructure *source_structure; GstBus *bus; g_object_get(media_source, "media-type", &media_type, "type", &source_type, NULL); media_enum_class = G_ENUM_CLASS(g_type_class_ref(OWR_TYPE_MEDIA_TYPE)); source_enum_class = G_ENUM_CLASS(g_type_class_ref(OWR_TYPE_SOURCE_TYPE)); media_enum_value = g_enum_get_value(media_enum_class, media_type); source_enum_value = g_enum_get_value(source_enum_class, source_type); bin_name = g_strdup_printf("local-%s-%s-source-bin-%u", media_enum_value ? media_enum_value->value_nick : "unknown", source_enum_value ? source_enum_value->value_nick : "unknown", g_atomic_int_add(&unique_bin_id, 1)); g_type_class_unref(media_enum_class); g_type_class_unref(source_enum_class); source_pipeline = gst_pipeline_new(bin_name); g_free(bin_name); bin_name = NULL; #ifdef OWR_DEBUG g_signal_connect(source_pipeline, "deep-notify", G_CALLBACK(gst_object_default_deep_notify), NULL); #endif bus = gst_pipeline_get_bus(GST_PIPELINE(source_pipeline)); g_main_context_push_thread_default(_owr_get_main_context()); gst_bus_add_watch(bus, (GstBusFunc)bus_call, source_pipeline); g_main_context_pop_thread_default(_owr_get_main_context()); gst_object_unref(bus); GST_DEBUG_OBJECT(local_source, "media_type: %d, type: %d", media_type, source_type); if (media_type == OWR_MEDIA_TYPE_UNKNOWN || source_type == OWR_SOURCE_TYPE_UNKNOWN) { GST_ERROR_OBJECT(local_source, "Cannot connect source with unknown type or media type to other component"); goto done; } switch (media_type) { case OWR_MEDIA_TYPE_AUDIO: { switch (source_type) { case OWR_SOURCE_TYPE_CAPTURE: CREATE_ELEMENT(source, AUDIO_SRC, "audio-source"); #if !defined(__APPLE__) || !TARGET_IPHONE_SIMULATOR g_object_set(source, "buffer-time", G_GINT64_CONSTANT(40000), "latency-time", G_GINT64_CONSTANT(10000), NULL); if (priv->device_index > -1) { #ifdef __APPLE__ g_object_set(source, "device", priv->device_index, NULL); #elif defined(__linux__) && !defined(__ANDROID__) tmp = g_strdup_printf("%d", priv->device_index); g_object_set(source, "device", tmp, NULL); g_free(tmp); #endif } #endif break; case OWR_SOURCE_TYPE_TEST: CREATE_ELEMENT(source, "audiotestsrc", "audio-source"); g_object_set(source, "is-live", TRUE, NULL); break; case OWR_SOURCE_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } #if defined(__APPLE__) && !TARGET_IPHONE_SIMULATOR /* workaround for osxaudiosrc bug * https://bugzilla.gnome.org/show_bug.cgi?id=711764 */ CREATE_ELEMENT(capsfilter, "capsfilter", "audio-source-capsfilter"); source_caps = gst_caps_copy(caps); source_structure = gst_caps_get_structure(source_caps, 0); gst_structure_set(source_structure, "format", G_TYPE_STRING, "S32LE", "rate", G_TYPE_INT, 44100, NULL); gst_structure_remove_field(source_structure, "channels"); g_object_set(capsfilter, "caps", source_caps, NULL); gst_caps_unref(source_caps); gst_bin_add(GST_BIN(source_pipeline), capsfilter); #endif break; } case OWR_MEDIA_TYPE_VIDEO: { gint fps_n, fps_d; switch (source_type) { case OWR_SOURCE_TYPE_CAPTURE: CREATE_ELEMENT(source, VIDEO_SRC, "video-source"); if (priv->device_index > -1) { #if defined(__APPLE__) && !TARGET_IPHONE_SIMULATOR g_object_set(source, "device-index", priv->device_index, NULL); #elif defined(__ANDROID__) g_object_set(source, "cam-index", priv->device_index, NULL); #elif defined(__linux__) tmp = g_strdup_printf("/dev/video%d", priv->device_index); g_object_set(source, "device", tmp, NULL); g_free(tmp); #endif } break; case OWR_SOURCE_TYPE_TEST: { GstElement *src, *time; GstPad *srcpad; source = gst_bin_new("video-source"); CREATE_ELEMENT(src, "videotestsrc", "videotestsrc"); g_object_set(src, "is-live", TRUE, NULL); gst_bin_add(GST_BIN(source), src); time = gst_element_factory_make("timeoverlay", "timeoverlay"); if (time) { g_object_set(time, "font-desc", "Sans 60", NULL); gst_bin_add(GST_BIN(source), time); gst_element_link(src, time); srcpad = gst_element_get_static_pad(time, "src"); } else { srcpad = gst_element_get_static_pad(src, "src"); } gst_element_add_pad(source, gst_ghost_pad_new("src", srcpad)); gst_object_unref(srcpad); break; } case OWR_SOURCE_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } CREATE_ELEMENT(capsfilter, "capsfilter", "video-source-capsfilter"); source_caps = gst_caps_copy(caps); source_structure = gst_caps_get_structure(source_caps, 0); gst_structure_remove_field(source_structure, "format"); /* If possible try to limit the framerate at the source already */ if (gst_structure_get_fraction(source_structure, "framerate", &fps_n, &fps_d)) { GstStructure *tmp = gst_structure_copy(source_structure); gst_structure_remove_field(tmp, "framerate"); gst_caps_append_structure(source_caps, tmp); } g_object_set(capsfilter, "caps", source_caps, NULL); gst_caps_unref(source_caps); gst_bin_add(GST_BIN(source_pipeline), capsfilter); break; } case OWR_MEDIA_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } g_assert(source); CREATE_ELEMENT(tee, "tee", "source-tee"); CREATE_ELEMENT(queue, "queue", "source-tee-fakesink-queue"); CREATE_ELEMENT(fakesink, "fakesink", "source-tee-fakesink"); g_object_set(fakesink, "async", FALSE, NULL); gst_bin_add_many(GST_BIN(source_pipeline), source, tee, queue, fakesink, NULL); gst_element_sync_state_with_parent(queue); gst_element_sync_state_with_parent(fakesink); LINK_ELEMENTS(tee, queue); LINK_ELEMENTS(queue, fakesink); /* Many sources don't like reconfiguration and it's pointless * here anyway right now. No need to reconfigure whenever something * is added to the tee or removed. * We will have to implement reconfiguration differently later by * selecting the best caps based on all consumers. */ sinkpad = gst_element_get_static_pad(tee, "sink"); gst_pad_add_probe(sinkpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, drop_reconfigure_event, NULL, NULL); gst_object_unref(sinkpad); if (!source) GST_ERROR_OBJECT(media_source, "Failed to create source element!"); if (capsfilter) { LINK_ELEMENTS(capsfilter, tee); gst_element_sync_state_with_parent(tee); gst_element_sync_state_with_parent(capsfilter); LINK_ELEMENTS(source, capsfilter); } else { gst_element_sync_state_with_parent(tee); LINK_ELEMENTS(source, tee); } gst_element_sync_state_with_parent(source); _owr_media_source_set_source_bin(media_source, source_pipeline); _owr_media_source_set_source_tee(media_source, tee); if (gst_element_set_state(source_pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { GST_ERROR("Failed to set local source pipeline %s to playing", GST_OBJECT_NAME(source_pipeline)); /* FIXME: We should handle this and don't expose the source */ } g_signal_connect(tee, "pad-removed", G_CALLBACK(tee_pad_removed_cb), media_source); } gst_object_unref(source_pipeline); source_element = OWR_MEDIA_SOURCE_CLASS(owr_local_media_source_parent_class)->request_source (media_source, caps); done: return source_element; }
/* * owr_local_media_source_get_pad * * The beginning of a media source chain in the pipeline looks like this: * * +--------+ +------------+ +-----+ * | source +---+ capsfilter +---+ tee +--- * +--------+ +------------+ +-----+ * * Only one such chain is created per media source for the initial get_pad * call. Subsequent calls will just obtain another tee pad. After these initial * elements are created, they are linked together and synced up to the PLAYING * state. * * Once the initial chain is created, a block is placed on the new src pad of * the tee. The rest of the new chain (conversion elements, capsfilter, queues, * etc.) is created, linked and synced in the pad block callback. */ static GstPad *owr_local_media_source_get_pad(OwrMediaSource *media_source, GstCaps *caps) { OwrLocalMediaSource *local_source; OwrLocalMediaSourcePrivate *priv; GstElement *source_bin, *post_tee_bin; GstElement *source = NULL, *capsfilter = NULL, *tee; GstPad *ghostpad = NULL; gchar *pad_name; OwrMediaType media_type = OWR_MEDIA_TYPE_UNKNOWN; OwrSourceType source_type = OWR_SOURCE_TYPE_UNKNOWN; OwrCodecType codec_type = OWR_CODEC_TYPE_NONE; guint source_id; g_assert(media_source); local_source = OWR_LOCAL_MEDIA_SOURCE(media_source); priv = local_source->priv; g_object_get(media_source, "media-type", &media_type, "type", &source_type, NULL); /* only create the source bin for this media source once */ if (_owr_media_source_get_element(media_source)) { GST_DEBUG_OBJECT(media_source, "Re-using existing source element/bin"); source_bin = _owr_media_source_get_element(media_source); tee = priv->source_tee; } else { GEnumClass *media_enum_class, *source_enum_class; GEnumValue *media_enum_value, *source_enum_value; gchar *bin_name; GstCaps *source_caps; GstStructure *source_structure; GstElement *fakesink; media_enum_class = G_ENUM_CLASS(g_type_class_ref(OWR_TYPE_MEDIA_TYPE)); source_enum_class = G_ENUM_CLASS(g_type_class_ref(OWR_TYPE_SOURCE_TYPE)); media_enum_value = g_enum_get_value(media_enum_class, media_type); source_enum_value = g_enum_get_value(source_enum_class, source_type); bin_name = g_strdup_printf("local-%s-%s-source-bin-%u", media_enum_value ? media_enum_value->value_nick : "unknown", source_enum_value ? source_enum_value->value_nick : "unknown", g_atomic_int_add(&unique_bin_id, 1)); g_type_class_unref(media_enum_class); g_type_class_unref(source_enum_class); source_bin = gst_bin_new(bin_name); g_free(bin_name); bin_name = NULL; gst_bin_add(GST_BIN(_owr_get_pipeline()), source_bin); gst_element_sync_state_with_parent(GST_ELEMENT(source_bin)); GST_DEBUG_OBJECT(local_source, "media_type: %d, type: %d", media_type, source_type); if (media_type == OWR_MEDIA_TYPE_UNKNOWN || source_type == OWR_SOURCE_TYPE_UNKNOWN) { GST_ERROR_OBJECT(local_source, "Cannot connect source with unknown type or media type to other component"); goto done; } switch (media_type) { case OWR_MEDIA_TYPE_AUDIO: { switch (source_type) { case OWR_SOURCE_TYPE_CAPTURE: CREATE_ELEMENT(source, AUDIO_SRC, "audio-source"); #if !defined(__APPLE__) || !TARGET_IPHONE_SIMULATOR g_object_set(source, "buffer-time", G_GINT64_CONSTANT(40000), "latency-time", G_GINT64_CONSTANT(10000), NULL); #ifdef __APPLE__ g_object_set(source, "device", priv->device_index, NULL); #endif #endif break; case OWR_SOURCE_TYPE_TEST: CREATE_ELEMENT(source, "audiotestsrc", "audio-source"); g_object_set(source, "is-live", TRUE, NULL); break; case OWR_SOURCE_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } #if defined(__APPLE__) && !TARGET_IPHONE_SIMULATOR /* workaround for osxaudiosrc bug * https://bugzilla.gnome.org/show_bug.cgi?id=711764 */ CREATE_ELEMENT(capsfilter, "capsfilter", "audio-source-capsfilter"); source_caps = gst_caps_copy(caps); source_structure = gst_caps_get_structure(source_caps, 0); gst_structure_set(source_structure, "format", G_TYPE_STRING, "S32LE", "rate", G_TYPE_INT, 44100, NULL); gst_structure_remove_field(source_structure, "channels"); g_object_set(capsfilter, "caps", source_caps, NULL); gst_caps_unref(source_caps); gst_bin_add(GST_BIN(source_bin), capsfilter); #endif break; } case OWR_MEDIA_TYPE_VIDEO: { switch (source_type) { case OWR_SOURCE_TYPE_CAPTURE: CREATE_ELEMENT(source, VIDEO_SRC, "video-source"); #if defined(__APPLE__) && !TARGET_IPHONE_SIMULATOR g_object_set(source, "device-index", priv->device_index, NULL); #elif defined(__ANDROID__) g_object_set(source, "cam-index", priv->device_index, NULL); #elif defined(__linux__) { gchar *device = g_strdup_printf("/dev/video%u", priv->device_index); g_object_set(source, "device", device, NULL); g_free(device); } #endif break; case OWR_SOURCE_TYPE_TEST: CREATE_ELEMENT(source, "videotestsrc", "video-source"); g_object_set(source, "is-live", TRUE, NULL); break; case OWR_SOURCE_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } CREATE_ELEMENT(capsfilter, "capsfilter", "video-source-capsfilter"); source_caps = gst_caps_copy(caps); source_structure = gst_caps_get_structure(source_caps, 0); gst_structure_remove_field(source_structure, "format"); gst_structure_remove_field(source_structure, "framerate"); g_object_set(capsfilter, "caps", source_caps, NULL); gst_caps_unref(source_caps); gst_bin_add(GST_BIN(source_bin), capsfilter); break; } case OWR_MEDIA_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } g_assert(source); CREATE_ELEMENT(tee, "tee", "source-tee"); CREATE_ELEMENT(fakesink, "fakesink", "source-tee-fakesink"); g_object_set(fakesink, "async", FALSE, NULL); gst_bin_add_many(GST_BIN(source_bin), source, tee, fakesink, NULL); gst_element_sync_state_with_parent(fakesink); LINK_ELEMENTS(tee, fakesink); if (!source) GST_ERROR_OBJECT(media_source, "Failed to create source element!"); } codec_type = _owr_caps_to_codec_type(caps); source_id = g_atomic_int_add(&unique_pad_id, 1); pad_name = g_strdup_printf("src_%u_%u", codec_type, source_id); ghostpad = gst_ghost_pad_new_no_target(pad_name, GST_PAD_SRC); g_free(pad_name); post_tee_bin = create_post_tee_bin(media_source, source_bin, caps, ghostpad, source_id); if (!post_tee_bin) { gst_object_unref(ghostpad); ghostpad = NULL; goto done; } if (!gst_element_link(tee, post_tee_bin)) { GST_ERROR("Failed to link source tee to source-post-tee-bin-%u", source_id); g_object_unref(post_tee_bin); ghostpad = NULL; goto done; } if (!_owr_media_source_get_element(media_source)) { /* the next code block inside the if is a workaround for avfvideosrc * not handling on-the-fly reconfiguration * on upstream reconfigure events, we drop the event in the probe */ if (media_type == OWR_MEDIA_TYPE_VIDEO) { GstPad *tee_sinkpad; tee_sinkpad = gst_element_get_static_pad(tee, "sink"); gst_pad_add_probe(tee_sinkpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, drop_reconfigure_cb, NULL, NULL); } if (capsfilter) { LINK_ELEMENTS(capsfilter, tee); gst_element_sync_state_with_parent(tee); gst_element_sync_state_with_parent(capsfilter); LINK_ELEMENTS(source, capsfilter); } else { gst_element_sync_state_with_parent(tee); LINK_ELEMENTS(source, tee); } gst_element_sync_state_with_parent(source); _owr_media_source_set_element(media_source, source_bin); priv->source_tee = tee; } done: return ghostpad; }
/* * owr_local_media_source_get_pad * * The beginning of a media source chain in the pipeline looks like this: * +------------+ * /---+ inter*sink | * +--------+ +--------+ +------------+ +-----+ / +------------+ * | source +----+ scale? +---+ capsfilter +---+ tee +---/ * +--------+ +--------+ +------------+ +-----+ \ * \ +------------+ * \---+ inter*sink | * +------------+ * * For each newly requested pad a new inter*sink is added to the tee. * Note that this is a completely independent pipeline, and the complete * pipeline is only created once for a specific media source. * * Then for each newly requested pad another bin with a inter*src is * created, which is then going to be part of the transport agent * pipeline. The ghostpad of it is what we return here. * * +-----------+ +-------------------------------+ +----------+ * | inter*src +---+ converters/queues/capsfilters +---+ ghostpad | * +-----------+ +-------------------------------+ +----------+ * */ static GstElement *owr_local_media_source_request_source(OwrMediaSource *media_source, GstCaps *caps) { OwrLocalMediaSource *local_source; OwrLocalMediaSourcePrivate *priv; GstElement *source_element = NULL; GstElement *source_pipeline; GHashTable *event_data; GValue *value; #if defined(__linux__) && !defined(__ANDROID__) gchar *tmp; #endif g_assert(media_source); local_source = OWR_LOCAL_MEDIA_SOURCE(media_source); priv = local_source->priv; /* only create the source bin for this media source once */ if ((source_pipeline = _owr_media_source_get_source_bin(media_source))) GST_DEBUG_OBJECT(media_source, "Re-using existing source element/bin"); else { OwrMediaType media_type = OWR_MEDIA_TYPE_UNKNOWN; OwrSourceType source_type = OWR_SOURCE_TYPE_UNKNOWN; GstElement *source, *source_process = NULL, *capsfilter = NULL, *tee; GstPad *sinkpad, *source_pad; GEnumClass *media_enum_class, *source_enum_class; GEnumValue *media_enum_value, *source_enum_value; gchar *bin_name; GstCaps *source_caps; GstBus *bus; GSource *bus_source; event_data = _owr_value_table_new(); value = _owr_value_table_add(event_data, "start_time", G_TYPE_INT64); g_value_set_int64(value, g_get_monotonic_time()); g_object_get(media_source, "media-type", &media_type, "type", &source_type, NULL); media_enum_class = G_ENUM_CLASS(g_type_class_ref(OWR_TYPE_MEDIA_TYPE)); source_enum_class = G_ENUM_CLASS(g_type_class_ref(OWR_TYPE_SOURCE_TYPE)); media_enum_value = g_enum_get_value(media_enum_class, media_type); source_enum_value = g_enum_get_value(source_enum_class, source_type); bin_name = g_strdup_printf("local-%s-%s-source-bin-%u", media_enum_value ? media_enum_value->value_nick : "unknown", source_enum_value ? source_enum_value->value_nick : "unknown", g_atomic_int_add(&unique_bin_id, 1)); g_type_class_unref(media_enum_class); g_type_class_unref(source_enum_class); source_pipeline = gst_pipeline_new(bin_name); gst_pipeline_use_clock(GST_PIPELINE(source_pipeline), gst_system_clock_obtain()); gst_element_set_base_time(source_pipeline, _owr_get_base_time()); gst_element_set_start_time(source_pipeline, GST_CLOCK_TIME_NONE); g_free(bin_name); bin_name = NULL; #ifdef OWR_DEBUG g_signal_connect(source_pipeline, "deep-notify", G_CALLBACK(_owr_deep_notify), NULL); #endif bus = gst_pipeline_get_bus(GST_PIPELINE(source_pipeline)); bus_source = gst_bus_create_watch(bus); g_source_set_callback(bus_source, (GSourceFunc) bus_call, media_source, NULL); g_source_attach(bus_source, _owr_get_main_context()); g_source_unref(bus_source); GST_DEBUG_OBJECT(local_source, "media_type: %d, type: %d", media_type, source_type); if (media_type == OWR_MEDIA_TYPE_UNKNOWN || source_type == OWR_SOURCE_TYPE_UNKNOWN) { GST_ERROR_OBJECT(local_source, "Cannot connect source with unknown type or media type to other component"); goto done; } switch (media_type) { case OWR_MEDIA_TYPE_AUDIO: { switch (source_type) { case OWR_SOURCE_TYPE_CAPTURE: CREATE_ELEMENT(source, AUDIO_SRC, "audio-source"); #if !defined(__APPLE__) || !TARGET_IPHONE_SIMULATOR /* Default values for buffer-time and latency-time on android are 200ms and 20ms. The minimum latency-time that can be used on Android is 20ms, and using a 40ms buffer-time with a 20ms latency-time causes crackling audio. So let's just stick with the defaults. */ #if !defined(__ANDROID__) g_object_set(source, "buffer-time", G_GINT64_CONSTANT(40000), "latency-time", G_GINT64_CONSTANT(10000), NULL); #endif if (priv->device_index > -1) { #ifdef __APPLE__ g_object_set(source, "device", priv->device_index, NULL); #elif defined(__linux__) && !defined(__ANDROID__) tmp = g_strdup_printf("%d", priv->device_index); g_object_set(source, "device", tmp, NULL); g_free(tmp); #endif } #endif break; case OWR_SOURCE_TYPE_TEST: CREATE_ELEMENT(source, "audiotestsrc", "audio-source"); g_object_set(source, "is-live", TRUE, NULL); break; case OWR_SOURCE_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } break; } case OWR_MEDIA_TYPE_VIDEO: { GstPad *srcpad; GstCaps *device_caps; switch (source_type) { case OWR_SOURCE_TYPE_CAPTURE: CREATE_ELEMENT(source, VIDEO_SRC, "video-source"); if (priv->device_index > -1) { #if defined(__APPLE__) && !TARGET_IPHONE_SIMULATOR g_object_set(source, "device-index", priv->device_index, NULL); #elif defined(__ANDROID__) g_object_set(source, "cam-index", priv->device_index, NULL); #elif defined(__linux__) tmp = g_strdup_printf("/dev/video%d", priv->device_index); g_object_set(source, "device", tmp, NULL); g_free(tmp); #endif } break; case OWR_SOURCE_TYPE_TEST: { GstElement *src, *time; GstPad *srcpad; source = gst_bin_new("video-source"); CREATE_ELEMENT(src, "videotestsrc", "videotestsrc"); g_object_set(src, "is-live", TRUE, NULL); gst_bin_add(GST_BIN(source), src); time = gst_element_factory_make("timeoverlay", "timeoverlay"); if (time) { g_object_set(time, "font-desc", "Sans 60", NULL); gst_bin_add(GST_BIN(source), time); gst_element_link(src, time); srcpad = gst_element_get_static_pad(time, "src"); } else srcpad = gst_element_get_static_pad(src, "src"); gst_element_add_pad(source, gst_ghost_pad_new("src", srcpad)); gst_object_unref(srcpad); break; } case OWR_SOURCE_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } /* First try to see if we can just get the format we want directly */ source_caps = gst_caps_new_empty(); #if GST_CHECK_VERSION(1, 5, 0) gst_caps_foreach(caps, fix_video_caps_framerate, source_caps); #else _owr_gst_caps_foreach(caps, fix_video_caps_framerate, source_caps); #endif /* Now see what the device can really produce */ srcpad = gst_element_get_static_pad(source, "src"); gst_element_set_state(source, GST_STATE_READY); device_caps = gst_pad_query_caps(srcpad, source_caps); if (gst_caps_is_empty(device_caps)) { /* Let's see if it works when we drop format constraints (which can be dealt with downsteram) */ GstCaps *tmp = source_caps; source_caps = gst_caps_new_empty(); #if GST_CHECK_VERSION(1, 5, 0) gst_caps_foreach(tmp, fix_video_caps_format, source_caps); #else _owr_gst_caps_foreach(tmp, fix_video_caps_format, source_caps); #endif gst_caps_unref(tmp); gst_caps_unref(device_caps); device_caps = gst_pad_query_caps(srcpad, source_caps); if (gst_caps_is_empty(device_caps)) { /* Accepting any format didn't work, we're going to hope that scaling fixes it */ CREATE_ELEMENT(source_process, "videoscale", "video-source-scale"); gst_bin_add(GST_BIN(source_pipeline), source_process); } } gst_caps_unref(device_caps); gst_object_unref(srcpad); #if defined(__APPLE__) && TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR /* Force NV12 on iOS else the source can negotiate BGRA * ercolorspace can do NV12 -> BGRA and NV12 -> I420 which is what * is needed for Bowser */ gst_caps_set_simple(source_caps, "format", G_TYPE_STRING, "NV12", NULL); #endif CREATE_ELEMENT(capsfilter, "capsfilter", "video-source-capsfilter"); g_object_set(capsfilter, "caps", source_caps, NULL); gst_caps_unref(source_caps); gst_bin_add(GST_BIN(source_pipeline), capsfilter); break; } case OWR_MEDIA_TYPE_UNKNOWN: default: g_assert_not_reached(); goto done; } g_assert(source); source_pad = gst_element_get_static_pad(source, "src"); g_signal_connect(source_pad, "notify::caps", G_CALLBACK(on_caps), media_source); gst_object_unref(source_pad); CREATE_ELEMENT(tee, "tee", "source-tee"); g_object_set(tee, "allow-not-linked", TRUE, NULL); gst_bin_add_many(GST_BIN(source_pipeline), source, tee, NULL); /* Many sources don't like reconfiguration and it's pointless * here anyway right now. No need to reconfigure whenever something * is added to the tee or removed. * We will have to implement reconfiguration differently later by * selecting the best caps based on all consumers. */ sinkpad = gst_element_get_static_pad(tee, "sink"); gst_pad_add_probe(sinkpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM, drop_reconfigure_event, NULL, NULL); gst_object_unref(sinkpad); if (!source) GST_ERROR_OBJECT(media_source, "Failed to create source element!"); if (capsfilter) { LINK_ELEMENTS(capsfilter, tee); if (source_process) { LINK_ELEMENTS(source_process, capsfilter); LINK_ELEMENTS(source, source_process); } else LINK_ELEMENTS(source, capsfilter); } else if (source_process) { LINK_ELEMENTS(source_process, tee); LINK_ELEMENTS(source, source_process); } else LINK_ELEMENTS(source, tee); gst_element_sync_state_with_parent(tee); if (capsfilter) gst_element_sync_state_with_parent(capsfilter); if (source_process) gst_element_sync_state_with_parent(source_process); gst_element_sync_state_with_parent(source); _owr_media_source_set_source_bin(media_source, source_pipeline); _owr_media_source_set_source_tee(media_source, tee); if (gst_element_set_state(source_pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { GST_ERROR("Failed to set local source pipeline %s to playing", GST_OBJECT_NAME(source_pipeline)); /* FIXME: We should handle this and don't expose the source */ } value = _owr_value_table_add(event_data, "end_time", G_TYPE_INT64); g_value_set_int64(value, g_get_monotonic_time()); OWR_POST_EVENT(media_source, LOCAL_SOURCE_STARTED, event_data); g_signal_connect(tee, "pad-removed", G_CALLBACK(tee_pad_removed_cb), media_source); } gst_object_unref(source_pipeline); source_element = OWR_MEDIA_SOURCE_CLASS(owr_local_media_source_parent_class)->request_source(media_source, caps); done: return source_element; }
static gpointer owr_local_media_source_get_bus_set(OwrMessageOrigin *origin) { return OWR_LOCAL_MEDIA_SOURCE(origin)->priv->message_origin_bus_set; }