static GstFlowReturn gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay) { GstFlowReturn ret; guint avail; GstBufferList *list; GstBuffer *outbuf; guint8 *payload; avail = gst_adapter_available (rtpmpvpay->adapter); ret = GST_FLOW_OK; list = gst_buffer_list_new_sized (avail / (GST_RTP_BASE_PAYLOAD_MTU (rtpmpvpay) - RTP_HEADER_LEN) + 1); while (avail > 0) { guint towrite; guint packet_len; guint payload_len; GstRTPBuffer rtp = { NULL }; GstBuffer *paybuf; packet_len = gst_rtp_buffer_calc_packet_len (avail + 4, 0, 0); towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpmpvpay)); payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); outbuf = gst_rtp_buffer_new_allocate (4, 0, 0); payload_len -= 4; gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); payload = gst_rtp_buffer_get_payload (&rtp); /* enable MPEG Video-specific header * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | MBZ |T| TR | |N|S|B|E| P | | BFC | | FFC | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * AN FBV FFV */ /* fill in the MPEG Video-specific header * data is set to 0x0 here */ memset (payload, 0x0, 4); avail -= payload_len; gst_rtp_buffer_set_marker (&rtp, avail == 0); gst_rtp_buffer_unmap (&rtp); paybuf = gst_adapter_take_buffer_fast (rtpmpvpay->adapter, payload_len); gst_rtp_copy_meta (GST_ELEMENT_CAST (rtpmpvpay), outbuf, paybuf, g_quark_from_static_string (GST_META_TAG_VIDEO_STR)); outbuf = gst_buffer_append (outbuf, paybuf); GST_BUFFER_PTS (outbuf) = rtpmpvpay->first_ts; gst_buffer_list_add (list, outbuf); } ret = gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmpvpay), list); return ret; }
static GstFlowReturn gst_rtp_mp4g_pay_flush (GstRtpMP4GPay * rtpmp4gpay) { guint avail, total; GstBuffer *outbuf; GstFlowReturn ret; guint mtu; /* the data available in the adapter is either smaller * than the MTU or bigger. In the case it is smaller, the complete * adapter contents can be put in one packet. In the case the * adapter has more than one MTU, we need to fragment the MPEG data * over multiple packets. */ total = avail = gst_adapter_available (rtpmp4gpay->adapter); ret = GST_FLOW_OK; mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp4gpay); while (avail > 0) { guint towrite; guint8 *payload; guint payload_len; guint packet_len; GstRTPBuffer rtp = { NULL }; /* this will be the total lenght of the packet */ packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0); /* fill one MTU or all available bytes, we need 4 spare bytes for * the AU header. */ towrite = MIN (packet_len, mtu - 4); /* this is the payload length */ payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); GST_DEBUG_OBJECT (rtpmp4gpay, "avail %d, towrite %d, packet_len %d, payload_len %d", avail, towrite, packet_len, payload_len); /* create buffer to hold the payload, also make room for the 4 header bytes. */ outbuf = gst_rtp_buffer_new_allocate (payload_len + 4, 0, 0); gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); /* copy payload */ payload = gst_rtp_buffer_get_payload (&rtp); /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+ * |AU-headers-length|AU-header|AU-header| |AU-header|padding| * | | (1) | (2) | | (n) | bits | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+ */ /* AU-headers-length, we only have 1 AU-header */ payload[0] = 0x00; payload[1] = 0x10; /* we use 16 bits for the header */ /* +---------------------------------------+ * | AU-size | * +---------------------------------------+ * | AU-Index / AU-Index-delta | * +---------------------------------------+ * | CTS-flag | * +---------------------------------------+ * | CTS-delta | * +---------------------------------------+ * | DTS-flag | * +---------------------------------------+ * | DTS-delta | * +---------------------------------------+ * | RAP-flag | * +---------------------------------------+ * | Stream-state | * +---------------------------------------+ */ /* The AU-header, no CTS, DTS, RAP, Stream-state * * AU-size is always the total size of the AU, not the fragmented size */ payload[2] = (total & 0x1fe0) >> 5; payload[3] = (total & 0x1f) << 3; /* we use 13 bits for the size, 3 bits index */ /* copy stuff from adapter to payload */ gst_adapter_copy (rtpmp4gpay->adapter, &payload[4], 0, payload_len); gst_adapter_flush (rtpmp4gpay->adapter, payload_len); /* marker only if the packet is complete */ gst_rtp_buffer_set_marker (&rtp, avail <= payload_len); gst_rtp_buffer_unmap (&rtp); GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4gpay->first_timestamp; GST_BUFFER_DURATION (outbuf) = rtpmp4gpay->first_duration; if (rtpmp4gpay->frame_len) { GST_BUFFER_OFFSET (outbuf) = rtpmp4gpay->offset; rtpmp4gpay->offset += rtpmp4gpay->frame_len; } ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), outbuf); avail -= payload_len; } return ret; }
static GstFlowReturn gst_rtp_ac3_pay_flush (GstRtpAC3Pay * rtpac3pay) { guint avail, FT, NF, mtu; GstBuffer *outbuf; GstFlowReturn ret; /* the data available in the adapter is either smaller * than the MTU or bigger. In the case it is smaller, the complete * adapter contents can be put in one packet. In the case the * adapter has more than one MTU, we need to split the AC3 data * over multiple packets. */ avail = gst_adapter_available (rtpac3pay->adapter); ret = GST_FLOW_OK; FT = 0; /* number of frames */ NF = rtpac3pay->NF; mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpac3pay); GST_LOG_OBJECT (rtpac3pay, "flushing %u bytes", avail); while (avail > 0) { guint towrite; guint8 *payload; guint payload_len; guint packet_len; GstRTPBuffer rtp = { NULL, }; GstBuffer *payload_buffer; /* this will be the total length of the packet */ packet_len = gst_rtp_buffer_calc_packet_len (2 + avail, 0, 0); /* fill one MTU or all available bytes */ towrite = MIN (packet_len, mtu); /* this is the payload length */ payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); /* create buffer to hold the payload */ outbuf = gst_rtp_buffer_new_allocate (2, 0, 0); if (FT == 0) { /* check if it all fits */ if (towrite < packet_len) { guint maxlen; GST_LOG_OBJECT (rtpac3pay, "we need to fragment"); /* check if we will be able to put at least 5/8th of the total * frame in this first frame. */ if ((avail * 5) / 8 >= (payload_len - 2)) FT = 1; else FT = 2; /* check how many fragments we will need */ maxlen = gst_rtp_buffer_calc_payload_len (mtu - 2, 0, 0); NF = (avail + maxlen - 1) / maxlen; } } else if (FT != 3) { /* remaining fragment */ FT = 3; } /* * 0 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | MBZ | FT| NF | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * FT: 0: one or more complete frames * 1: initial 5/8 fragment * 2: initial fragment not 5/8 * 3: other fragment * NF: amount of frames if FT = 0, else number of fragments. */ gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); GST_LOG_OBJECT (rtpac3pay, "FT %u, NF %u", FT, NF); payload = gst_rtp_buffer_get_payload (&rtp); payload[0] = (FT & 3); payload[1] = NF; payload_len -= 2; if (avail == payload_len) gst_rtp_buffer_set_marker (&rtp, TRUE); gst_rtp_buffer_unmap (&rtp); payload_buffer = gst_adapter_take_buffer_fast (rtpac3pay->adapter, payload_len); gst_rtp_copy_meta (GST_ELEMENT_CAST (rtpac3pay), outbuf, payload_buffer, g_quark_from_static_string (GST_META_TAG_AUDIO_STR)); outbuf = gst_buffer_append (outbuf, payload_buffer); avail -= payload_len; GST_BUFFER_PTS (outbuf) = rtpac3pay->first_ts; GST_BUFFER_DURATION (outbuf) = rtpac3pay->duration; ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpac3pay), outbuf); } return ret; }
static GstFlowReturn gst_rtp_asf_pay_handle_packet (GstRtpAsfPay * rtpasfpay, GstBuffer * buffer) { GstRTPBasePayload *rtppay; GstAsfPacketInfo *packetinfo; guint8 flags; guint8 *data; guint32 packet_util_size; guint32 packet_offset; guint32 size_left; GstFlowReturn ret = GST_FLOW_OK; rtppay = GST_RTP_BASE_PAYLOAD (rtpasfpay); packetinfo = &rtpasfpay->packetinfo; if (!gst_asf_parse_packet (buffer, packetinfo, TRUE, rtpasfpay->asfinfo.packet_size)) { GST_ERROR_OBJECT (rtpasfpay, "Error while parsing asf packet"); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } if (packetinfo->packet_size == 0) packetinfo->packet_size = rtpasfpay->asfinfo.packet_size; GST_LOG_OBJECT (rtpasfpay, "Packet size: %" G_GUINT32_FORMAT ", padding: %" G_GUINT32_FORMAT, packetinfo->packet_size, packetinfo->padding); /* update padding field to 0 */ if (packetinfo->padding > 0) { GstAsfPacketInfo info; /* find padding field offset */ guint offset = packetinfo->err_cor_len + 2 + gst_asf_get_var_size_field_len (packetinfo->packet_field_type) + gst_asf_get_var_size_field_len (packetinfo->seq_field_type); buffer = gst_buffer_make_writable (buffer); switch (packetinfo->padd_field_type) { case ASF_FIELD_TYPE_DWORD: gst_buffer_memset (buffer, offset, 0, 4); break; case ASF_FIELD_TYPE_WORD: gst_buffer_memset (buffer, offset, 0, 2); break; case ASF_FIELD_TYPE_BYTE: gst_buffer_memset (buffer, offset, 0, 1); break; case ASF_FIELD_TYPE_NONE: default: break; } gst_asf_parse_packet (buffer, &info, FALSE, 0); } if (packetinfo->padding != 0) packet_util_size = rtpasfpay->asfinfo.packet_size - packetinfo->padding; else packet_util_size = packetinfo->packet_size; packet_offset = 0; while (packet_util_size > 0) { /* Even if we don't fill completely an output buffer we * push it when we add an fragment. Because it seems that * it is not possible to determine where a asf packet * fragment ends inside a rtp packet payload. * This flag tells us to push the packet. */ gboolean force_push = FALSE; GstRTPBuffer rtp; /* we have no output buffer pending, create one */ if (rtpasfpay->current == NULL) { GST_LOG_OBJECT (rtpasfpay, "Creating new output buffer"); rtpasfpay->current = gst_rtp_buffer_new_allocate_len (GST_RTP_BASE_PAYLOAD_MTU (rtpasfpay), 0, 0); rtpasfpay->cur_off = 0; rtpasfpay->has_ts = FALSE; rtpasfpay->marker = FALSE; } gst_rtp_buffer_map (rtpasfpay->current, GST_MAP_READWRITE, &rtp); data = gst_rtp_buffer_get_payload (&rtp); data += rtpasfpay->cur_off; size_left = gst_rtp_buffer_get_payload_len (&rtp) - rtpasfpay->cur_off; GST_DEBUG_OBJECT (rtpasfpay, "Input buffer bytes consumed: %" G_GUINT32_FORMAT "/%" G_GSIZE_FORMAT, packet_offset, gst_buffer_get_size (buffer)); GST_DEBUG_OBJECT (rtpasfpay, "Output rtpbuffer status"); GST_DEBUG_OBJECT (rtpasfpay, "Current offset: %" G_GUINT32_FORMAT, rtpasfpay->cur_off); GST_DEBUG_OBJECT (rtpasfpay, "Size left: %" G_GUINT32_FORMAT, size_left); GST_DEBUG_OBJECT (rtpasfpay, "Has ts: %s", rtpasfpay->has_ts ? "yes" : "no"); if (rtpasfpay->has_ts) { GST_DEBUG_OBJECT (rtpasfpay, "Ts: %" G_GUINT32_FORMAT, rtpasfpay->ts); } flags = 0; if (packetinfo->has_keyframe) { flags = flags | 0x80; } flags = flags | 0x20; /* Relative timestamp is present */ if (!rtpasfpay->has_ts) { /* this is the first asf packet, its send time is the * rtp packet timestamp */ rtpasfpay->has_ts = TRUE; rtpasfpay->ts = packetinfo->send_time; } if (size_left >= packet_util_size + 8) { /* enough space for the rest of the packet */ if (packet_offset == 0) { flags = flags | 0x40; GST_WRITE_UINT24_BE (data + 1, packet_util_size); } else { GST_WRITE_UINT24_BE (data + 1, packet_offset); force_push = TRUE; } data[0] = flags; GST_WRITE_UINT32_BE (data + 4, (gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts); gst_buffer_extract (buffer, packet_offset, data + 8, packet_util_size); /* updating status variables */ rtpasfpay->cur_off += 8 + packet_util_size; size_left -= packet_util_size + 8; packet_offset += packet_util_size; packet_util_size = 0; rtpasfpay->marker = TRUE; } else { /* fragment packet */ data[0] = flags; GST_WRITE_UINT24_BE (data + 1, packet_offset); GST_WRITE_UINT32_BE (data + 4, (gint32) (packetinfo->send_time) - (gint32) rtpasfpay->ts); gst_buffer_extract (buffer, packet_offset, data + 8, size_left - 8); /* updating status variables */ rtpasfpay->cur_off += size_left; packet_offset += size_left - 8; packet_util_size -= size_left - 8; size_left = 0; force_push = TRUE; } /* there is not enough room for any more buffers */ if (force_push || size_left <= 8) { gst_rtp_buffer_set_ssrc (&rtp, rtppay->current_ssrc); gst_rtp_buffer_set_marker (&rtp, rtpasfpay->marker); gst_rtp_buffer_set_payload_type (&rtp, GST_RTP_BASE_PAYLOAD_PT (rtppay)); gst_rtp_buffer_set_seq (&rtp, rtppay->seqnum + 1); gst_rtp_buffer_set_timestamp (&rtp, packetinfo->send_time); gst_rtp_buffer_unmap (&rtp); /* trim remaining bytes not used */ if (size_left != 0) { gst_buffer_set_size (rtpasfpay->current, gst_buffer_get_size (rtpasfpay->current) - size_left); } GST_BUFFER_TIMESTAMP (rtpasfpay->current) = GST_BUFFER_TIMESTAMP (buffer); rtppay->seqnum++; rtppay->timestamp = packetinfo->send_time; GST_DEBUG_OBJECT (rtpasfpay, "Pushing rtp buffer"); ret = gst_pad_push (GST_RTP_BASE_PAYLOAD_SRCPAD (rtppay), rtpasfpay->current); rtpasfpay->current = NULL; if (ret != GST_FLOW_OK) { gst_buffer_unref (buffer); return ret; } } } gst_buffer_unref (buffer); return ret; }
static GstFlowReturn gst_rtp_h263p_pay_flush (GstRtpH263PPay * rtph263ppay) { guint avail; GstBuffer *outbuf; GstFlowReturn ret; gboolean fragmented; avail = gst_adapter_available (rtph263ppay->adapter); if (avail == 0) return GST_FLOW_OK; fragmented = FALSE; /* This algorithm assumes the H263/+/++ encoder sends complete frames in each * buffer */ /* With Fragmentation Mode at GST_FRAGMENTATION_MODE_NORMAL: * This algorithm implements the Follow-on packets method for packetization. * This assumes low packet loss network. * With Fragmentation Mode at GST_FRAGMENTATION_MODE_SYNC: * This algorithm separates large frames at synchronisation points (Segments) * (See RFC 4629 section 6). It would be interesting to have a property such as network * quality to select between both packetization methods */ /* TODO Add VRC supprt (See RFC 4629 section 5.2) */ while (avail > 0) { guint towrite; guint8 *payload; guint payload_len; gint header_len; guint next_gop = 0; gboolean found_gob = FALSE; GstRTPBuffer rtp = { NULL }; if (rtph263ppay->fragmentation_mode == GST_FRAGMENTATION_MODE_SYNC) { /* start after 1st gop possible */ guint parsed_len = 3; const guint8 *parse_data = NULL; parse_data = gst_adapter_map (rtph263ppay->adapter, avail); /* Check if we have a gob or eos , eossbs */ /* FIXME EOS and EOSSBS packets should never contain any gobs and vice-versa */ if (avail >= 3 && *parse_data == 0 && *(parse_data + 1) == 0 && *(parse_data + 2) >= 0x80) { GST_DEBUG_OBJECT (rtph263ppay, " Found GOB header"); found_gob = TRUE; } /* Find next and cut the packet accordingly */ /* TODO we should get as many gobs as possible until MTU is reached, this * code seems to just get one GOB per packet */ while (parsed_len + 2 < avail) { if (parse_data[parsed_len] == 0 && parse_data[parsed_len + 1] == 0 && parse_data[parsed_len + 2] >= 0x80) { next_gop = parsed_len; GST_DEBUG_OBJECT (rtph263ppay, " Next GOB Detected at : %d", next_gop); break; } parsed_len++; } gst_adapter_unmap (rtph263ppay->adapter); } /* for picture start frames (non-fragmented), we need to remove the first * two 0x00 bytes and set P=1 */ header_len = (fragmented && !found_gob) ? 2 : 0; towrite = MIN (avail, gst_rtp_buffer_calc_payload_len (GST_RTP_BASE_PAYLOAD_MTU (rtph263ppay) - header_len, 0, 0)); if (next_gop > 0) towrite = MIN (next_gop, towrite); payload_len = header_len + towrite; outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); /* last fragment gets the marker bit set */ gst_rtp_buffer_set_marker (&rtp, avail > towrite ? 0 : 1); payload = gst_rtp_buffer_get_payload (&rtp); gst_adapter_copy (rtph263ppay->adapter, &payload[header_len], 0, towrite); /* 0 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | RR |P|V| PLEN |PEBIT| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /* if fragmented or gop header , write p bit =1 */ payload[0] = (fragmented && !found_gob) ? 0x00 : 0x04; payload[1] = 0; GST_BUFFER_TIMESTAMP (outbuf) = rtph263ppay->first_timestamp; GST_BUFFER_DURATION (outbuf) = rtph263ppay->first_duration; gst_rtp_buffer_unmap (&rtp); gst_adapter_flush (rtph263ppay->adapter, towrite); ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtph263ppay), outbuf); avail -= towrite; fragmented = TRUE; } return ret; }
static GstFlowReturn gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) { GstRtpVRawPay *rtpvrawpay; GstFlowReturn ret = GST_FLOW_OK; gfloat packets_per_packline; guint pgroups_per_packet; guint packlines_per_list, buffers_per_list; guint lines_delay; /* after how many packed lines we push out a buffer list */ guint last_line; /* last pack line number we pushed out a buffer list */ guint line, offset; guint8 *p0, *yp, *up, *vp; guint ystride, uvstride; guint xinc, yinc; guint pgroup; guint mtu; guint width, height; gint field, fields; GstVideoFormat format; GstVideoFrame frame; gint interlaced; gboolean use_buffer_lists; GstBufferList *list = NULL; GstRTPBuffer rtp = { NULL, }; rtpvrawpay = GST_RTP_VRAW_PAY (payload); if (!gst_video_frame_map (&frame, &rtpvrawpay->vinfo, buffer, GST_MAP_READ)) { gst_buffer_unref (buffer); return GST_FLOW_ERROR; } GST_LOG_OBJECT (rtpvrawpay, "new frame of %" G_GSIZE_FORMAT " bytes", gst_buffer_get_size (buffer)); /* get pointer and strides of the planes */ p0 = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0); yp = GST_VIDEO_FRAME_COMP_DATA (&frame, 0); up = GST_VIDEO_FRAME_COMP_DATA (&frame, 1); vp = GST_VIDEO_FRAME_COMP_DATA (&frame, 2); ystride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0); uvstride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, 1); mtu = GST_RTP_BASE_PAYLOAD_MTU (payload); /* amount of bytes for one pixel */ pgroup = rtpvrawpay->pgroup; width = GST_VIDEO_INFO_WIDTH (&rtpvrawpay->vinfo); height = GST_VIDEO_INFO_HEIGHT (&rtpvrawpay->vinfo); interlaced = GST_VIDEO_INFO_IS_INTERLACED (&rtpvrawpay->vinfo); format = GST_VIDEO_INFO_FORMAT (&rtpvrawpay->vinfo); yinc = rtpvrawpay->yinc; xinc = rtpvrawpay->xinc; /* after how many packed lines we push out a buffer list */ lines_delay = GST_ROUND_UP_4 (height / rtpvrawpay->chunks_per_frame); /* calculate how many buffers we expect to store in a single buffer list */ pgroups_per_packet = (mtu - (12 + 14)) / pgroup; packets_per_packline = width / (xinc * pgroups_per_packet * 1.0); packlines_per_list = height / (yinc * rtpvrawpay->chunks_per_frame); buffers_per_list = packlines_per_list * packets_per_packline; buffers_per_list = GST_ROUND_UP_8 (buffers_per_list); use_buffer_lists = buffers_per_list > 1 && (rtpvrawpay->chunks_per_frame < (height / yinc)); fields = 1 + interlaced; /* start with line 0, offset 0 */ for (field = 0; field < fields; field++) { line = field; offset = 0; last_line = 0; if (use_buffer_lists) list = gst_buffer_list_new_sized (buffers_per_list); /* write all lines */ while (line < height) { guint left, pack_line; GstBuffer *out; guint8 *outdata, *headers; gboolean next_line, complete = FALSE; guint length, cont, pixels; /* get the max allowed payload length size, we try to fill the complete MTU */ left = gst_rtp_buffer_calc_payload_len (mtu, 0, 0); out = gst_rtp_buffer_new_allocate (left, 0, 0); if (field == 0) { GST_BUFFER_PTS (out) = GST_BUFFER_PTS (buffer); } else { GST_BUFFER_PTS (out) = GST_BUFFER_PTS (buffer) + GST_BUFFER_DURATION (buffer) / 2; } gst_rtp_buffer_map (out, GST_MAP_WRITE, &rtp); outdata = gst_rtp_buffer_get_payload (&rtp); GST_LOG_OBJECT (rtpvrawpay, "created buffer of size %u for MTU %u", left, mtu); /* * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Extended Sequence Number | Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |F| Line No |C| Offset | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Length |F| Line No | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |C| Offset | . * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ . * . . * . Two (partial) lines of video data . * . . * +---------------------------------------------------------------+ */ /* need 2 bytes for the extended sequence number */ *outdata++ = 0; *outdata++ = 0; left -= 2; /* the headers start here */ headers = outdata; /* make sure we can fit at least *one* header and pixel */ if (!(left > (6 + pgroup))) { gst_rtp_buffer_unmap (&rtp); gst_buffer_unref (out); goto too_small; } /* while we can fit at least one header and one pixel */ while (left > (6 + pgroup)) { /* we need a 6 bytes header */ left -= 6; /* get how may bytes we need for the remaining pixels */ pixels = width - offset; length = (pixels * pgroup) / xinc; if (left >= length) { /* pixels and header fit completely, we will write them and skip to the * next line. */ next_line = TRUE; } else { /* line does not fit completely, see how many pixels fit */ pixels = (left / pgroup) * xinc; length = (pixels * pgroup) / xinc; next_line = FALSE; } GST_LOG_OBJECT (rtpvrawpay, "filling %u bytes in %u pixels", length, pixels); left -= length; /* write length */ *outdata++ = (length >> 8) & 0xff; *outdata++ = length & 0xff; /* write line no */ *outdata++ = ((line >> 8) & 0x7f) | ((field << 7) & 0x80); *outdata++ = line & 0xff; if (next_line) { /* go to next line we do this here to make the check below easier */ line += yinc; } /* calculate continuation marker */ cont = (left > (6 + pgroup) && line < height) ? 0x80 : 0x00; /* write offset and continuation marker */ *outdata++ = ((offset >> 8) & 0x7f) | cont; *outdata++ = offset & 0xff; if (next_line) { /* reset offset */ offset = 0; GST_LOG_OBJECT (rtpvrawpay, "go to next line %u", line); } else { offset += pixels; GST_LOG_OBJECT (rtpvrawpay, "next offset %u", offset); } if (!cont) break; } GST_LOG_OBJECT (rtpvrawpay, "consumed %u bytes", (guint) (outdata - headers)); /* second pass, read headers and write the data */ while (TRUE) { guint offs, lin; /* read length and cont */ length = (headers[0] << 8) | headers[1]; lin = ((headers[2] & 0x7f) << 8) | headers[3]; offs = ((headers[4] & 0x7f) << 8) | headers[5]; cont = headers[4] & 0x80; pixels = length / pgroup; headers += 6; GST_LOG_OBJECT (payload, "writing length %u, line %u, offset %u, cont %d", length, lin, offs, cont); switch (format) { case GST_VIDEO_FORMAT_RGB: case GST_VIDEO_FORMAT_RGBA: case GST_VIDEO_FORMAT_BGR: case GST_VIDEO_FORMAT_BGRA: case GST_VIDEO_FORMAT_UYVY: case GST_VIDEO_FORMAT_UYVP: offs /= xinc; memcpy (outdata, p0 + (lin * ystride) + (offs * pgroup), length); outdata += length; break; case GST_VIDEO_FORMAT_AYUV: { gint i; guint8 *datap; datap = p0 + (lin * ystride) + (offs * 4); for (i = 0; i < pixels; i++) { *outdata++ = datap[2]; *outdata++ = datap[1]; *outdata++ = datap[3]; datap += 4; } break; } case GST_VIDEO_FORMAT_I420: { gint i; guint uvoff; guint8 *yd1p, *yd2p, *udp, *vdp; yd1p = yp + (lin * ystride) + (offs); yd2p = yd1p + ystride; uvoff = (lin / yinc * uvstride) + (offs / xinc); udp = up + uvoff; vdp = vp + uvoff; for (i = 0; i < pixels; i++) { *outdata++ = *yd1p++; *outdata++ = *yd1p++; *outdata++ = *yd2p++; *outdata++ = *yd2p++; *outdata++ = *udp++; *outdata++ = *vdp++; } break; } case GST_VIDEO_FORMAT_Y41B: { gint i; guint uvoff; guint8 *ydp, *udp, *vdp; ydp = yp + (lin * ystride) + offs; uvoff = (lin / yinc * uvstride) + (offs / xinc); udp = up + uvoff; vdp = vp + uvoff; for (i = 0; i < pixels; i++) { *outdata++ = *udp++; *outdata++ = *ydp++; *outdata++ = *ydp++; *outdata++ = *vdp++; *outdata++ = *ydp++; *outdata++ = *ydp++; } break; } default: gst_rtp_buffer_unmap (&rtp); gst_buffer_unref (out); goto unknown_sampling; } if (!cont) break; } if (line >= height) { GST_LOG_OBJECT (rtpvrawpay, "field/frame complete, set marker"); gst_rtp_buffer_set_marker (&rtp, TRUE); complete = TRUE; } gst_rtp_buffer_unmap (&rtp); if (left > 0) { GST_LOG_OBJECT (rtpvrawpay, "we have %u bytes left", left); gst_buffer_resize (out, 0, gst_buffer_get_size (out) - left); } gst_rtp_copy_video_meta (rtpvrawpay, out, buffer); /* Now either push out the buffer directly */ if (!use_buffer_lists) { ret = gst_rtp_base_payload_push (payload, out); continue; } /* or add the buffer to buffer list ... */ gst_buffer_list_add (list, out); /* .. and check if we need to push out the list */ pack_line = (line - field) / fields; if (complete || (pack_line > last_line && pack_line % lines_delay == 0)) { GST_LOG_OBJECT (rtpvrawpay, "pushing list of %u buffers up to pack " "line %u", gst_buffer_list_length (list), pack_line); ret = gst_rtp_base_payload_push_list (payload, list); list = NULL; if (!complete) list = gst_buffer_list_new_sized (buffers_per_list); last_line = pack_line; } } } gst_video_frame_unmap (&frame); gst_buffer_unref (buffer); return ret; /* ERRORS */ unknown_sampling: { GST_ELEMENT_ERROR (payload, STREAM, FORMAT, (NULL), ("unimplemented sampling")); gst_video_frame_unmap (&frame); gst_buffer_unref (buffer); return GST_FLOW_NOT_SUPPORTED; } too_small: { GST_ELEMENT_ERROR (payload, RESOURCE, NO_SPACE_LEFT, (NULL), ("not enough space to send at least one pixel")); gst_video_frame_unmap (&frame); gst_buffer_unref (buffer); return GST_FLOW_NOT_SUPPORTED; } }
/* calculate the min and max length of a packet. This depends on the configured * mtu and min/max_ptime values. We cache those so that we don't have to redo * all the calculations */ static gboolean gst_rtp_base_audio_payload_get_lengths (GstRTPBasePayload * basepayload, guint * min_payload_len, guint * max_payload_len, guint * align) { GstRTPBaseAudioPayload *payload; GstRTPBaseAudioPayloadPrivate *priv; guint max_mtu, mtu; guint maxptime_octets; guint minptime_octets; guint ptime_mult_octets; payload = GST_RTP_BASE_AUDIO_PAYLOAD_CAST (basepayload); priv = payload->priv; if (priv->align == 0) return FALSE; mtu = GST_RTP_BASE_PAYLOAD_MTU (payload); /* check cached values */ if (G_LIKELY (priv->cached_mtu == mtu && priv->cached_ptime_multiple == basepayload->ptime_multiple && priv->cached_ptime == basepayload->ptime && priv->cached_max_ptime == basepayload->max_ptime && priv->cached_min_ptime == basepayload->min_ptime)) { /* if nothing changed, return cached values */ *min_payload_len = priv->cached_min_length; *max_payload_len = priv->cached_max_length; *align = priv->cached_align; return TRUE; } ptime_mult_octets = priv->time_to_bytes (payload, basepayload->ptime_multiple); *align = ALIGN_DOWN (MAX (priv->align, ptime_mult_octets), priv->align); /* ptime max */ if (basepayload->max_ptime != -1) { maxptime_octets = priv->time_to_bytes (payload, basepayload->max_ptime); } else { maxptime_octets = G_MAXUINT; } /* MTU max */ max_mtu = gst_rtp_buffer_calc_payload_len (mtu, 0, 0); /* round down to alignment */ max_mtu = ALIGN_DOWN (max_mtu, *align); /* combine max ptime and max payload length */ *max_payload_len = MIN (max_mtu, maxptime_octets); /* min number of bytes based on a given ptime */ minptime_octets = priv->time_to_bytes (payload, basepayload->min_ptime); /* must be at least one frame size */ *min_payload_len = MAX (minptime_octets, *align); if (*min_payload_len > *max_payload_len) *min_payload_len = *max_payload_len; /* If the ptime is specified in the caps, tried to adhere to it exactly */ if (basepayload->ptime) { guint ptime_in_bytes = priv->time_to_bytes (payload, basepayload->ptime); /* clip to computed min and max lengths */ ptime_in_bytes = MAX (*min_payload_len, ptime_in_bytes); ptime_in_bytes = MIN (*max_payload_len, ptime_in_bytes); *min_payload_len = *max_payload_len = ptime_in_bytes; } /* cache values */ priv->cached_mtu = mtu; priv->cached_ptime = basepayload->ptime; priv->cached_min_ptime = basepayload->min_ptime; priv->cached_max_ptime = basepayload->max_ptime; priv->cached_ptime_multiple = basepayload->ptime_multiple; priv->cached_min_length = *min_payload_len; priv->cached_max_length = *max_payload_len; priv->cached_align = *align; return TRUE; }