/* from the given two data buffers, create two streamheader buffers and * some caps that match it, and store them in the given pointers * returns one ref to each of the buffers and the caps */ static void gst_multisocketsink_create_streamheader (const gchar * data1, const gchar * data2, GstBuffer ** hbuf1, GstBuffer ** hbuf2, GstCaps ** caps) { GstBuffer *buf; GValue array = { 0 }; GValue value = { 0 }; GstStructure *structure; guint size1 = strlen (data1); guint size2 = strlen (data2); fail_if (hbuf1 == NULL); fail_if (hbuf2 == NULL); fail_if (caps == NULL); /* create caps with streamheader, set the caps, and push the HEADER * buffers */ *hbuf1 = gst_buffer_new_and_alloc (size1); GST_BUFFER_FLAG_SET (*hbuf1, GST_BUFFER_FLAG_HEADER); gst_buffer_fill (*hbuf1, 0, data1, size1); *hbuf2 = gst_buffer_new_and_alloc (size2); GST_BUFFER_FLAG_SET (*hbuf2, GST_BUFFER_FLAG_HEADER); gst_buffer_fill (*hbuf2, 0, data2, size2); g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* we take a copy, set it on the array (which refs it), then unref our copy */ buf = gst_buffer_copy (*hbuf1); gst_value_set_buffer (&value, buf); ASSERT_BUFFER_REFCOUNT (buf, "copied buffer", 2); gst_buffer_unref (buf); gst_value_array_append_value (&array, &value); g_value_unset (&value); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_copy (*hbuf2); gst_value_set_buffer (&value, buf); ASSERT_BUFFER_REFCOUNT (buf, "copied buffer", 2); gst_buffer_unref (buf); gst_value_array_append_value (&array, &value); g_value_unset (&value); *caps = gst_caps_from_string ("application/x-gst-check"); structure = gst_caps_get_structure (*caps, 0); gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&array); ASSERT_CAPS_REFCOUNT (*caps, "streamheader caps", 1); /* we want to keep them around for the tests */ gst_buffer_ref (*hbuf1); gst_buffer_ref (*hbuf2); GST_DEBUG ("created streamheader caps %p %" GST_PTR_FORMAT, *caps, *caps); }
static GstCaps * theora_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, GstBuffer * buf2, GstBuffer * buf3) { GstStructure *structure; GValue array = { 0 }; GValue value = { 0 }; caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); /* mark buffers */ GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS); /* Copy buffers, because we can't use the originals - * it creates a circular refcount with the caps<->buffers */ buf1 = gst_buffer_copy (buf1); buf2 = gst_buffer_copy (buf2); buf3 = gst_buffer_copy (buf3); /* put copies of the buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buf1); gst_value_array_append_value (&array, &value); g_value_unset (&value); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buf2); gst_value_array_append_value (&array, &value); g_value_unset (&value); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buf3); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&array); /* Unref our copies */ gst_buffer_unref (buf1); gst_buffer_unref (buf2); gst_buffer_unref (buf3); return caps; }
/* * _gst_caps_set_buffer_array: * @caps: (transfer full): a #GstCaps * @field: field in caps to set * @buf: header buffers * * Adds given buffers to an array of buffers set as the given @field * on the given @caps. List of buffer arguments must be NULL-terminated. * * Returns: (transfer full): input caps with a streamheader field added, or NULL * if some error occurred */ static GstCaps * _gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, GstBuffer * buf, ...) { GstStructure *structure = NULL; va_list va; GValue array = { 0 }; GValue value = { 0 }; g_return_val_if_fail (caps != NULL, NULL); g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); g_return_val_if_fail (field != NULL, NULL); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&array, GST_TYPE_ARRAY); va_start (va, buf); /* put buffers in a fixed list */ while (buf) { g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buf); gst_value_array_append_value (&array, &value); g_value_unset (&value); buf = va_arg (va, GstBuffer *); } va_end (va); gst_structure_take_value (structure, field, &array); return caps; }
static GstCaps * theora_set_header_on_caps (GstCaps * caps, GList * buffers) { GstStructure *structure; GValue array = { 0 }; GValue value = { 0 }; GstBuffer *buffer; GList *walk; caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); /* put copies of the buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); for (walk = buffers; walk; walk = walk->next) { buffer = walk->data; g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buffer); gst_value_array_append_value (&array, &value); g_value_unset (&value); } gst_structure_take_value (structure, "streamheader", &array); return caps; }
static void theora_parse_set_header_on_caps (GstTheoraParse * parse, GstCaps * caps) { GstBuffer **bufs; GstStructure *structure; gint i; GValue array = { 0 }; GValue value = { 0 }; bufs = parse->streamheader; structure = gst_caps_get_structure (caps, 0); g_value_init (&array, GST_TYPE_ARRAY); for (i = 0; i < 3; i++) { if (bufs[i] == NULL) continue; bufs[i] = gst_buffer_make_writable (bufs[i]); GST_BUFFER_FLAG_SET (bufs[i], GST_BUFFER_FLAG_HEADER); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, bufs[i]); gst_value_array_append_value (&array, &value); g_value_unset (&value); } gst_structure_take_value (structure, "streamheader", &array); }
static void vorbis_parse_set_header_on_caps (GstVorbisParse * parse, GstCaps * caps) { GstBuffer *buf1, *buf2, *buf3; GstStructure *structure; GValue array = { 0 }; GValue value = { 0 }; g_assert (parse); g_assert (parse->streamheader); g_assert (parse->streamheader->next); g_assert (parse->streamheader->next->next); buf1 = parse->streamheader->data; g_assert (buf1); buf2 = parse->streamheader->next->data; g_assert (buf2); buf3 = parse->streamheader->next->next->data; g_assert (buf3); structure = gst_caps_get_structure (caps, 0); /* mark buffers */ GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buf1); gst_value_array_append_value (&array, &value); g_value_unset (&value); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buf2); gst_value_array_append_value (&array, &value); g_value_unset (&value); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buf3); gst_value_array_append_value (&array, &value); gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&value); g_value_unset (&array); }
static GstCaps * gst_cmml_enc_set_header_on_caps (GstCmmlEnc * enc, GstCaps * caps, GstBuffer * ident, GstBuffer * preamble, GstBuffer * head) { GValue array = { 0 }; GValue value = { 0 }; GstStructure *structure; GstBuffer *buffer; caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Make copies of header buffers to avoid circular references */ buffer = gst_buffer_copy (ident); gst_value_set_buffer (&value, buffer); gst_value_array_append_value (&array, &value); gst_buffer_unref (buffer); buffer = gst_buffer_copy (preamble); gst_value_set_buffer (&value, buffer); gst_value_array_append_value (&array, &value); gst_buffer_unref (buffer); buffer = gst_buffer_copy (head); gst_value_set_buffer (&value, buffer); gst_value_array_append_value (&array, &value); gst_buffer_unref (buffer); GST_BUFFER_FLAG_SET (ident, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (preamble, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (head, GST_BUFFER_FLAG_IN_CAPS); gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&value); g_value_unset (&array); return caps; }
/* Set a copy of these buffers as 'streamheader' on the caps. * We need a copy to avoid these buffers ending up with (indirect) refs on * themselves */ static GstCaps * gst_vorbis_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, GstBuffer * buf2, GstBuffer * buf3) { GstBuffer *buf; GstStructure *structure; GValue array = { 0 }; GValue value = { 0 }; caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); /* mark buffers */ GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS); GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_copy (buf1); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_value_array_append_value (&array, &value); g_value_unset (&value); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_copy (buf2); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_value_array_append_value (&array, &value); g_value_unset (&value); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_copy (buf3); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_value_array_append_value (&array, &value); gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&value); g_value_unset (&array); return caps; }
static void gst_ogg_parse_append_header (GValue * array, GstBuffer * buf) { GValue value = { 0 }; /* We require a copy to avoid circular refcounts */ GstBuffer *buffer = gst_buffer_copy (buf); GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_HEADER); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buffer); gst_value_array_append_value (array, &value); g_value_unset (&value); }
static void gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay) { gchar *profile, *config; GValue v = { 0 }; profile = g_strdup_printf ("%d", rtpmp4vpay->profile); g_value_init (&v, GST_TYPE_BUFFER); gst_value_set_buffer (&v, rtpmp4vpay->config); config = gst_value_serialize (&v); gst_basertppayload_set_outcaps (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), "profile-level-id", G_TYPE_STRING, profile, "config", G_TYPE_STRING, config, NULL); g_value_unset (&v); g_free (profile); g_free (config); }
GstCaps * gst_kate_util_set_header_on_caps (GstElement * element, GstCaps * caps, GList * headers) { GstStructure *structure; GValue array = { 0 }; GST_LOG_OBJECT (element, "caps: %" GST_PTR_FORMAT, caps); if (G_UNLIKELY (!caps)) return NULL; if (G_UNLIKELY (!headers)) return NULL; caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&array, GST_TYPE_ARRAY); while (headers) { GValue value = { 0 }; GstBuffer *buffer = headers->data; g_assert (buffer); GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_IN_CAPS); g_value_init (&value, GST_TYPE_BUFFER); /* as in theoraenc, we need to copy to avoid circular references */ buffer = gst_buffer_copy (buffer); gst_value_set_buffer (&value, buffer); gst_buffer_unref (buffer); gst_value_array_append_value (&array, &value); g_value_unset (&value); headers = headers->next; } gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&array); GST_LOG_OBJECT (element, "here are the newly set caps: %" GST_PTR_FORMAT, caps); return caps; }
static GstCaps * theora_set_header_on_caps (GstCaps * caps, GList * buffers) { GstStructure *structure; GValue array = { 0 }; GValue value = { 0 }; GstBuffer *buffer; GList *walk; caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); /* put copies of the buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); for (walk = buffers; walk; walk = walk->next) { buffer = walk->data; /* mark buffer */ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_HEADER); /* Copy buffer, because we can't use the original - * it creates a circular refcount with the caps<->buffers */ buffer = gst_buffer_copy (buffer); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, buffer); gst_value_array_append_value (&array, &value); g_value_unset (&value); /* Unref our copy */ gst_buffer_unref (buffer); } gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&array); return caps; }
static gboolean gst_rtp_mp4g_pay_new_caps (GstRtpMP4GPay * rtpmp4gpay) { gchar *config; GValue v = { 0 }; gboolean res; #define MP4GCAPS \ "streamtype", G_TYPE_STRING, rtpmp4gpay->streamtype, \ "profile-level-id", G_TYPE_STRING, rtpmp4gpay->profile, \ "mode", G_TYPE_STRING, rtpmp4gpay->mode, \ "config", G_TYPE_STRING, config, \ "sizelength", G_TYPE_STRING, "13", \ "indexlength", G_TYPE_STRING, "3", \ "indexdeltalength", G_TYPE_STRING, "3", \ NULL g_value_init (&v, GST_TYPE_BUFFER); gst_value_set_buffer (&v, rtpmp4gpay->config); config = gst_value_serialize (&v); /* hmm, silly */ if (rtpmp4gpay->params) { res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), "encoding-params", G_TYPE_STRING, rtpmp4gpay->params, MP4GCAPS); } else { res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), MP4GCAPS); } g_value_unset (&v); g_free (config); #undef MP4GCAPS return res; }
static gboolean gst_vp8_enc_set_format (GstBaseVideoEncoder * base_video_encoder, GstVideoState * state) { GstVP8Enc *encoder; vpx_codec_enc_cfg_t cfg; vpx_codec_err_t status; vpx_image_t *image; guint8 *data = NULL; GstCaps *caps; gboolean ret; encoder = GST_VP8_ENC (base_video_encoder); GST_DEBUG_OBJECT (base_video_encoder, "set_format"); if (encoder->inited) { GST_DEBUG_OBJECT (base_video_encoder, "refusing renegotiation"); return FALSE; } status = vpx_codec_enc_config_default (&vpx_codec_vp8_cx_algo, &cfg, 0); if (status != VPX_CODEC_OK) { GST_ELEMENT_ERROR (encoder, LIBRARY, INIT, ("Failed to get default encoder configuration"), ("%s", gst_vpx_error_name (status))); return FALSE; } /* Scale default bitrate to our size */ cfg.rc_target_bitrate = gst_util_uint64_scale (cfg.rc_target_bitrate, state->width * state->height, cfg.g_w * cfg.g_h); cfg.g_w = state->width; cfg.g_h = state->height; cfg.g_timebase.num = state->fps_d; cfg.g_timebase.den = state->fps_n; cfg.g_error_resilient = encoder->error_resilient; cfg.g_lag_in_frames = encoder->max_latency; cfg.g_threads = encoder->threads; cfg.rc_end_usage = encoder->mode; cfg.rc_2pass_vbr_minsection_pct = encoder->minsection_pct; cfg.rc_2pass_vbr_maxsection_pct = encoder->maxsection_pct; /* Standalone qp-min do not make any sence, with bitrate=0 and qp-min=1 * encoder will use only default qp-max=63. Also this will make * worst possbile quality. */ if (encoder->bitrate != DEFAULT_BITRATE || encoder->max_quantizer != DEFAULT_MAX_QUANTIZER) { cfg.rc_target_bitrate = encoder->bitrate / 1000; cfg.rc_min_quantizer = encoder->min_quantizer; cfg.rc_max_quantizer = encoder->max_quantizer; } else { cfg.rc_min_quantizer = (gint) (63 - encoder->quality * 6.2); cfg.rc_max_quantizer = (gint) (63 - encoder->quality * 6.2); } cfg.rc_dropframe_thresh = encoder->drop_frame; cfg.rc_resize_allowed = encoder->resize_allowed; cfg.kf_mode = VPX_KF_AUTO; cfg.kf_min_dist = 0; cfg.kf_max_dist = encoder->max_keyframe_distance; cfg.g_pass = encoder->multipass_mode; if (encoder->multipass_mode == VPX_RC_FIRST_PASS) { encoder->first_pass_cache_content = g_byte_array_sized_new (4096); } else if (encoder->multipass_mode == VPX_RC_LAST_PASS) { GError *err = NULL; if (!encoder->multipass_cache_file) { GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ, ("No multipass cache file provided"), (NULL)); return FALSE; } if (!g_file_get_contents (encoder->multipass_cache_file, (gchar **) & encoder->last_pass_cache_content.buf, &encoder->last_pass_cache_content.sz, &err)) { GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ, ("Failed to read multipass cache file provided"), ("%s", err->message)); g_error_free (err); return FALSE; } cfg.rc_twopass_stats_in = encoder->last_pass_cache_content; } status = vpx_codec_enc_init (&encoder->encoder, &vpx_codec_vp8_cx_algo, &cfg, 0); if (status != VPX_CODEC_OK) { GST_ELEMENT_ERROR (encoder, LIBRARY, INIT, ("Failed to initialize encoder"), ("%s", gst_vpx_error_name (status))); return FALSE; } /* FIXME move this to a set_speed() function */ status = vpx_codec_control (&encoder->encoder, VP8E_SET_CPUUSED, (encoder->speed == 0) ? 0 : (encoder->speed - 1)); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_CPUUSED to 0: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_NOISE_SENSITIVITY, encoder->noise_sensitivity); status = vpx_codec_control (&encoder->encoder, VP8E_SET_SHARPNESS, encoder->sharpness); status = vpx_codec_control (&encoder->encoder, VP8E_SET_STATIC_THRESHOLD, encoder->static_threshold); status = vpx_codec_control (&encoder->encoder, VP8E_SET_TOKEN_PARTITIONS, encoder->partitions); #if 0 status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_MAXFRAMES, encoder->arnr_maxframes); status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_STRENGTH, encoder->arnr_strength); status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_TYPE, encoder->arnr_type); #endif #ifdef HAVE_VP8ENC_TUNING status = vpx_codec_control (&encoder->encoder, VP8E_SET_TUNING, encoder->tuning); #endif status = vpx_codec_control (&encoder->encoder, VP8E_SET_ENABLEAUTOALTREF, (encoder->auto_alt_ref_frames ? 1 : 0)); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_ENABLEAUTOALTREF to %d: %s", (encoder->auto_alt_ref_frames ? 1 : 0), gst_vpx_error_name (status)); } cfg.g_lag_in_frames = encoder->lag_in_frames; gst_base_video_encoder_set_latency (base_video_encoder, 0, gst_util_uint64_scale (encoder->max_latency, state->fps_d * GST_SECOND, state->fps_n)); encoder->inited = TRUE; /* prepare cached image buffer setup */ image = &encoder->image; memset (image, 0, sizeof (*image)); image->fmt = VPX_IMG_FMT_I420; image->bps = 12; image->x_chroma_shift = image->y_chroma_shift = 1; image->w = image->d_w = state->width; image->h = image->d_h = state->height; image->stride[VPX_PLANE_Y] = gst_video_format_get_row_stride (state->format, 0, state->width); image->stride[VPX_PLANE_U] = gst_video_format_get_row_stride (state->format, 1, state->width); image->stride[VPX_PLANE_V] = gst_video_format_get_row_stride (state->format, 2, state->width); image->planes[VPX_PLANE_Y] = data + gst_video_format_get_component_offset (state->format, 0, state->width, state->height); image->planes[VPX_PLANE_U] = data + gst_video_format_get_component_offset (state->format, 1, state->width, state->height); image->planes[VPX_PLANE_V] = data + gst_video_format_get_component_offset (state->format, 2, state->width, state->height); caps = gst_caps_new_simple ("video/x-vp8", "width", G_TYPE_INT, state->width, "height", G_TYPE_INT, state->height, "framerate", GST_TYPE_FRACTION, state->fps_n, state->fps_d, "pixel-aspect-ratio", GST_TYPE_FRACTION, state->par_n, state->par_d, NULL); { GstStructure *s; GstBuffer *stream_hdr, *vorbiscomment; const GstTagList *iface_tags; GValue array = { 0, }; GValue value = { 0, }; s = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Create Ogg stream-info */ stream_hdr = gst_buffer_new_and_alloc (26); data = GST_BUFFER_DATA (stream_hdr); GST_WRITE_UINT8 (data, 0x4F); GST_WRITE_UINT32_BE (data + 1, 0x56503830); /* "VP80" */ GST_WRITE_UINT8 (data + 5, 0x01); /* stream info header */ GST_WRITE_UINT8 (data + 6, 1); /* Major version 1 */ GST_WRITE_UINT8 (data + 7, 0); /* Minor version 0 */ GST_WRITE_UINT16_BE (data + 8, state->width); GST_WRITE_UINT16_BE (data + 10, state->height); GST_WRITE_UINT24_BE (data + 12, state->par_n); GST_WRITE_UINT24_BE (data + 15, state->par_d); GST_WRITE_UINT32_BE (data + 18, state->fps_n); GST_WRITE_UINT32_BE (data + 22, state->fps_d); GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_IN_CAPS); gst_value_set_buffer (&value, stream_hdr); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (stream_hdr); iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (base_video_encoder)); if (iface_tags) { vorbiscomment = gst_tag_list_to_vorbiscomment_buffer (iface_tags, (const guint8 *) "OVP80\2 ", 7, "Encoded with GStreamer vp8enc " PACKAGE_VERSION); GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_IN_CAPS); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, vorbiscomment); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (vorbiscomment); } gst_structure_set_value (s, "streamheader", &array); g_value_unset (&array); } ret = gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (encoder), caps); gst_caps_unref (caps); return ret; }
static void output_loop (gpointer data) { GstPad *pad; GOmxCore *gomx; GOmxPort *out_port; GstOmxBaseFilter *self; GstFlowReturn ret = GST_FLOW_OK; pad = data; self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad)); gomx = self->gomx; GST_LOG_OBJECT (self, "begin"); /* do not bother if we have been setup to bail out */ if ((ret = g_atomic_int_get (&self->last_pad_push_return)) != GST_FLOW_OK) goto leave; if (!self->ready) { g_error ("not ready"); return; } out_port = self->out_port; if (G_LIKELY (out_port->enabled)) { OMX_BUFFERHEADERTYPE *omx_buffer = NULL; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (out_port); GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer); if (G_UNLIKELY (!omx_buffer)) { GST_WARNING_OBJECT (self, "null buffer: leaving"); ret = GST_FLOW_WRONG_STATE; goto leave; } log_buffer (self, omx_buffer); if (G_LIKELY (omx_buffer->nFilledLen > 0)) { GstBuffer *buf; #if 1 /** @todo remove this check */ if (G_LIKELY (self->in_port->enabled)) { GstCaps *caps = NULL; caps = gst_pad_get_negotiated_caps (self->srcpad); #ifdef ANDROID if (!caps || gomx->settings_changed) { #else if (!caps) { #endif /** @todo We shouldn't be doing this. */ GST_WARNING_OBJECT (self, "faking settings changed notification"); if (gomx->settings_changed_cb) gomx->settings_changed_cb (gomx); #ifdef ANDROID gomx->settings_changed = FALSE; #endif } else { GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps); gst_caps_unref (caps); } } #endif /* buf is always null when the output buffer pointer isn't shared. */ buf = omx_buffer->pAppPrivate; /** @todo we need to move all the caps handling to one single * place, in the output loop probably. */ if (G_UNLIKELY (omx_buffer->nFlags & 0x80)) { GstCaps *caps = NULL; GstStructure *structure; GValue value = { 0, {{0} } }; caps = gst_pad_get_negotiated_caps (self->srcpad); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen); memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_structure_set_value (structure, "codec_data", &value); g_value_unset (&value); gst_pad_set_caps (self->srcpad, caps); } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_BUFFER_SIZE (buf) = omx_buffer->nFilledLen; if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } omx_buffer->pAppPrivate = NULL; omx_buffer->pBuffer = NULL; ret = push_buffer (self, buf); gst_buffer_unref (buf); } else { /* This is only meant for the first OpenMAX buffers, * which need to be pre-allocated. */ /* Also for the very last one. */ ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nFilledLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (buf)) { memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } if (self->share_output_buffer) { GST_WARNING_OBJECT (self, "couldn't zero-copy"); /* If pAppPrivate is NULL, it means it was a dummy * allocation, free it. */ if (!omx_buffer->pAppPrivate) { g_free (omx_buffer->pBuffer); omx_buffer->pBuffer = NULL; } } ret = push_buffer (self, buf); } else { GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %lu", omx_buffer->nFilledLen); } } } else { GST_WARNING_OBJECT (self, "empty buffer"); } if (self->share_output_buffer && !omx_buffer->pBuffer && omx_buffer->nOffset == 0) { GstBuffer *buf; GstFlowReturn result; GST_LOG_OBJECT (self, "allocate buffer"); result = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (result == GST_FLOW_OK)) { gst_buffer_ref (buf); omx_buffer->pAppPrivate = buf; omx_buffer->pBuffer = GST_BUFFER_DATA (buf); omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf); } else { GST_WARNING_OBJECT (self, "could not pad allocate buffer, using malloc"); omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen); } } if (self->share_output_buffer && !omx_buffer->pBuffer) { GST_ERROR_OBJECT (self, "no input buffer to share"); } if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_DEBUG_OBJECT (self, "got eos"); gst_pad_push_event (self->srcpad, gst_event_new_eos ()); omx_buffer->nFlags &= ~OMX_BUFFERFLAG_EOS; ret = GST_FLOW_UNEXPECTED; } omx_buffer->nFilledLen = 0; GST_LOG_OBJECT (self, "release_buffer"); g_omx_port_release_buffer (out_port, omx_buffer); } leave: self->last_pad_push_return = ret; if (gomx->omx_error != OMX_ErrorNone) ret = GST_FLOW_ERROR; if (ret != GST_FLOW_OK) { GST_INFO_OBJECT (self, "pause task, reason: %s", gst_flow_get_name (ret)); gst_pad_pause_task (self->srcpad); } GST_LOG_OBJECT (self, "end"); gst_object_unref (self); } static GstFlowReturn pad_chain (GstPad * pad, GstBuffer * buf) { GOmxCore *gomx; GOmxPort *in_port; GstOmxBaseFilter *self; GstFlowReturn ret = GST_FLOW_OK; self = GST_OMX_BASE_FILTER (GST_OBJECT_PARENT (pad)); gomx = self->gomx; GST_LOG_OBJECT (self, "begin"); GST_LOG_OBJECT (self, "gst_buffer: size=%u", GST_BUFFER_SIZE (buf)); GST_LOG_OBJECT (self, "state: %d", gomx->omx_state); if (G_UNLIKELY (gomx->omx_state == OMX_StateLoaded)) { g_mutex_lock (self->ready_lock); GST_INFO_OBJECT (self, "omx: prepare"); /** @todo this should probably go after doing preparations. */ if (self->omx_setup) { self->omx_setup (self); } setup_ports (self); g_omx_core_prepare (self->gomx); if (gomx->omx_state == OMX_StateIdle) { self->ready = TRUE; GST_INFO_OBJECT (self, "start srcpad task"); gst_pad_start_task (self->srcpad, output_loop, self->srcpad); } g_mutex_unlock (self->ready_lock); if (gomx->omx_state != OMX_StateIdle) goto out_flushing; } #ifdef ANDROID if (gomx->settings_changed) { GST_DEBUG_OBJECT (self, "settings changed called from streaming thread... Android"); if (gomx->settings_changed_cb) gomx->settings_changed_cb (gomx); gomx->settings_changed = FALSE; } #endif in_port = self->in_port; if (G_LIKELY (in_port->enabled)) { guint buffer_offset = 0; if (G_UNLIKELY (gomx->omx_state == OMX_StateIdle)) { GST_INFO_OBJECT (self, "omx: play"); g_omx_core_start (gomx); if (gomx->omx_state != OMX_StateExecuting) goto out_flushing; /* send buffer with codec data flag */ /** @todo move to util */ if (self->codec_data) { OMX_BUFFERHEADERTYPE *omx_buffer; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (in_port); if (G_LIKELY (omx_buffer)) { omx_buffer->nFlags |= 0x00000080; /* codec data flag */ omx_buffer->nFilledLen = GST_BUFFER_SIZE (self->codec_data); memcpy (omx_buffer->pBuffer + omx_buffer->nOffset, GST_BUFFER_DATA (self->codec_data), omx_buffer->nFilledLen); GST_LOG_OBJECT (self, "release_buffer"); g_omx_port_release_buffer (in_port, omx_buffer); } } } if (G_UNLIKELY (gomx->omx_state != OMX_StateExecuting)) { GST_ERROR_OBJECT (self, "Whoa! very wrong"); } while (G_LIKELY (buffer_offset < GST_BUFFER_SIZE (buf))) { OMX_BUFFERHEADERTYPE *omx_buffer; if (self->last_pad_push_return != GST_FLOW_OK || !(gomx->omx_state == OMX_StateExecuting || gomx->omx_state == OMX_StatePause)) { goto out_flushing; } GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (in_port); GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer); if (G_LIKELY (omx_buffer)) { log_buffer (self, omx_buffer); if (omx_buffer->nOffset == 0 && self->share_input_buffer) { { GstBuffer *old_buf; old_buf = omx_buffer->pAppPrivate; if (old_buf) { gst_buffer_unref (old_buf); } else if (omx_buffer->pBuffer) { g_free (omx_buffer->pBuffer); } } omx_buffer->pBuffer = GST_BUFFER_DATA (buf); omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf); omx_buffer->nFilledLen = GST_BUFFER_SIZE (buf); omx_buffer->pAppPrivate = buf; } else { omx_buffer->nFilledLen = MIN (GST_BUFFER_SIZE (buf) - buffer_offset, omx_buffer->nAllocLen - omx_buffer->nOffset); memcpy (omx_buffer->pBuffer + omx_buffer->nOffset, GST_BUFFER_DATA (buf) + buffer_offset, omx_buffer->nFilledLen); } if (self->use_timestamps) { GstClockTime timestamp_offset = 0; if (buffer_offset && GST_BUFFER_DURATION (buf) != GST_CLOCK_TIME_NONE) { timestamp_offset = gst_util_uint64_scale_int (buffer_offset, GST_BUFFER_DURATION (buf), GST_BUFFER_SIZE (buf)); } omx_buffer->nTimeStamp = gst_util_uint64_scale_int (GST_BUFFER_TIMESTAMP (buf) + timestamp_offset, OMX_TICKS_PER_SECOND, GST_SECOND); } buffer_offset += omx_buffer->nFilledLen; #ifdef ANDROID omx_buffer->nFlags |= OMX_BUFFERFLAG_ENDOFFRAME; log_buffer (self, omx_buffer); #endif GST_LOG_OBJECT (self, "release_buffer"); /** @todo untaint buffer */ g_omx_port_release_buffer (in_port, omx_buffer); } else { GST_WARNING_OBJECT (self, "null buffer"); ret = GST_FLOW_WRONG_STATE; goto out_flushing; } } } else { GST_WARNING_OBJECT (self, "done"); ret = GST_FLOW_UNEXPECTED; } if (!self->share_input_buffer) { gst_buffer_unref (buf); } leave: GST_LOG_OBJECT (self, "end"); return ret; /* special conditions */ out_flushing: { const gchar *error_msg = NULL; if (gomx->omx_error) { error_msg = "Error from OpenMAX component"; } else if (gomx->omx_state != OMX_StateExecuting && gomx->omx_state != OMX_StatePause) { error_msg = "OpenMAX component in wrong state"; } if (error_msg) { GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL), ("%s", error_msg)); ret = GST_FLOW_ERROR; } gst_buffer_unref (buf); goto leave; } }
static void gst_vp8_enc_set_stream_info (GstVPXEnc * enc, GstCaps * caps, GstVideoInfo * info) { GstStructure *s; GstVideoEncoder *video_encoder; GstBuffer *stream_hdr, *vorbiscomment; const GstTagList *iface_tags; GValue array = { 0, }; GValue value = { 0, }; guint8 *data = NULL; GstMapInfo map; video_encoder = GST_VIDEO_ENCODER (enc); s = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Create Ogg stream-info */ stream_hdr = gst_buffer_new_and_alloc (26); gst_buffer_map (stream_hdr, &map, GST_MAP_WRITE); data = map.data; GST_WRITE_UINT8 (data, 0x4F); GST_WRITE_UINT32_BE (data + 1, 0x56503830); /* "VP80" */ GST_WRITE_UINT8 (data + 5, 0x01); /* stream info header */ GST_WRITE_UINT8 (data + 6, 1); /* Major version 1 */ GST_WRITE_UINT8 (data + 7, 0); /* Minor version 0 */ GST_WRITE_UINT16_BE (data + 8, GST_VIDEO_INFO_WIDTH (info)); GST_WRITE_UINT16_BE (data + 10, GST_VIDEO_INFO_HEIGHT (info)); GST_WRITE_UINT24_BE (data + 12, GST_VIDEO_INFO_PAR_N (info)); GST_WRITE_UINT24_BE (data + 15, GST_VIDEO_INFO_PAR_D (info)); GST_WRITE_UINT32_BE (data + 18, GST_VIDEO_INFO_FPS_N (info)); GST_WRITE_UINT32_BE (data + 22, GST_VIDEO_INFO_FPS_D (info)); gst_buffer_unmap (stream_hdr, &map); GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_HEADER); gst_value_set_buffer (&value, stream_hdr); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (stream_hdr); iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (video_encoder)); if (iface_tags) { vorbiscomment = gst_tag_list_to_vorbiscomment_buffer (iface_tags, (const guint8 *) "OVP80\2 ", 7, "Encoded with GStreamer vp8enc " PACKAGE_VERSION); GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_HEADER); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, vorbiscomment); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (vorbiscomment); } gst_structure_set_value (s, "streamheader", &array); g_value_unset (&array); }
static void output_loop (gpointer data) { GstPad *pad; GOmxCore *gomx; GOmxPort *out_port; GstOmxBaseFilter2 *self; GstFlowReturn ret = GST_FLOW_OK; GstOmxBaseFilter2Class *bclass; pad = data; self = GST_OMX_BASE_FILTER2 (gst_pad_get_parent (pad)); gomx = self->gomx; bclass = GST_OMX_BASE_FILTER2_GET_CLASS (self); GST_LOG_OBJECT (self, "begin"); if (!self->ready) { g_error ("not ready"); return; } out_port = (GOmxPort *)gst_pad_get_element_private(pad); if (G_LIKELY (out_port->enabled)) { gpointer obj = g_omx_port_recv (out_port); if (G_UNLIKELY (!obj)) { GST_WARNING_OBJECT (self, "null buffer: leaving"); ret = GST_FLOW_WRONG_STATE; goto leave; } if (G_LIKELY (GST_IS_BUFFER (obj))) { if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (obj, GST_BUFFER_FLAG_IN_CAPS))) { GstCaps *caps = NULL; GstStructure *structure; GValue value = { 0 }; caps = gst_pad_get_negotiated_caps (pad); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, obj); gst_buffer_unref (obj); gst_structure_set_value (structure, "codec_data", &value); g_value_unset (&value); gst_pad_set_caps (pad, caps); } else { GstBuffer *buf = GST_BUFFER (obj); ret = bclass->push_buffer (self, buf); GST_DEBUG_OBJECT (self, "ret=%s", gst_flow_get_name (ret)); // HACK!! Dont care if one of the output pads are not connected ret = GST_FLOW_OK; } } else if (GST_IS_EVENT (obj)) { GST_DEBUG_OBJECT (self, "got eos"); gst_pad_push_event (pad, obj); ret = GST_FLOW_UNEXPECTED; goto leave; } } leave: self->last_pad_push_return = ret; if (gomx->omx_error != OMX_ErrorNone) { GST_DEBUG_OBJECT (self, "omx_error=%s", g_omx_error_to_str (gomx->omx_error)); ret = GST_FLOW_ERROR; } if (ret != GST_FLOW_OK) { GST_INFO_OBJECT (self, "pause task, reason: %s", gst_flow_get_name (ret)); gst_pad_pause_task (pad); } GST_LOG_OBJECT (self, "end"); gst_object_unref (self); }
static GstCaps * gst_vp8_enc_get_caps (GstBaseVideoEncoder * base_video_encoder) { GstCaps *caps; const GstVideoState *state; GstTagList *tags = NULL; const GstTagList *iface_tags; GstBuffer *stream_hdr, *vorbiscomment; guint8 *data; GstStructure *s; GValue array = { 0 }; GValue value = { 0 }; state = gst_base_video_encoder_get_state (base_video_encoder); caps = gst_caps_new_simple ("video/x-vp8", "width", G_TYPE_INT, state->width, "height", G_TYPE_INT, state->height, "framerate", GST_TYPE_FRACTION, state->fps_n, state->fps_d, "pixel-aspect-ratio", GST_TYPE_FRACTION, state->par_n, state->par_d, NULL); s = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Create Ogg stream-info */ stream_hdr = gst_buffer_new_and_alloc (26); data = GST_BUFFER_DATA (stream_hdr); GST_WRITE_UINT8 (data, 0x4F); GST_WRITE_UINT32_BE (data + 1, 0x56503830); /* "VP80" */ GST_WRITE_UINT8 (data + 5, 0x01); /* stream info header */ GST_WRITE_UINT8 (data + 6, 1); /* Major version 1 */ GST_WRITE_UINT8 (data + 7, 0); /* Minor version 0 */ GST_WRITE_UINT16_BE (data + 8, state->width); GST_WRITE_UINT16_BE (data + 10, state->height); GST_WRITE_UINT24_BE (data + 12, state->par_n); GST_WRITE_UINT24_BE (data + 15, state->par_d); GST_WRITE_UINT32_BE (data + 18, state->fps_n); GST_WRITE_UINT32_BE (data + 22, state->fps_d); GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_IN_CAPS); gst_value_set_buffer (&value, stream_hdr); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (stream_hdr); iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (base_video_encoder)); if (iface_tags) { vorbiscomment = gst_tag_list_to_vorbiscomment_buffer ((iface_tags) ? iface_tags : tags, (const guint8 *) "OVP80\2 ", 7, "Encoded with GStreamer vp8enc " PACKAGE_VERSION); GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_IN_CAPS); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, vorbiscomment); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (vorbiscomment); } gst_structure_set_value (s, "streamheader", &array); g_value_unset (&array); return caps; }
static void output_loop (gpointer data) { GstPad *pad; GOmxCore *gomx; GOmxPort *out_port; GstOmxBaseFilter *self; GstFlowReturn ret = GST_FLOW_OK; pad = data; self = GST_OMX_BASE_FILTER (gst_pad_get_parent (pad)); gomx = self->gomx; GST_LOG_OBJECT (self, "begin"); if (!self->ready) { g_error ("not ready"); return; } out_port = self->out_port; if (G_LIKELY (out_port->enabled)) { OMX_BUFFERHEADERTYPE *omx_buffer = NULL; GST_LOG_OBJECT (self, "request buffer"); omx_buffer = g_omx_port_request_buffer (out_port); GST_LOG_OBJECT (self, "omx_buffer: %p", omx_buffer); if (G_UNLIKELY (!omx_buffer)) { GST_WARNING_OBJECT (self, "null buffer: leaving"); ret = GST_FLOW_WRONG_STATE; goto leave; } GST_DEBUG_OBJECT (self, "omx_buffer: size=%lu, len=%lu, flags=%lu, offset=%lu, timestamp=%lld", omx_buffer->nAllocLen, omx_buffer->nFilledLen, omx_buffer->nFlags, omx_buffer->nOffset, omx_buffer->nTimeStamp); if (G_LIKELY (omx_buffer->nFilledLen > 0)) { GstBuffer *buf; #if 1 /** @todo remove this check */ if (G_LIKELY (self->in_port->enabled)) { GstCaps *caps = NULL; caps = gst_pad_get_negotiated_caps (self->srcpad); if (!caps) { /** @todo We shouldn't be doing this. */ GST_WARNING_OBJECT (self, "faking settings changed notification"); if (gomx->settings_changed_cb) gomx->settings_changed_cb (gomx); } else { GST_LOG_OBJECT (self, "caps already fixed: %" GST_PTR_FORMAT, caps); gst_caps_unref (caps); } } #endif /* buf is always null when the output buffer pointer isn't shared. */ buf = omx_buffer->pAppPrivate; /** @todo we need to move all the caps handling to one single * place, in the output loop probably. */ if (G_UNLIKELY (omx_buffer->nFlags & 0x80)) { GstCaps *caps = NULL; GstStructure *structure; GValue value = { 0 }; caps = gst_pad_get_negotiated_caps (self->srcpad); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); g_value_init (&value, GST_TYPE_BUFFER); buf = gst_buffer_new_and_alloc (omx_buffer->nFilledLen); memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); gst_value_set_buffer (&value, buf); gst_buffer_unref (buf); gst_structure_set_value (structure, "codec_data", &value); g_value_unset (&value); gst_pad_set_caps (self->srcpad, caps); } else if (buf && !(omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_BUFFER_SIZE (buf) = omx_buffer->nFilledLen; if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } omx_buffer->pAppPrivate = NULL; omx_buffer->pBuffer = NULL; ret = push_buffer (self, buf); gst_buffer_unref (buf); } else { /* This is only meant for the first OpenMAX buffers, * which need to be pre-allocated. */ /* Also for the very last one. */ ret = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nFilledLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (buf)) { memcpy (GST_BUFFER_DATA (buf), omx_buffer->pBuffer + omx_buffer->nOffset, omx_buffer->nFilledLen); if (self->use_timestamps) { GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (omx_buffer->nTimeStamp, GST_SECOND, OMX_TICKS_PER_SECOND); } if (self->share_output_buffer) { GST_WARNING_OBJECT (self, "couldn't zero-copy"); /* If pAppPrivate is NULL, it means it was a dummy * allocation, free it. */ if (!omx_buffer->pAppPrivate) { g_free (omx_buffer->pBuffer); omx_buffer->pBuffer = NULL; } } ret = push_buffer (self, buf); } else { GST_WARNING_OBJECT (self, "couldn't allocate buffer of size %d", omx_buffer->nFilledLen); } } } else { GST_WARNING_OBJECT (self, "empty buffer"); } if (G_UNLIKELY (omx_buffer->nFlags & OMX_BUFFERFLAG_EOS)) { GST_DEBUG_OBJECT (self, "got eos"); gst_pad_push_event (self->srcpad, gst_event_new_eos ()); ret = GST_FLOW_UNEXPECTED; goto leave; } if (self->share_output_buffer && !omx_buffer->pBuffer && omx_buffer->nOffset == 0) { GstBuffer *buf; GstFlowReturn result; GST_LOG_OBJECT (self, "allocate buffer"); result = gst_pad_alloc_buffer_and_set_caps (self->srcpad, GST_BUFFER_OFFSET_NONE, omx_buffer->nAllocLen, GST_PAD_CAPS (self->srcpad), &buf); if (G_LIKELY (result == GST_FLOW_OK)) { gst_buffer_ref (buf); omx_buffer->pAppPrivate = buf; omx_buffer->pBuffer = GST_BUFFER_DATA (buf); omx_buffer->nAllocLen = GST_BUFFER_SIZE (buf); } else { GST_WARNING_OBJECT (self, "could not pad allocate buffer, using malloc"); omx_buffer->pBuffer = g_malloc (omx_buffer->nAllocLen); } } if (self->share_output_buffer && !omx_buffer->pBuffer) { GST_ERROR_OBJECT (self, "no input buffer to share"); } omx_buffer->nFilledLen = 0; GST_LOG_OBJECT (self, "release_buffer"); g_omx_port_release_buffer (out_port, omx_buffer); } leave: self->last_pad_push_return = ret; if (gomx->omx_error != OMX_ErrorNone) ret = GST_FLOW_ERROR; if (ret != GST_FLOW_OK) { GST_INFO_OBJECT (self, "pause task, reason: %s", gst_flow_get_name (ret)); gst_pad_pause_task (self->srcpad); } GST_LOG_OBJECT (self, "end"); gst_object_unref (self); }
static int gst_ffmpegdata_write (URLContext * h, unsigned char *buf, int size) { GstProtocolInfo *info; GstBuffer *outbuf; GST_DEBUG ("Writing %d bytes", size); info = (GstProtocolInfo *) h->priv_data; g_return_val_if_fail (h->flags != URL_RDONLY, -EIO); /* * WHISPERCAST BEGIN: set the "streamheader" value on the source pad caps, * so we are able to use ffmpegmux with fdsink/multifdsink/etc... */ if (info->set_streamheader) { GstCaps *caps; GstStructure *structure; GValue array = { 0 }; GstBuffer *copy; GValue value = { 0 }; caps = gst_pad_get_caps(info->pad); caps = gst_caps_make_writable (caps); structure = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); copy = gst_buffer_new_and_alloc (size); memcpy (GST_BUFFER_DATA (copy), buf, size); gst_value_set_buffer (&value, copy); gst_buffer_unref (copy); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_structure_set_value (structure, "streamheader", &array); g_value_unset (&array); gst_pad_set_caps(info->pad, caps); gst_caps_unref(caps); } /* WHISPERCAST END */ /* create buffer and push data further */ if (gst_pad_alloc_buffer_and_set_caps (info->pad, info->offset, size, GST_PAD_CAPS (info->pad), &outbuf) != GST_FLOW_OK) return 0; /* * WHISPERCAST BEGIN: mark the buffer as added to the source pad's caps. */ if (info->set_streamheader) { GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_IN_CAPS); info->set_streamheader = 0; } /* WHISPERCAST END */ memcpy (GST_BUFFER_DATA (outbuf), buf, size); if (gst_pad_push (info->pad, outbuf) != GST_FLOW_OK) return 0; info->offset += size; return size; }