/** * gst_adapter_take_buffer: * @adapter: a #GstAdapter * @nbytes: the number of bytes to take * * Returns a #GstBuffer containing the first @nbytes bytes of the * @adapter. The returned bytes will be flushed from the adapter. * This function is potentially more performant than gst_adapter_take() * since it can reuse the memory in pushed buffers by subbuffering * or merging. * * Note that no assumptions should be made as to whether certain buffer * flags such as the DISCONT flag are set on the returned buffer, or not. * The caller needs to explicitly set or unset flags that should be set or * unset. * * Caller owns a reference to the returned buffer. gst_buffer_unref() after * usage. * * Free-function: gst_buffer_unref * * Returns: (transfer full): a #GstBuffer containing the first @nbytes of * the adapter, or #NULL if @nbytes bytes are not available. * gst_buffer_unref() when no longer needed. */ GstBuffer * gst_adapter_take_buffer (GstAdapter * adapter, gsize nbytes) { GstBuffer *buffer; GstBuffer *cur; gsize hsize, skip; guint8 *data; g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (nbytes > 0, NULL); GST_LOG_OBJECT (adapter, "taking buffer of %" G_GSIZE_FORMAT " bytes", nbytes); /* we don't have enough data, return NULL. This is unlikely * as one usually does an _available() first instead of grabbing a * random size. */ if (G_UNLIKELY (nbytes > adapter->size)) return NULL; cur = adapter->buflist->data; skip = adapter->skip; hsize = gst_buffer_get_size (cur); /* our head buffer has enough data left, return it */ if (skip == 0 && hsize == nbytes) { GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" " as head buffer", nbytes); buffer = gst_buffer_ref (cur); goto done; } else if (hsize >= nbytes + skip) { GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" " via region copy", nbytes); buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, nbytes); goto done; } #if 0 if (gst_adapter_try_to_merge_up (adapter, nbytes)) { /* Merged something, let's try again for sub-buffering */ cur = adapter->buflist->data; skip = adapter->skip; if (gst_buffer_get_size (cur) >= nbytes + skip) { GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" " via sub-buffer", nbytes); buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, nbytes); goto done; } } #endif data = gst_adapter_take_internal (adapter, nbytes); buffer = gst_buffer_new_wrapped (data, nbytes); done: gst_adapter_flush_unchecked (adapter, nbytes); return buffer; }
static void udp_streaming (Encoder *encoder, GstBuffer *buffer) { gsize buffer_size; gssize offset; GstFlowReturn ret; offset = 0; buffer_size = gst_buffer_get_size (buffer); while (buffer_size != 0) { if ((encoder->cache_size == 0) && (buffer_size < 1316)) { encoder->cache_7x188 = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, offset, buffer_size); encoder->cache_size = buffer_size; break; } else if (encoder->cache_size == 0) { /* buffer_size >= 1316 */ encoder->cache_7x188 = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, offset, 1316); offset += 1316; buffer_size -= 1316; } else if (encoder->cache_size + buffer_size >= 1316) { gsize size; gst_buffer_ref (buffer); size = 1316 - encoder->cache_size; encoder->cache_7x188 = gst_buffer_append_region (encoder->cache_7x188, buffer, offset, size); offset += 1316 - encoder->cache_size; buffer_size -= 1316 - encoder->cache_size; encoder->cache_size = 0; } else { /* encoder->cache_size + buffer_size < 1316 */ gst_buffer_ref (buffer); encoder->cache_7x188 = gst_buffer_append_region (encoder->cache_7x188, buffer, offset, buffer_size); encoder->cache_size += buffer_size; break; } ret = gst_app_src_push_buffer ((GstAppSrc *)encoder->appsrc, encoder->cache_7x188); if (ret != GST_FLOW_OK) { GST_ERROR ("appsrc push buffer failure, return %s.", gst_flow_get_name (ret)); gst_buffer_unref (encoder->cache_7x188); } encoder->cache_size = 0; } }
static gboolean gst_raw_video_parse_process (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config, GstBuffer * in_data, G_GNUC_UNUSED gsize total_num_in_bytes, G_GNUC_UNUSED gsize num_valid_in_bytes, GstBuffer ** processed_data) { GstRawVideoParse *raw_video_parse = GST_RAW_VIDEO_PARSE (raw_base_parse); GstRawVideoParseConfig *config_ptr = gst_raw_video_parse_get_config_ptr (raw_video_parse, config); guint frame_flags = 0; GstVideoInfo *video_info = &(config_ptr->info); GstVideoMeta *videometa; GstBuffer *out_data; /* In case of extra padding bytes, get a subbuffer without the padding bytes. * Otherwise, just add the video meta. */ if (GST_VIDEO_INFO_SIZE (video_info) < config_ptr->frame_stride) { *processed_data = out_data = gst_buffer_copy_region (in_data, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_MEMORY, 0, GST_VIDEO_INFO_SIZE (video_info)); } else { out_data = in_data; *processed_data = NULL; } if (config_ptr->interlaced) { GST_BUFFER_FLAG_SET (out_data, GST_VIDEO_BUFFER_FLAG_INTERLACED); frame_flags |= GST_VIDEO_FRAME_FLAG_INTERLACED; if (config_ptr->top_field_first) { GST_BUFFER_FLAG_SET (out_data, GST_VIDEO_BUFFER_FLAG_TFF); frame_flags |= GST_VIDEO_FRAME_FLAG_TFF; } else GST_BUFFER_FLAG_UNSET (out_data, GST_VIDEO_BUFFER_FLAG_TFF); } /* Remove any existing videometa - it will be replaced by the new videometa * from here */ while ((videometa = gst_buffer_get_video_meta (out_data))) { GST_LOG_OBJECT (raw_base_parse, "removing existing videometa from buffer"); gst_buffer_remove_meta (out_data, (GstMeta *) videometa); } gst_buffer_add_video_meta_full (out_data, frame_flags, config_ptr->format, config_ptr->width, config_ptr->height, GST_VIDEO_INFO_N_PLANES (video_info), config_ptr->plane_offsets, config_ptr->plane_strides); return TRUE; }
/** * gst_adapter_get_buffer_fast: * @adapter: a #GstAdapter * @nbytes: the number of bytes to get * * Returns a #GstBuffer containing the first @nbytes of the @adapter, but * does not flush them from the adapter. See gst_adapter_take_buffer_fast() * for details. * * Caller owns a reference to the returned buffer. gst_buffer_unref() after * usage. * * Free-function: gst_buffer_unref * * Returns: (transfer full) (nullable): a #GstBuffer containing the first * @nbytes of the adapter, or %NULL if @nbytes bytes are not available. * gst_buffer_unref() when no longer needed. * * Since: 1.6 */ GstBuffer * gst_adapter_get_buffer_fast (GstAdapter * adapter, gsize nbytes) { GstBuffer *buffer = NULL; GstBuffer *cur; GSList *item; gsize skip; gsize left = nbytes; g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (nbytes > 0, NULL); GST_LOG_OBJECT (adapter, "getting buffer of %" G_GSIZE_FORMAT " bytes", nbytes); /* we don't have enough data, return NULL. This is unlikely * as one usually does an _available() first instead of grabbing a * random size. */ if (G_UNLIKELY (nbytes > adapter->size)) return NULL; skip = adapter->skip; cur = adapter->buflist->data; if (skip == 0 && gst_buffer_get_size (cur) == nbytes) { GST_LOG_OBJECT (adapter, "providing buffer of %" G_GSIZE_FORMAT " bytes" " as head buffer", nbytes); buffer = gst_buffer_ref (cur); goto done; } for (item = adapter->buflist; item && left > 0; item = item->next) { gsize size, cur_size; cur = item->data; cur_size = gst_buffer_get_size (cur); size = MIN (cur_size - skip, left); GST_LOG_OBJECT (adapter, "appending %" G_GSIZE_FORMAT " bytes" " via region copy", size); if (buffer) gst_buffer_copy_into (buffer, cur, GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_META, skip, size); else buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, size); skip = 0; left -= size; } done: return buffer; }
/** * gst_adapter_get_buffer_list: * @adapter: a #GstAdapter * @nbytes: the number of bytes to get * * Returns a #GstBufferList of buffers containing the first @nbytes bytes of * the @adapter but does not flush them from the adapter. See * gst_adapter_take_buffer_list() for details. * * Caller owns the returned list. Call gst_buffer_list_unref() to free * the list after usage. * * Returns: (transfer full) (nullable): a #GstBufferList of buffers containing * the first @nbytes of the adapter, or %NULL if @nbytes bytes are not * available * * Since: 1.6 */ GstBufferList * gst_adapter_get_buffer_list (GstAdapter * adapter, gsize nbytes) { GstBufferList *buffer_list; GstBuffer *cur, *buffer; gsize hsize, skip, cur_size; guint n_bufs; GSList *g = NULL; g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); if (nbytes > adapter->size) return NULL; GST_LOG_OBJECT (adapter, "getting %" G_GSIZE_FORMAT " bytes", nbytes); /* try to create buffer list with sufficient size, so no resize is done later */ if (adapter->count < 64) n_bufs = adapter->count; else n_bufs = (adapter->count * nbytes * 1.2 / adapter->size) + 1; buffer_list = gst_buffer_list_new_sized (n_bufs); g = adapter->buflist; skip = adapter->skip; while (nbytes > 0) { cur = g->data; cur_size = gst_buffer_get_size (cur); hsize = MIN (nbytes, cur_size - skip); if (skip == 0 && cur_size == hsize) { GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes", hsize); buffer = gst_buffer_ref (cur); } else { GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes" " via region copy", hsize); buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, hsize); } gst_buffer_list_add (buffer_list, buffer); nbytes -= hsize; skip = 0; g = g_slist_next (g); } return buffer_list; }
static GstBuffer * gst_fake_src_create_buffer (GstFakeSrc * src, gsize * bufsize) { GstBuffer *buf; gsize size = gst_fake_src_get_size (src); gboolean dump = src->dump; GstMapInfo info; *bufsize = size; switch (src->data) { case FAKE_SRC_DATA_ALLOCATE: buf = gst_fake_src_alloc_buffer (src, size); break; case FAKE_SRC_DATA_SUBBUFFER: /* see if we have a parent to subbuffer */ if (!src->parent) { gst_fake_src_alloc_parent (src); g_assert (src->parent); } /* see if it's large enough */ if ((src->parentsize - src->parentoffset) >= size) { buf = gst_buffer_copy_region (src->parent, GST_BUFFER_COPY_ALL, src->parentoffset, size); src->parentoffset += size; } else { /* the parent is useless now */ gst_buffer_unref (src->parent); src->parent = NULL; /* try again (this will allocate a new parent) */ return gst_fake_src_create_buffer (src, bufsize); } gst_buffer_map (buf, &info, GST_MAP_WRITE); gst_fake_src_prepare_buffer (src, info.data, info.size); gst_buffer_unmap (buf, &info); break; default: g_warning ("fakesrc: dunno how to allocate buffers !"); buf = gst_buffer_new (); break; } if (dump) { gst_buffer_map (buf, &info, GST_MAP_READ); gst_util_dump_mem (info.data, info.size); gst_buffer_unmap (buf, &info); } return buf; }
GstBuffer * gst_rdt_packet_to_buffer (GstRDTPacket * packet) { GstBuffer *result; g_return_val_if_fail (packet != NULL, NULL); g_return_val_if_fail (packet->type != GST_RDT_TYPE_INVALID, NULL); result = gst_buffer_copy_region (packet->buffer, GST_BUFFER_COPY_ALL, packet->offset, packet->length); /* timestamp applies to all packets in this buffer */ GST_BUFFER_TIMESTAMP (result) = GST_BUFFER_TIMESTAMP (packet->buffer); return result; }
static GstFlowReturn gst_data_uri_src_create (GstBaseSrc * basesrc, guint64 offset, guint size, GstBuffer ** buf) { GstDataURISrc *src = GST_DATA_URI_SRC (basesrc); GstFlowReturn ret; GST_OBJECT_LOCK (src); if (!src->buffer) goto no_buffer; /* This is only correct because GstBaseSrc already clips size for us to be no * larger than the max. available size if a segment at the end is requested */ if (offset + size > gst_buffer_get_size (src->buffer)) { ret = GST_FLOW_EOS; } else if (*buf != NULL) { GstMapInfo src_info; GstMapInfo dest_info; gsize fill_size; gst_buffer_map (src->buffer, &src_info, GST_MAP_READ); gst_buffer_map (*buf, &dest_info, GST_MAP_WRITE); fill_size = gst_buffer_fill (*buf, 0, src_info.data + offset, size); gst_buffer_unmap (*buf, &dest_info); gst_buffer_unmap (src->buffer, &src_info); gst_buffer_set_size (*buf, fill_size); ret = GST_FLOW_OK; } else { *buf = gst_buffer_copy_region (src->buffer, GST_BUFFER_COPY_ALL, offset, size); ret = GST_FLOW_OK; } GST_OBJECT_UNLOCK (src); return ret; /* ERRORS */ no_buffer: { GST_OBJECT_UNLOCK (src); GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, (NULL), (NULL)); return GST_FLOW_NOT_NEGOTIATED; } }
static gboolean gst_raw_audio_parse_process (GstRawBaseParse * raw_base_parse, GstRawBaseParseConfig config, GstBuffer * in_data, gsize total_num_in_bytes, gsize num_valid_in_bytes, GstBuffer ** processed_data) { GstRawAudioParse *raw_audio_parse = GST_RAW_AUDIO_PARSE (raw_base_parse); GstRawAudioParseConfig *config_ptr = gst_raw_audio_parse_get_config_ptr (raw_audio_parse, config); if ((config_ptr->format == GST_RAW_AUDIO_PARSE_FORMAT_PCM) && config_ptr->needs_channel_reordering) { /* Need to reorder samples, since they are in an invalid * channel order. */ GstBuffer *outbuf; GST_LOG_OBJECT (raw_audio_parse, "using %" G_GSIZE_FORMAT " bytes out of the %" G_GSIZE_FORMAT " bytes from the input buffer with reordering", num_valid_in_bytes, total_num_in_bytes); outbuf = gst_buffer_copy_region (in_data, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY, 0, num_valid_in_bytes); gst_audio_buffer_reorder_channels (outbuf, config_ptr->pcm_format, config_ptr->num_channels, config_ptr->channel_positions, config_ptr->reordered_channel_positions); *processed_data = outbuf; } else { /* Nothing needs to be done with the sample data. * Instruct the baseparse class to just take out_size bytes * from the input buffer */ GST_LOG_OBJECT (raw_audio_parse, "using %" G_GSIZE_FORMAT " bytes out of the %" G_GSIZE_FORMAT " bytes from the input buffer without reordering", num_valid_in_bytes, total_num_in_bytes); *processed_data = NULL; } return TRUE; }
/** * gst_adapter_get_list: * @adapter: a #GstAdapter * @nbytes: the number of bytes to get * * Returns a #GList of buffers containing the first @nbytes bytes of the * @adapter, but does not flush them from the adapter. See * gst_adapter_take_list() for details. * * Caller owns returned list and contained buffers. gst_buffer_unref() each * buffer in the list before freeing the list after usage. * * Returns: (element-type Gst.Buffer) (transfer full) (nullable): a #GList of * buffers containing the first @nbytes of the adapter, or %NULL if @nbytes * bytes are not available * * Since: 1.6 */ GList * gst_adapter_get_list (GstAdapter * adapter, gsize nbytes) { GQueue queue = G_QUEUE_INIT; GstBuffer *cur, *buffer; gsize hsize, skip, cur_size; GSList *g = NULL; g_return_val_if_fail (GST_IS_ADAPTER (adapter), NULL); g_return_val_if_fail (nbytes <= adapter->size, NULL); GST_LOG_OBJECT (adapter, "getting %" G_GSIZE_FORMAT " bytes", nbytes); g = adapter->buflist; skip = adapter->skip; while (nbytes > 0) { cur = g->data; cur_size = gst_buffer_get_size (cur); hsize = MIN (nbytes, cur_size - skip); if (skip == 0 && cur_size == hsize) { GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes", hsize); buffer = gst_buffer_ref (cur); } else { GST_LOG_OBJECT (adapter, "inserting a buffer of %" G_GSIZE_FORMAT " bytes" " via region copy", hsize); buffer = gst_buffer_copy_region (cur, GST_BUFFER_COPY_ALL, skip, hsize); } g_queue_push_tail (&queue, buffer); nbytes -= hsize; skip = 0; g = g_slist_next (g); } return queue.head; }
/** * gst_audio_buffer_clip: * @buffer: (transfer full): The buffer to clip. * @segment: Segment in %GST_FORMAT_TIME or %GST_FORMAT_DEFAULT to which * the buffer should be clipped. * @rate: sample rate. * @bpf: size of one audio frame in bytes. This is the size of one sample * * channels. * * Clip the buffer to the given %GstSegment. * * After calling this function the caller does not own a reference to * @buffer anymore. * * Returns: (transfer full): %NULL if the buffer is completely outside the configured segment, * otherwise the clipped buffer is returned. * * If the buffer has no timestamp, it is assumed to be inside the segment and * is not clipped */ GstBuffer * gst_audio_buffer_clip (GstBuffer * buffer, GstSegment * segment, gint rate, gint bpf) { GstBuffer *ret; GstClockTime timestamp = GST_CLOCK_TIME_NONE, duration = GST_CLOCK_TIME_NONE; guint64 offset = GST_BUFFER_OFFSET_NONE, offset_end = GST_BUFFER_OFFSET_NONE; gsize trim, size, osize; gboolean change_duration = TRUE, change_offset = TRUE, change_offset_end = TRUE; g_return_val_if_fail (segment->format == GST_FORMAT_TIME || segment->format == GST_FORMAT_DEFAULT, buffer); g_return_val_if_fail (GST_IS_BUFFER (buffer), NULL); if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) /* No timestamp - assume the buffer is completely in the segment */ return buffer; /* Get copies of the buffer metadata to change later. * Calculate the missing values for the calculations, * they won't be changed later though. */ trim = 0; osize = size = gst_buffer_get_size (buffer); /* no data, nothing to clip */ if (!size) return buffer; timestamp = GST_BUFFER_TIMESTAMP (buffer); GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); if (GST_BUFFER_DURATION_IS_VALID (buffer)) { duration = GST_BUFFER_DURATION (buffer); } else { change_duration = FALSE; duration = gst_util_uint64_scale (size / bpf, GST_SECOND, rate); } if (GST_BUFFER_OFFSET_IS_VALID (buffer)) { offset = GST_BUFFER_OFFSET (buffer); } else { change_offset = FALSE; offset = 0; } if (GST_BUFFER_OFFSET_END_IS_VALID (buffer)) { offset_end = GST_BUFFER_OFFSET_END (buffer); } else { change_offset_end = FALSE; offset_end = offset + size / bpf; } if (segment->format == GST_FORMAT_TIME) { /* Handle clipping for GST_FORMAT_TIME */ guint64 start, stop, cstart, cstop, diff; start = timestamp; stop = timestamp + duration; if (gst_segment_clip (segment, GST_FORMAT_TIME, start, stop, &cstart, &cstop)) { diff = cstart - start; if (diff > 0) { timestamp = cstart; if (change_duration) duration -= diff; diff = gst_util_uint64_scale (diff, rate, GST_SECOND); if (change_offset) offset += diff; trim += diff * bpf; size -= diff * bpf; } diff = stop - cstop; if (diff > 0) { /* duration is always valid if stop is valid */ duration -= diff; diff = gst_util_uint64_scale (diff, rate, GST_SECOND); if (change_offset_end) offset_end -= diff; size -= diff * bpf; } } else { gst_buffer_unref (buffer); return NULL; } } else { /* Handle clipping for GST_FORMAT_DEFAULT */ guint64 start, stop, cstart, cstop, diff; g_return_val_if_fail (GST_BUFFER_OFFSET_IS_VALID (buffer), buffer); start = offset; stop = offset_end; if (gst_segment_clip (segment, GST_FORMAT_DEFAULT, start, stop, &cstart, &cstop)) { diff = cstart - start; if (diff > 0) { offset = cstart; timestamp = gst_util_uint64_scale (cstart, GST_SECOND, rate); if (change_duration) duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); trim += diff * bpf; size -= diff * bpf; } diff = stop - cstop; if (diff > 0) { offset_end = cstop; if (change_duration) duration -= gst_util_uint64_scale (diff, GST_SECOND, rate); size -= diff * bpf; } } else { gst_buffer_unref (buffer); return NULL; } } if (trim == 0 && size == osize) { ret = buffer; if (GST_BUFFER_TIMESTAMP (ret) != timestamp) { ret = gst_buffer_make_writable (ret); GST_BUFFER_TIMESTAMP (ret) = timestamp; } if (GST_BUFFER_DURATION (ret) != duration) { ret = gst_buffer_make_writable (ret); GST_BUFFER_DURATION (ret) = duration; } } else { /* Get a writable buffer and apply all changes */ GST_DEBUG ("trim %" G_GSIZE_FORMAT " size %" G_GSIZE_FORMAT, trim, size); ret = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, trim, size); gst_buffer_unref (buffer); GST_DEBUG ("timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); GST_BUFFER_TIMESTAMP (ret) = timestamp; if (change_duration) GST_BUFFER_DURATION (ret) = duration; if (change_offset) GST_BUFFER_OFFSET (ret) = offset; if (change_offset_end) GST_BUFFER_OFFSET_END (ret) = offset_end; } return ret; }
static GstFlowReturn gst_mpeg4p2unpack_chain(GstPad *pad, GstObject *parent, GstBuffer *buffer) { guint8 *data; gsize data_len; GstMapInfo buffermap; GstFlowReturn ret = GST_FLOW_OK; GstMpeg4P2Unpack *self = GST_MPEG4P2UNPACK(GST_PAD_PARENT(pad)); if (self->buffer_duration == GST_CLOCK_TIME_NONE) { if (!GST_BUFFER_DURATION_IS_VALID(buffer)) { GST_WARNING_OBJECT(self, "Cannot retrieve buffer duration, dropping"); gst_buffer_unref(buffer); return GST_FLOW_OK; } self->buffer_duration = GST_BUFFER_DURATION(buffer); } gst_buffer_map(buffer, &buffermap, GST_MAP_READ); data = buffermap.data; data_len = buffermap.size; int pos_p = -1, nb_vop = 0, pos_vop2 = -1; mpeg4p2_scan_buffer(data, data_len, &pos_p, &nb_vop, &pos_vop2); // GST_LOG_OBJECT(self, "pos_p=%d, num_vop=%d, pos_vop2=%d", pos_p, nb_vop, pos_vop2); /* if we don't have userdata we can unmap buffer */ if (pos_p < 0) { gst_buffer_unmap(buffer, &buffermap); data = NULL; } if (pos_vop2 >= 0) { if (self->b_frame) { GST_WARNING_OBJECT(self, "Missing one N-VOP packet, discarding one B-frame"); gst_buffer_unref(self->b_frame); self->b_frame = NULL; } // GST_LOG_OBJECT(self, "Storing B-Frame of packed PB-Frame"); self->b_frame = gst_buffer_copy_region(buffer, GST_BUFFER_COPY_ALL, pos_vop2, data_len - pos_vop2); GST_BUFFER_DTS(self->b_frame) = GST_BUFFER_DTS(buffer) + self->buffer_duration; } if (nb_vop > 2) { GST_WARNING_OBJECT(self, "Found %d VOP headers in one packet, only unpacking one.", nb_vop); } if (nb_vop == 1 && self->b_frame) { // GST_LOG_OBJECT(self, "Push previous B-Frame"); ret = gst_mpeg4p2unpack_handle_frame(self, self->b_frame); if (data_len <= MPEG4P2_MAX_NVOP_SIZE) { // GST_LOG_OBJECT(self, "Skipping N-VOP"); self->b_frame = NULL; gst_buffer_unref(buffer); } else { // GST_LOG_OBJECT(self, "Store B-Frame"); GST_BUFFER_DTS(buffer) = GST_BUFFER_DTS(self->b_frame) + self->buffer_duration; self->b_frame = buffer; } } else if (nb_vop >= 2) { // GST_LOG_OBJECT(self, "Push P-frame of packed PB-Frame"); GstBuffer *p_frame = gst_buffer_copy_region(buffer, GST_BUFFER_COPY_ALL, 0, pos_vop2); ret = gst_mpeg4p2unpack_handle_frame(self, p_frame); gst_buffer_unref(buffer); } else if (pos_p >= 0) { // GST_LOG_OBJECT(self, "Updating DivX userdata (replacing trailing 'p')"); gst_buffer_unmap(buffer, &buffermap); gst_buffer_map(buffer, &buffermap, GST_MAP_WRITE); data = buffermap.data; data[pos_p] = 'n'; gst_buffer_unmap(buffer, &buffermap); data = NULL; ret = gst_mpeg4p2unpack_handle_frame(self, buffer); } else { ret = gst_mpeg4p2unpack_handle_frame(self, buffer); } return ret; }
static gboolean theora_dec_set_format (GstVideoDecoder * bdec, GstVideoCodecState * state) { GstTheoraDec *dec; dec = GST_THEORA_DEC (bdec); /* Keep a copy of the input state */ if (dec->input_state) gst_video_codec_state_unref (dec->input_state); dec->input_state = gst_video_codec_state_ref (state); /* FIXME : Interesting, we always accept any kind of caps ? */ if (state->codec_data) { GstBuffer *buffer; GstMapInfo minfo; guint8 *data; guint size; guint offset; buffer = state->codec_data; gst_buffer_map (buffer, &minfo, GST_MAP_READ); offset = 0; size = minfo.size; data = (guint8 *) minfo.data; while (size > 2) { guint psize; GstBuffer *buf; psize = (data[0] << 8) | data[1]; /* skip header */ data += 2; size -= 2; offset += 2; /* make sure we don't read too much */ psize = MIN (psize, size); buf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, psize); /* first buffer is a discont buffer */ if (offset == 2) GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); /* now feed it to the decoder we can ignore the error */ theora_dec_decode_buffer (dec, buf, NULL); gst_buffer_unref (buf); /* skip the data */ size -= psize; data += psize; offset += psize; } gst_buffer_unmap (buffer, &minfo); } GST_DEBUG_OBJECT (dec, "Done"); return TRUE; }
static GstFlowReturn gst_pngenc_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstPngEnc *pngenc; gint row_index; png_byte **row_pointers; GstFlowReturn ret = GST_FLOW_OK; GstBuffer *encoded_buf = NULL; GstVideoFrame frame; pngenc = GST_PNGENC (parent); GST_DEBUG_OBJECT (pngenc, "BEGINNING"); if (G_UNLIKELY (pngenc->width <= 0 || pngenc->height <= 0)) { ret = GST_FLOW_NOT_NEGOTIATED; goto exit; } if (!gst_video_frame_map (&frame, &pngenc->info, buf, GST_MAP_READ)) { GST_ELEMENT_ERROR (pngenc, STREAM, FORMAT, (NULL), ("Failed to map video frame, caps problem?")); ret = GST_FLOW_ERROR; goto exit; } /* initialize png struct stuff */ pngenc->png_struct_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, user_error_fn, user_warning_fn); if (pngenc->png_struct_ptr == NULL) { GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL), ("Failed to initialize png structure")); ret = GST_FLOW_ERROR; goto done; } pngenc->png_info_ptr = png_create_info_struct (pngenc->png_struct_ptr); if (!pngenc->png_info_ptr) { png_destroy_write_struct (&(pngenc->png_struct_ptr), (png_infopp) NULL); GST_ELEMENT_ERROR (pngenc, LIBRARY, INIT, (NULL), ("Failed to initialize the png info structure")); ret = GST_FLOW_ERROR; goto done; } /* non-0 return is from a longjmp inside of libpng */ if (setjmp (png_jmpbuf (pngenc->png_struct_ptr)) != 0) { png_destroy_write_struct (&pngenc->png_struct_ptr, &pngenc->png_info_ptr); GST_ELEMENT_ERROR (pngenc, LIBRARY, FAILED, (NULL), ("returning from longjmp")); ret = GST_FLOW_ERROR; goto done; } png_set_filter (pngenc->png_struct_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE); png_set_compression_level (pngenc->png_struct_ptr, pngenc->compression_level); png_set_IHDR (pngenc->png_struct_ptr, pngenc->png_info_ptr, pngenc->width, pngenc->height, pngenc->depth, pngenc->png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_write_fn (pngenc->png_struct_ptr, pngenc, (png_rw_ptr) user_write_data, user_flush_data); row_pointers = g_new (png_byte *, pngenc->height); for (row_index = 0; row_index < pngenc->height; row_index++) { row_pointers[row_index] = GST_VIDEO_FRAME_COMP_DATA (&frame, 0) + (row_index * GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0)); } /* allocate the output buffer */ pngenc->buffer_out = gst_buffer_new_and_alloc (pngenc->height * pngenc->width); pngenc->written = 0; png_write_info (pngenc->png_struct_ptr, pngenc->png_info_ptr); png_write_image (pngenc->png_struct_ptr, row_pointers); png_write_end (pngenc->png_struct_ptr, NULL); g_free (row_pointers); GST_DEBUG_OBJECT (pngenc, "written %d", pngenc->written); encoded_buf = gst_buffer_copy_region (pngenc->buffer_out, GST_BUFFER_COPY_MEMORY, 0, pngenc->written); png_destroy_info_struct (pngenc->png_struct_ptr, &pngenc->png_info_ptr); png_destroy_write_struct (&pngenc->png_struct_ptr, (png_infopp) NULL); GST_BUFFER_TIMESTAMP (encoded_buf) = GST_BUFFER_TIMESTAMP (buf); GST_BUFFER_DURATION (encoded_buf) = GST_BUFFER_DURATION (buf); if ((ret = gst_pad_push (pngenc->srcpad, encoded_buf)) != GST_FLOW_OK) goto done; if (pngenc->snapshot) { GstEvent *event; GST_DEBUG_OBJECT (pngenc, "snapshot mode, sending EOS"); /* send EOS event, since a frame has been pushed out */ event = gst_event_new_eos (); gst_pad_push_event (pngenc->srcpad, event); ret = GST_FLOW_EOS; } done: gst_video_frame_unmap (&frame); exit: gst_buffer_unref (buf); GST_DEBUG_OBJECT (pngenc, "END, ret:%d", ret); if (pngenc->buffer_out != NULL) { gst_buffer_unref (pngenc->buffer_out); pngenc->buffer_out = NULL; } return ret; }
static GstFlowReturn gst_rtp_asf_pay_handle_buffer (GstRTPBasePayload * rtppay, GstBuffer * buffer) { GstRtpAsfPay *rtpasfpay = GST_RTP_ASF_PAY_CAST (rtppay); if (G_UNLIKELY (rtpasfpay->state == ASF_END)) { GST_LOG_OBJECT (rtpasfpay, "Dropping buffer as we already pushed all packets"); gst_buffer_unref (buffer); return GST_FLOW_EOS; /* we already finished our job */ } /* receive headers * we only accept if they are in a single buffer */ if (G_UNLIKELY (rtpasfpay->state == ASF_NOT_STARTED)) { guint64 header_size; if (gst_buffer_get_size (buffer) < 24) { /* guid+object size size */ GST_ERROR_OBJECT (rtpasfpay, "Buffer too small, smaller than a Guid and object size"); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } header_size = gst_asf_match_and_peek_obj_size_buf (buffer, &(guids[ASF_HEADER_OBJECT_INDEX])); if (header_size > 0) { GST_DEBUG_OBJECT (rtpasfpay, "ASF header guid received, size %" G_GUINT64_FORMAT, header_size); if (gst_buffer_get_size (buffer) < header_size) { GST_ERROR_OBJECT (rtpasfpay, "Headers should be contained in a single" " buffer"); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } else { rtpasfpay->state = ASF_DATA_OBJECT; /* clear previous headers, if any */ if (rtpasfpay->headers) { gst_buffer_unref (rtpasfpay->headers); } GST_DEBUG_OBJECT (rtpasfpay, "Storing headers"); if (gst_buffer_get_size (buffer) == header_size) { rtpasfpay->headers = buffer; return GST_FLOW_OK; } else { /* headers are a subbuffer of thie buffer */ GstBuffer *aux = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, header_size, gst_buffer_get_size (buffer) - header_size); rtpasfpay->headers = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, 0, header_size); gst_buffer_replace (&buffer, aux); } } } else { GST_ERROR_OBJECT (rtpasfpay, "Missing ASF header start"); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } } if (G_UNLIKELY (rtpasfpay->state == ASF_DATA_OBJECT)) { GstMapInfo map; if (gst_buffer_get_size (buffer) != ASF_DATA_OBJECT_SIZE) { GST_ERROR_OBJECT (rtpasfpay, "Received buffer of different size of " "the data object header"); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } gst_buffer_map (buffer, &map, GST_MAP_READ); if (gst_asf_match_guid (map.data, &(guids[ASF_DATA_OBJECT_INDEX]))) { gst_buffer_unmap (buffer, &map); GST_DEBUG_OBJECT (rtpasfpay, "Received data object header"); rtpasfpay->headers = gst_buffer_append (rtpasfpay->headers, buffer); rtpasfpay->state = ASF_PACKETS; return gst_rtp_asf_pay_parse_headers (rtpasfpay); } else { gst_buffer_unmap (buffer, &map); GST_ERROR_OBJECT (rtpasfpay, "Unexpected object received (was expecting " "data object)"); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } } if (G_LIKELY (rtpasfpay->state == ASF_PACKETS)) { /* in broadcast mode we can't trust the packets count information * from the headers * We assume that if this is on broadcast mode it is a live stream * and we are going to keep receiving packets indefinitely */ if (rtpasfpay->asfinfo.broadcast || rtpasfpay->packets_count < rtpasfpay->asfinfo.packets_count) { GST_DEBUG_OBJECT (rtpasfpay, "Received packet %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT, rtpasfpay->packets_count, rtpasfpay->asfinfo.packets_count); rtpasfpay->packets_count++; return gst_rtp_asf_pay_handle_packet (rtpasfpay, buffer); } else { GST_INFO_OBJECT (rtpasfpay, "Packets ended"); rtpasfpay->state = ASF_END; gst_buffer_unref (buffer); return GST_FLOW_EOS; } } gst_buffer_unref (buffer); return GST_FLOW_OK; }
static gboolean gst_ogg_avi_parse_setcaps (GstPad * pad, GstCaps * caps) { GstOggAviParse *ogg; GstStructure *structure; const GValue *codec_data; GstBuffer *buffer; GstMapInfo map; guint8 *ptr; gsize left; guint32 sizes[3]; GstCaps *outcaps; gint i, offs; ogg = GST_OGG_AVI_PARSE (GST_OBJECT_PARENT (pad)); structure = gst_caps_get_structure (caps, 0); /* take codec data */ codec_data = gst_structure_get_value (structure, "codec_data"); if (codec_data == NULL) goto no_data; /* only buffers are valid */ if (G_VALUE_TYPE (codec_data) != GST_TYPE_BUFFER) goto wrong_format; /* Now parse the data */ buffer = gst_value_get_buffer (codec_data); /* first 22 bytes are bits_per_sample, channel_mask, GUID * Then we get 3 LE guint32 with the 3 header sizes * then we get the bytes of the 3 headers. */ gst_buffer_map (buffer, &map, GST_MAP_READ); ptr = map.data; left = map.size; GST_LOG_OBJECT (ogg, "configuring codec_data of size %" G_GSIZE_FORMAT, left); /* skip headers */ ptr += 22; left -= 22; /* we need at least 12 bytes for the packet sizes of the 3 headers */ if (left < 12) goto buffer_too_small; /* read sizes of the 3 headers */ sizes[0] = GST_READ_UINT32_LE (ptr); sizes[1] = GST_READ_UINT32_LE (ptr + 4); sizes[2] = GST_READ_UINT32_LE (ptr + 8); GST_DEBUG_OBJECT (ogg, "header sizes: %u %u %u", sizes[0], sizes[1], sizes[2]); left -= 12; /* and we need at least enough data for all the headers */ if (left < sizes[0] + sizes[1] + sizes[2]) goto buffer_too_small; /* set caps */ outcaps = gst_caps_new_empty_simple ("audio/x-vorbis"); gst_pad_set_caps (ogg->srcpad, outcaps); gst_caps_unref (outcaps); /* copy header data */ offs = 34; for (i = 0; i < 3; i++) { GstBuffer *out; /* now output the raw vorbis header packets */ out = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offs, sizes[i]); gst_pad_push (ogg->srcpad, out); offs += sizes[i]; } gst_buffer_unmap (buffer, &map); return TRUE; /* ERRORS */ no_data: { GST_DEBUG_OBJECT (ogg, "no codec_data found in caps"); return FALSE; } wrong_format: { GST_DEBUG_OBJECT (ogg, "codec_data is not a buffer"); return FALSE; } buffer_too_small: { GST_DEBUG_OBJECT (ogg, "codec_data is too small"); gst_buffer_unmap (buffer, &map); return FALSE; } }
static GstFlowReturn gst_ivf_parse_handle_frame_data (GstIvfParse * ivf, GstBaseParseFrame * frame, gint * skipsize) { GstBuffer *const buffer = frame->buffer; GstMapInfo map; GstFlowReturn ret = GST_FLOW_OK; GstBuffer *out_buffer; gst_buffer_map (buffer, &map, GST_MAP_READ); if (map.size >= IVF_FILE_HEADER_SIZE) { guint32 frame_size = GST_READ_UINT32_LE (map.data); guint64 frame_pts = GST_READ_UINT64_LE (map.data + 4); GST_LOG_OBJECT (ivf, "Read frame header: size %u, pts %" G_GUINT64_FORMAT, frame_size, frame_pts); if (map.size < IVF_FRAME_HEADER_SIZE + frame_size) { gst_base_parse_set_min_frame_size (GST_BASE_PARSE_CAST (ivf), IVF_FRAME_HEADER_SIZE + frame_size); gst_buffer_unmap (buffer, &map); *skipsize = 0; goto end; } gst_buffer_unmap (buffer, &map); /* Eventually, we would need the buffer memory in a merged state anyway */ out_buffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS | GST_BUFFER_COPY_META | GST_BUFFER_COPY_MEMORY | GST_BUFFER_COPY_MERGE, IVF_FRAME_HEADER_SIZE, frame_size); if (!out_buffer) { GST_ERROR_OBJECT (ivf, "Failed to copy frame buffer"); ret = GST_FLOW_ERROR; *skipsize = IVF_FRAME_HEADER_SIZE + frame_size; goto end; } gst_buffer_replace (&frame->out_buffer, out_buffer); gst_buffer_unref (out_buffer); /* Detect resolution changes on key frames */ if (gst_buffer_map (frame->out_buffer, &map, GST_MAP_READ)) { guint32 width, height; if (ivf->fourcc == GST_MAKE_FOURCC ('V', 'P', '8', '0')) { guint32 frame_tag; frame_tag = GST_READ_UINT24_LE (map.data); if (!(frame_tag & 0x01) && map.size >= 10) { /* key frame */ GST_DEBUG_OBJECT (ivf, "key frame detected"); width = GST_READ_UINT16_LE (map.data + 6) & 0x3fff; height = GST_READ_UINT16_LE (map.data + 8) & 0x3fff; gst_ivf_parse_set_size (ivf, width, height); } } else if (ivf->fourcc == GST_MAKE_FOURCC ('V', 'P', '9', '0')) { /* Fixme: Add vp9 frame header parsing? */ } else if (ivf->fourcc == GST_MAKE_FOURCC ('A', 'V', '0', '1')) { /* Fixme: Add av1 frame header parsing? */ /* This would allow to parse dynamic resolution changes */ /* implement when gstav1parser is ready */ } gst_buffer_unmap (frame->out_buffer, &map); } if (ivf->fps_n > 0) { GST_BUFFER_TIMESTAMP (out_buffer) = gst_util_uint64_scale_int (GST_SECOND * frame_pts, ivf->fps_d, ivf->fps_n); } gst_ivf_parse_update_src_caps (ivf); ret = gst_base_parse_finish_frame (GST_BASE_PARSE_CAST (ivf), frame, IVF_FRAME_HEADER_SIZE + frame_size); *skipsize = 0; } else { GST_LOG_OBJECT (ivf, "Frame data not yet available."); gst_buffer_unmap (buffer, &map); *skipsize = 0; } end: return ret; }
static GstFlowReturn gst_dtsdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; GstDtsDec *dts = GST_DTSDEC (parent); gint first_access; if (dts->dvdmode) { guint8 data[2]; gsize size; gint offset, len; GstBuffer *subbuf; size = gst_buffer_get_size (buf); if (size < 2) goto not_enough_data; gst_buffer_extract (buf, 0, data, 2); first_access = (data[0] << 8) | data[1]; /* Skip the first_access header */ offset = 2; if (first_access > 1) { /* Length of data before first_access */ len = first_access - 1; if (len <= 0 || offset + len > size) goto bad_first_access_parameter; subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len); GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE; ret = dts->base_chain (pad, parent, subbuf); if (ret != GST_FLOW_OK) { gst_buffer_unref (buf); goto done; } offset += len; len = size - offset; if (len > 0) { subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len); GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf); ret = dts->base_chain (pad, parent, subbuf); } gst_buffer_unref (buf); } else { /* first_access = 0 or 1, so if there's a timestamp it applies to the first byte */ subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, size - offset); GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf); ret = dts->base_chain (pad, parent, subbuf); gst_buffer_unref (buf); } } else { ret = dts->base_chain (pad, parent, buf); } done: return ret; /* ERRORS */ not_enough_data: { GST_ELEMENT_ERROR (GST_ELEMENT (dts), STREAM, DECODE, (NULL), ("Insufficient data in buffer. Can't determine first_acess")); gst_buffer_unref (buf); return GST_FLOW_ERROR; } bad_first_access_parameter: { GST_ELEMENT_ERROR (GST_ELEMENT (dts), STREAM, DECODE, (NULL), ("Bad first_access parameter (%d) in buffer", first_access)); gst_buffer_unref (buf); return GST_FLOW_ERROR; } }
/* FIXME: this entire function looks completely wrong, if there is or * was a bug in GStreamer it should be fixed there (tpm) */ static GstPadProbeReturn brasero_transcode_buffer_handler (GstPad *pad, GstPadProbeInfo *info, gpointer user_data) { BraseroTranscodePrivate *priv; BraseroTranscode *self = user_data; GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info); GstPad *peer; gint64 size; priv = BRASERO_TRANSCODE_PRIVATE (self); size = gst_buffer_get_size (buffer); if (priv->segment_start <= 0 && priv->segment_end <= 0) return GST_PAD_PROBE_OK; /* what we do here is more or less what gstreamer does when seeking: * it reads and process from 0 to the seek position (I tried). * It even forwards the data before the seek position to the sink (which * is a problem in our case as it would be written) */ if (priv->size > priv->segment_end) { priv->size += size; return GST_PAD_PROBE_DROP; } if (priv->size + size > priv->segment_end) { GstBuffer *new_buffer; int data_size; /* the entire the buffer is not interesting for us */ /* create a new buffer and push it on the pad: * NOTE: we're going to receive it ... */ data_size = priv->segment_end - priv->size; new_buffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_METADATA, 0, data_size); /* FIXME: we can now modify the probe buffer in 0.11 */ /* Recursive: the following calls ourselves BEFORE we finish */ peer = gst_pad_get_peer (pad); gst_pad_push (peer, new_buffer); priv->size += size - data_size; /* post an EOS event to stop pipeline */ gst_pad_push_event (peer, gst_event_new_eos ()); gst_object_unref (peer); return GST_PAD_PROBE_DROP; } /* see if the buffer is in the segment */ if (priv->size < priv->segment_start) { GstBuffer *new_buffer; gint data_size; /* see if all the buffer is interesting for us */ if (priv->size + size < priv->segment_start) { priv->size += size; return GST_PAD_PROBE_DROP; } /* create a new buffer and push it on the pad: * NOTE: we're going to receive it ... */ data_size = priv->size + size - priv->segment_start; new_buffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_METADATA, size - data_size, data_size); /* FIXME: this looks dodgy (tpm) */ GST_BUFFER_TIMESTAMP (new_buffer) = GST_BUFFER_TIMESTAMP (buffer) + data_size; /* move forward by the size of bytes we dropped */ priv->size += size - data_size; /* FIXME: we can now modify the probe buffer in 0.11 */ /* this is recursive the following calls ourselves * BEFORE we finish */ peer = gst_pad_get_peer (pad); gst_pad_push (peer, new_buffer); gst_object_unref (peer); return GST_PAD_PROBE_DROP; } priv->size += size; priv->pos += size; return GST_PAD_PROBE_OK; }
static GstFlowReturn gst_rtp_klv_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; GstBufferList *list = NULL; GstRtpKlvPay *pay; GstMapInfo map; GstBuffer *outbuf = NULL; gsize offset; guint mtu, rtp_header_size, max_payload_size; pay = GST_RTP_KLV_PAY (basepayload); mtu = GST_RTP_BASE_PAYLOAD_MTU (basepayload); rtp_header_size = gst_rtp_buffer_calc_header_len (0); max_payload_size = mtu - rtp_header_size; gst_buffer_map (buf, &map, GST_MAP_READ); if (map.size == 0) goto done; /* KLV coding shall use and only use a fixed 16-byte SMPTE-administered * Universal Label, according to SMPTE 298M as Key (Rec. ITU R-BT.1653-1) */ if (map.size < 16 || GST_READ_UINT32_BE (map.data) != 0x060E2B34) goto bad_input; if (map.size > max_payload_size) list = gst_buffer_list_new (); GST_LOG_OBJECT (pay, "%" G_GSIZE_FORMAT " bytes of data to payload", map.size); offset = 0; while (offset < map.size) { GstBuffer *payloadbuf; GstRTPBuffer rtp = { NULL }; guint payload_size; guint bytes_left; bytes_left = map.size - offset; payload_size = MIN (bytes_left, max_payload_size); outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); if (payload_size == bytes_left) { GST_LOG_OBJECT (pay, "last packet of KLV unit"); gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); gst_rtp_buffer_set_marker (&rtp, 1); gst_rtp_buffer_unmap (&rtp); } GST_LOG_OBJECT (pay, "packet with payload size %u", payload_size); gst_rtp_copy_meta (GST_ELEMENT_CAST (pay), outbuf, buf, 0); payloadbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_MEMORY, offset, payload_size); /* join rtp header + payload memory parts */ outbuf = gst_buffer_append (outbuf, payloadbuf); GST_BUFFER_PTS (outbuf) = GST_BUFFER_PTS (buf); GST_BUFFER_DTS (outbuf) = GST_BUFFER_DTS (buf); /* and add to list */ if (list != NULL) gst_buffer_list_insert (list, -1, outbuf); offset += payload_size; } done: gst_buffer_unmap (buf, &map); gst_buffer_unref (buf); if (list != NULL) ret = gst_rtp_base_payload_push_list (basepayload, list); else if (outbuf != NULL) ret = gst_rtp_base_payload_push (basepayload, outbuf); return ret; /* ERRORS */ bad_input: { GST_ERROR_OBJECT (pay, "Input doesn't look like a KLV packet, ignoring"); goto done; } }
/** * gst_v4l2_buffer_pool_process: * @bpool: a #GstBufferPool * @buf: a #GstBuffer, maybe be replaced * * Process @buf in @bpool. For capture devices, this functions fills @buf with * data from the device. For output devices, this functions send the contents of * @buf to the device for playback. * * Returns: %GST_FLOW_OK on success. */ GstFlowReturn gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) { GstFlowReturn ret = GST_FLOW_OK; GstBufferPool *bpool = GST_BUFFER_POOL_CAST (pool); GstV4l2Object *obj = pool->obj; GST_DEBUG_OBJECT (pool, "process buffer %p", buf); g_return_val_if_fail (gst_buffer_pool_is_active (bpool), GST_FLOW_ERROR); if (GST_BUFFER_POOL_IS_FLUSHING (pool)) return GST_FLOW_FLUSHING; switch (obj->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: /* capture */ switch (obj->mode) { case GST_V4L2_IO_RW: /* capture into the buffer */ ret = gst_v4l2_do_read (pool, *buf); break; case GST_V4L2_IO_MMAP: case GST_V4L2_IO_DMABUF: { GstBuffer *tmp; if ((*buf)->pool == bpool) { if (gst_buffer_get_size (*buf) == 0) goto eos; /* start copying buffers when we are running low on buffers */ if (g_atomic_int_get (&pool->num_queued) < pool->copy_threshold) { GstBuffer *copy; if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) { if (gst_buffer_pool_acquire_buffer (bpool, ©, NULL) == GST_FLOW_OK) { gst_v4l2_buffer_pool_release_buffer (bpool, copy); goto done; } } /* copy the buffer */ copy = gst_buffer_copy_region (*buf, GST_BUFFER_COPY_ALL | GST_BUFFER_COPY_DEEP, 0, -1); GST_LOG_OBJECT (pool, "copy buffer %p->%p", *buf, copy); /* and requeue so that we can continue capturing */ gst_buffer_unref (*buf); *buf = copy; } /* nothing, data was inside the buffer when we did _acquire() */ goto done; } /* buffer not from our pool, grab a frame and copy it into the target */ if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp)) != GST_FLOW_OK) goto done; /* An empty buffer on capture indicates the end of stream */ if (gst_buffer_get_size (tmp) == 0) { gst_v4l2_buffer_pool_release_buffer (bpool, tmp); goto eos; } ret = gst_v4l2_buffer_pool_copy_buffer (pool, *buf, tmp); /* an queue the buffer again after the copy */ gst_v4l2_buffer_pool_release_buffer (bpool, tmp); if (ret != GST_FLOW_OK) goto copy_failed; break; } case GST_V4L2_IO_USERPTR: { struct UserPtrData *data; /* Replace our buffer with downstream allocated buffer */ data = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); gst_buffer_replace (buf, data->buffer); _unmap_userptr_frame (data); break; } case GST_V4L2_IO_DMABUF_IMPORT: { GstBuffer *tmp; /* Replace our buffer with downstream allocated buffer */ tmp = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); gst_buffer_replace (buf, tmp); gst_buffer_unref (tmp); break; } default: g_assert_not_reached (); break; } break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: /* playback */ switch (obj->mode) { case GST_V4L2_IO_RW: /* FIXME, do write() */ GST_WARNING_OBJECT (pool, "implement write()"); break; case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF_IMPORT: case GST_V4L2_IO_DMABUF: case GST_V4L2_IO_MMAP: { GstBuffer *to_queue = NULL; GstV4l2MemoryGroup *group; gint index; if ((*buf)->pool != bpool) goto copying; if (!gst_v4l2_is_buffer_valid (*buf, &group)) goto copying; index = group->buffer.index; GST_LOG_OBJECT (pool, "processing buffer %i from our pool", index); index = group->buffer.index; if (pool->buffers[index] != NULL) { GST_LOG_OBJECT (pool, "buffer %i already queued, copying", index); goto copying; } /* we can queue directly */ to_queue = gst_buffer_ref (*buf); copying: if (to_queue == NULL) { GstBufferPoolAcquireParams params = { 0 }; GST_LOG_OBJECT (pool, "alloc buffer from our pool"); /* this can return EOS if all buffers are outstanding which would * be strange because we would expect the upstream element to have * allocated them and returned to us.. */ params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT; ret = gst_buffer_pool_acquire_buffer (bpool, &to_queue, ¶ms); if (ret != GST_FLOW_OK) goto acquire_failed; ret = gst_v4l2_buffer_pool_prepare_buffer (pool, to_queue, *buf); if (ret != GST_FLOW_OK) { gst_buffer_unref (to_queue); goto prepare_failed; } } if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue)) != GST_FLOW_OK) goto queue_failed; /* if we are not streaming yet (this is the first buffer, start * streaming now */ if (!gst_v4l2_buffer_pool_streamon (pool)) { /* don't check return value because qbuf would have failed */ gst_v4l2_is_buffer_valid (to_queue, &group); /* qbuf has taken the ref of the to_queue buffer but we are no in * streaming state, so the flush logic won't be performed. * To avoid leaks, flush the allocator and restore the queued * buffer as non-queued */ gst_v4l2_allocator_flush (pool->vallocator); pool->buffers[group->buffer.index] = NULL; gst_mini_object_set_qdata (GST_MINI_OBJECT (to_queue), GST_V4L2_IMPORT_QUARK, NULL, NULL); gst_buffer_unref (to_queue); g_atomic_int_add (&pool->num_queued, -1); goto start_failed; } if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) { GstBuffer *out; /* all buffers are queued, try to dequeue one and release it back * into the pool so that _acquire can get to it again. */ ret = gst_v4l2_buffer_pool_dqbuf (pool, &out); if (ret == GST_FLOW_OK) /* release the rendered buffer back into the pool. This wakes up any * thread waiting for a buffer in _acquire(). */ gst_buffer_unref (out); } break; } default: g_assert_not_reached (); break; } break; default: g_assert_not_reached (); break; } done: return ret; /* ERRORS */ copy_failed: { GST_ERROR_OBJECT (pool, "failed to copy buffer"); return ret; } eos: { GST_DEBUG_OBJECT (pool, "end of stream reached"); return GST_FLOW_EOS; } acquire_failed: { if (ret == GST_FLOW_FLUSHING) GST_DEBUG_OBJECT (pool, "flushing"); else GST_WARNING_OBJECT (pool, "failed to acquire a buffer: %s", gst_flow_get_name (ret)); return ret; } prepare_failed: { GST_ERROR_OBJECT (pool, "failed to prepare data"); return ret; } queue_failed: { GST_ERROR_OBJECT (pool, "failed to queue buffer"); return ret; } start_failed: { GST_ERROR_OBJECT (pool, "failed to start streaming"); return GST_FLOW_ERROR; } }
static GstFlowReturn gst_opus_parse_handle_frame (GstBaseParse * base, GstBaseParseFrame * frame, gint * skip) { GstOpusParse *parse; guint8 *data; gsize size; guint32 packet_size; int ret = FALSE; const unsigned char *frames[48]; unsigned char toc; short frame_sizes[48]; int payload_offset; int packet_offset = 0; gboolean is_header, is_idheader, is_commentheader; GstMapInfo map; parse = GST_OPUS_PARSE (base); *skip = -1; gst_buffer_map (frame->buffer, &map, GST_MAP_READ); data = map.data; size = map.size; GST_DEBUG_OBJECT (parse, "Checking for frame, %" G_GSIZE_FORMAT " bytes in buffer", size); /* check for headers */ is_idheader = gst_opus_header_is_id_header (frame->buffer); is_commentheader = gst_opus_header_is_comment_header (frame->buffer); is_header = is_idheader || is_commentheader; if (!is_header) { int nframes; /* Next, check if there's an Opus packet there */ nframes = opus_packet_parse (data, size, &toc, frames, frame_sizes, &payload_offset); if (nframes < 0) { /* Then, check for the test vector framing */ GST_DEBUG_OBJECT (parse, "No Opus packet found, trying test vector framing"); if (size < 4) { GST_DEBUG_OBJECT (parse, "Too small"); goto beach; } packet_size = GST_READ_UINT32_BE (data); GST_DEBUG_OBJECT (parse, "Packet size: %u bytes", packet_size); if (packet_size > MAX_PAYLOAD_BYTES) { GST_DEBUG_OBJECT (parse, "Too large"); goto beach; } if (packet_size > size - 4) { GST_DEBUG_OBJECT (parse, "Truncated"); goto beach; } nframes = opus_packet_parse (data + 8, packet_size, &toc, frames, frame_sizes, &payload_offset); if (nframes < 0) { GST_DEBUG_OBJECT (parse, "No test vector framing either"); goto beach; } packet_offset = 8; /* for ad hoc framing, heed the framing, so we eat any padding */ payload_offset = packet_size; } else { /* Add up all the frame sizes found */ int f; for (f = 0; f < nframes; ++f) payload_offset += frame_sizes[f]; } } if (is_header) { *skip = 0; } else { *skip = packet_offset; size = payload_offset; } GST_DEBUG_OBJECT (parse, "Got Opus packet at offset %d, %" G_GSIZE_FORMAT " bytes", *skip, size); ret = TRUE; beach: gst_buffer_unmap (frame->buffer, &map); /* convert old style result to new one */ if (!ret) { if (*skip < 0) *skip = 1; return GST_FLOW_OK; } /* always skip first if needed */ if (*skip > 0) return GST_FLOW_OK; /* normalize again */ if (*skip < 0) *skip = 0; /* not enough */ if (size > map.size) return GST_FLOW_OK; /* FIXME some day ... should not mess with buffer itself */ if (!parse->got_headers) { gst_buffer_replace (&frame->buffer, gst_buffer_copy_region (frame->buffer, GST_BUFFER_COPY_ALL, 0, size)); gst_buffer_unref (frame->buffer); } ret = gst_opus_parse_parse_frame (base, frame); if (ret == GST_BASE_PARSE_FLOW_DROPPED) { frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP; ret = GST_FLOW_OK; } if (ret == GST_FLOW_OK) ret = gst_base_parse_finish_frame (base, frame, size); return ret; }
static GstFlowReturn gst_gio_base_src_create (GstBaseSrc * base_src, guint64 offset, guint size, GstBuffer ** buf_return) { GstGioBaseSrc *src = GST_GIO_BASE_SRC (base_src); GstBuffer *buf; GstFlowReturn ret = GST_FLOW_OK; g_return_val_if_fail (G_IS_INPUT_STREAM (src->stream), GST_FLOW_ERROR); /* If we have the requested part in our cache take a subbuffer of that, * otherwise fill the cache again with at least 4096 bytes from the * requested offset and return a subbuffer of that. * * We need caching because every read/seek operation will need to go * over DBus if our backend is GVfs and this is painfully slow. */ if (src->cache && offset >= GST_BUFFER_OFFSET (src->cache) && offset + size <= GST_BUFFER_OFFSET_END (src->cache)) { GST_DEBUG_OBJECT (src, "Creating subbuffer from cached buffer: offset %" G_GUINT64_FORMAT " length %u", offset, size); buf = gst_buffer_copy_region (src->cache, GST_BUFFER_COPY_ALL, offset - GST_BUFFER_OFFSET (src->cache), size); GST_BUFFER_OFFSET (buf) = offset; GST_BUFFER_OFFSET_END (buf) = offset + size; } else { guint cachesize = MAX (4096, size); GstMapInfo map; gssize read, res; gboolean success, eos; GError *err = NULL; if (src->cache) { gst_buffer_unref (src->cache); src->cache = NULL; } if (G_UNLIKELY (offset != src->position)) { if (!GST_GIO_STREAM_IS_SEEKABLE (src->stream)) return GST_FLOW_NOT_SUPPORTED; GST_DEBUG_OBJECT (src, "Seeking to position %" G_GUINT64_FORMAT, offset); ret = gst_gio_seek (src, G_SEEKABLE (src->stream), offset, src->cancel); if (ret == GST_FLOW_OK) src->position = offset; else return ret; } src->cache = gst_buffer_new_and_alloc (cachesize); if (G_UNLIKELY (src->cache == NULL)) { GST_ERROR_OBJECT (src, "Failed to allocate %u bytes", cachesize); return GST_FLOW_ERROR; } GST_LOG_OBJECT (src, "Reading %u bytes from offset %" G_GUINT64_FORMAT, cachesize, offset); /* GIO sometimes gives less bytes than requested although * it's not at the end of file. SMB for example only * supports reads up to 64k. So we loop here until we get at * at least the requested amount of bytes or a read returns * nothing. */ gst_buffer_map (src->cache, &map, GST_MAP_WRITE); read = 0; while (size - read > 0 && (res = g_input_stream_read (G_INPUT_STREAM (src->stream), map.data + read, cachesize - read, src->cancel, &err)) > 0) { read += res; } gst_buffer_unmap (src->cache, &map); success = (read >= 0); eos = (cachesize > 0 && read == 0); if (!success && !gst_gio_error (src, "g_input_stream_read", &err, &ret)) { GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), ("Could not read from stream: %s", err->message)); g_clear_error (&err); } if (success && !eos) { src->position += read; GST_BUFFER_OFFSET (src->cache) = offset; GST_BUFFER_OFFSET_END (src->cache) = offset + read; GST_DEBUG_OBJECT (src, "Read successful"); GST_DEBUG_OBJECT (src, "Creating subbuffer from new " "cached buffer: offset %" G_GUINT64_FORMAT " length %u", offset, size); buf = gst_buffer_copy_region (src->cache, GST_BUFFER_COPY_ALL, 0, MIN (size, read)); GST_BUFFER_OFFSET (buf) = offset; GST_BUFFER_OFFSET_END (buf) = offset + MIN (size, read); } else { GST_DEBUG_OBJECT (src, "Read not successful"); gst_buffer_unref (src->cache); src->cache = NULL; buf = NULL; } if (eos) ret = GST_FLOW_EOS; } *buf_return = buf; return ret; }
static GstFlowReturn gst_ac3_parse_chain_priv (GstPad * pad, GstObject * parent, GstBuffer * buf) { GstAc3Parse *ac3parse = GST_AC3_PARSE (parent); GstFlowReturn ret; gsize size; guint8 data[2]; gint offset; gint len; GstBuffer *subbuf; gint first_access; size = gst_buffer_get_size (buf); if (size < 2) goto not_enough_data; gst_buffer_extract (buf, 0, data, 2); first_access = (data[0] << 8) | data[1]; /* Skip the first_access header */ offset = 2; if (first_access > 1) { /* Length of data before first_access */ len = first_access - 1; if (len <= 0 || offset + len > size) goto bad_first_access_parameter; subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len); GST_BUFFER_DTS (subbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_PTS (subbuf) = GST_CLOCK_TIME_NONE; ret = ac3parse->baseparse_chainfunc (pad, parent, subbuf); if (ret != GST_FLOW_OK && ret != GST_FLOW_NOT_LINKED) { gst_buffer_unref (buf); goto done; } offset += len; len = size - offset; if (len > 0) { subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len); GST_BUFFER_PTS (subbuf) = GST_BUFFER_PTS (buf); GST_BUFFER_DTS (subbuf) = GST_BUFFER_DTS (buf); ret = ac3parse->baseparse_chainfunc (pad, parent, subbuf); } gst_buffer_unref (buf); } else { /* first_access = 0 or 1, so if there's a timestamp it applies to the first byte */ subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, size - offset); GST_BUFFER_PTS (subbuf) = GST_BUFFER_PTS (buf); GST_BUFFER_DTS (subbuf) = GST_BUFFER_DTS (buf); gst_buffer_unref (buf); ret = ac3parse->baseparse_chainfunc (pad, parent, subbuf); } done: return ret; /* ERRORS */ not_enough_data: { GST_ELEMENT_ERROR (GST_ELEMENT (ac3parse), STREAM, FORMAT, (NULL), ("Insufficient data in buffer. Can't determine first_acess")); gst_buffer_unref (buf); return GST_FLOW_ERROR; } bad_first_access_parameter: { GST_ELEMENT_ERROR (GST_ELEMENT (ac3parse), STREAM, FORMAT, (NULL), ("Bad first_access parameter (%d) in buffer", first_access)); gst_buffer_unref (buf); return GST_FLOW_ERROR; } }