📄 gsta2dpsink.c
字号:
element_class->change_state = GST_DEBUG_FUNCPTR( gst_a2dp_sink_change_state); g_object_class_install_property(object_class, PROP_DEVICE, g_param_spec_string("device", "Device", "Bluetooth remote device address", NULL, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_AUTOCONNECT, g_param_spec_boolean("auto-connect", "Auto-connect", "Automatically attempt to connect to device", DEFAULT_AUTOCONNECT, G_PARAM_READWRITE)); GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0, "A2DP sink element");}GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self){ return gst_avdtp_sink_get_device_caps(self->sink);}static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad){ GstCaps *caps; GstCaps *caps_aux; GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); if (self->sink == NULL) { GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized " "returning template caps"); caps = gst_static_pad_template_get_caps( &gst_a2dp_sink_factory); } else { GST_LOG_OBJECT(self, "Getting device caps"); caps = gst_a2dp_sink_get_device_caps(self); if (caps == NULL) caps = gst_static_pad_template_get_caps( &gst_a2dp_sink_factory); } caps_aux = gst_caps_copy(caps); g_object_set(self->capsfilter, "caps", caps_aux, NULL); gst_caps_unref(caps_aux); return caps;}static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self){ GstElement *sink; /* check if we don't need a new sink */ if (self->sink_is_in_bin) return TRUE; if (self->sink == NULL) sink = gst_element_factory_make("avdtpsink", "avdtpsink"); else sink = GST_ELEMENT(self->sink); if (sink == NULL) { GST_ERROR_OBJECT(self, "Couldn't create avdtpsink"); return FALSE; } if (!gst_bin_add(GST_BIN(self), sink)) { GST_ERROR_OBJECT(self, "failed to add avdtpsink " "to the bin"); goto cleanup_and_fail; } if (gst_element_set_state(sink, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE) { GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready"); goto remove_element_and_fail; } if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) { GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay " "to avdtpsink"); goto remove_element_and_fail; } self->sink = GST_AVDTP_SINK(sink); self->sink_is_in_bin = TRUE; g_object_set(G_OBJECT(self->sink), "device", self->device, NULL); gst_element_set_state(sink, GST_STATE_PAUSED); return TRUE;remove_element_and_fail: gst_element_set_state (sink, GST_STATE_NULL); gst_bin_remove(GST_BIN(self), sink); return FALSE;cleanup_and_fail: if (sink != NULL) g_object_unref(G_OBJECT(sink)); return FALSE;}static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self){ GstElement *rtppay; /* if we already have a rtp, we don't need a new one */ if (self->rtp != NULL) return TRUE; rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp", self->capsfilter); if (rtppay == NULL) return FALSE; self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL); gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE;}static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self){ GstElement *rtppay; /* check if we don't need a new rtp */ if (self->rtp) return TRUE; GST_LOG_OBJECT(self, "Initializing rtp mpeg element"); /* if capsfilter is not created then we can't have our rtp element */ if (self->capsfilter == NULL) return FALSE; rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp", self->capsfilter); if (rtppay == NULL) return FALSE; self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); gst_element_set_state(rtppay, GST_STATE_PAUSED); return TRUE;}static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, GstCaps *caps){ GstStructure *structure; GstEvent *event; GstPad *capsfilterpad; gboolean crc; gchar *mode = NULL; structure = gst_caps_get_structure(caps, 0); /* before everything we need to remove fakesink */ gst_a2dp_sink_remove_fakesink(self); /* first, we need to create our rtp payloader */ if (gst_structure_has_name(structure, "audio/x-sbc")) { GST_LOG_OBJECT(self, "sbc media received"); if (!gst_a2dp_sink_init_rtp_sbc_element(self)) return FALSE; } else if (gst_structure_has_name(structure, "audio/mpeg")) { GST_LOG_OBJECT(self, "mp3 media received"); if (!gst_a2dp_sink_init_rtp_mpeg_element(self)) return FALSE; } else { GST_ERROR_OBJECT(self, "Unexpected media type"); return FALSE; } if (!gst_a2dp_sink_init_avdtp_sink(self)) return FALSE; /* check if we should push the taglist FIXME should we push this? * we can send the tags directly if needed */ if (self->taglist != NULL && gst_structure_has_name(structure, "audio/mpeg")) { event = gst_event_new_tag(self->taglist); /* send directly the crc */ if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc)) gst_avdtp_sink_set_crc(self->sink, crc); if (gst_tag_list_get_string(self->taglist, "channel-mode", &mode)) gst_avdtp_sink_set_channel_mode(self->sink, mode); capsfilterpad = gst_ghost_pad_get_target(self->ghostpad); gst_pad_send_event(capsfilterpad, event); self->taglist = NULL; g_free(mode); } if (!gst_avdtp_sink_set_device_caps(self->sink, caps)) return FALSE; g_object_set(G_OBJECT(self->rtp), "mtu", gst_avdtp_sink_get_link_mtu(self->sink), NULL); /* we forward our new segment here if we have one */ if (self->newseg_event) { gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), self->newseg_event); self->newseg_event = NULL; } return TRUE;}static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps){ GstA2dpSink *self; self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); GST_INFO_OBJECT(self, "setting caps"); /* now we know the caps */ gst_a2dp_sink_init_dynamic_elements(self, caps); return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);}/* used for catching newsegment events while we don't have a sink, for * later forwarding it to the sink */static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event){ GstA2dpSink *self; GstTagList *taglist = NULL; GstObject *parent; self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); parent = gst_element_get_parent(GST_ELEMENT(self->sink)); if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT && parent != GST_OBJECT_CAST(self)) { if (self->newseg_event != NULL) gst_event_unref(self->newseg_event); self->newseg_event = gst_event_ref(event); } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG && parent != GST_OBJECT_CAST(self)) { if (self->taglist == NULL) gst_event_parse_tag(event, &self->taglist); else { gst_event_parse_tag(event, &taglist); gst_tag_list_insert(self->taglist, taglist, GST_TAG_MERGE_REPLACE); } } if (parent != NULL) gst_object_unref(GST_OBJECT(parent)); return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);}static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self){ GstElement *element; element = gst_element_factory_make("capsfilter", "filter"); if (element == NULL) goto failed; if (!gst_bin_add(GST_BIN(self), element)) goto failed; self->capsfilter = element; return TRUE;failed: GST_ERROR_OBJECT(self, "Failed to initialize caps filter"); return FALSE;}static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self){ if (self->fakesink != NULL) return TRUE; g_mutex_lock (self->cb_mutex); self->fakesink = gst_a2dp_sink_init_element(self, "fakesink", "fakesink", self->capsfilter); g_mutex_unlock (self->cb_mutex); if (!self->fakesink) return FALSE; return TRUE;}static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self){ g_mutex_lock(self->cb_mutex); if (self->fakesink != NULL) { gst_element_set_locked_state(self->fakesink, TRUE); gst_element_set_state(self->fakesink, GST_STATE_NULL); gst_bin_remove(GST_BIN(self), self->fakesink); self->fakesink = NULL; } g_mutex_unlock(self->cb_mutex); return TRUE;}static void gst_a2dp_sink_init(GstA2dpSink *self, GstA2dpSinkClass *klass){ self->sink = NULL; self->fakesink = NULL; self->rtp = NULL; self->device = NULL; self->autoconnect = DEFAULT_AUTOCONNECT; self->capsfilter = NULL; self->newseg_event = NULL; self->taglist = NULL; self->ghostpad = NULL; self->sink_is_in_bin = FALSE; self->cb_mutex = g_mutex_new(); /* we initialize our capsfilter */ gst_a2dp_sink_init_caps_filter(self); g_object_set(self->capsfilter, "caps", gst_static_pad_template_get_caps(&gst_a2dp_sink_factory), NULL); gst_a2dp_sink_init_fakesink(self); gst_a2dp_sink_init_ghost_pad(self);}gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin){ return gst_element_register (plugin, "a2dpsink", GST_RANK_PRIMARY, GST_TYPE_A2DP_SINK);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -