/** * gst_base_rtp_audio_payload_push: * @baseaudiopayload: a #GstBaseRTPPayload * @data: data to set as payload * @payload_len: length of payload * @timestamp: a #GstClockTime * * Create an RTP buffer and store @payload_len bytes of @data as the * payload. Set the timestamp on the new buffer to @timestamp before pushing * the buffer downstream. * * Returns: a #GstFlowReturn * * Since: 0.10.13 */ GstFlowReturn gst_base_rtp_audio_payload_push (GstBaseRTPAudioPayload * baseaudiopayload, const guint8 * data, guint payload_len, GstClockTime timestamp) { GstBaseRTPPayload *basepayload; GstBuffer *outbuf; guint8 *payload; GstFlowReturn ret; basepayload = GST_BASE_RTP_PAYLOAD (baseaudiopayload); GST_DEBUG_OBJECT (baseaudiopayload, "Pushing %d bytes ts %" GST_TIME_FORMAT, payload_len, GST_TIME_ARGS (timestamp)); /* create buffer to hold the payload */ outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); /* copy payload */ payload = gst_rtp_buffer_get_payload (outbuf); memcpy (payload, data, payload_len); /* set metadata */ gst_base_rtp_audio_payload_set_meta (baseaudiopayload, outbuf, payload_len, timestamp); ret = gst_basertppayload_push (basepayload, outbuf); return ret; }
static GstFlowReturn gst_rtp_mp2t_pay_flush (GstRTPMP2TPay * rtpmp2tpay) { guint avail; guint8 *payload; GstFlowReturn ret; GstBuffer *outbuf; avail = gst_adapter_available (rtpmp2tpay->adapter); if (avail == 0) return GST_FLOW_OK; outbuf = gst_rtp_buffer_new_allocate (avail, 0, 0); /* get payload */ payload = gst_rtp_buffer_get_payload (outbuf); /* copy stuff from adapter to payload */ gst_adapter_copy (rtpmp2tpay->adapter, payload, 0, avail); GST_BUFFER_TIMESTAMP (outbuf) = rtpmp2tpay->first_ts; GST_BUFFER_DURATION (outbuf) = rtpmp2tpay->duration; GST_DEBUG_OBJECT (rtpmp2tpay, "pushing buffer of size %d", GST_BUFFER_SIZE (outbuf)); ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp2tpay), outbuf); /* flush the adapter content */ gst_adapter_flush (rtpmp2tpay->adapter, avail); return ret; }
static GstFlowReturn gst_rtp_g723_pay_flush (GstRTPG723Pay * pay) { GstBuffer *outbuf; GstFlowReturn ret; guint8 *payload; guint avail; avail = gst_adapter_available (pay->adapter); outbuf = gst_rtp_buffer_new_allocate (avail, 0, 0); payload = gst_rtp_buffer_get_payload (outbuf); GST_BUFFER_TIMESTAMP (outbuf) = pay->timestamp; GST_BUFFER_DURATION (outbuf) = pay->duration; /* copy G723 data as payload */ gst_adapter_copy (pay->adapter, payload, 0, avail); /* flush bytes from adapter */ gst_adapter_flush (pay->adapter, avail); pay->timestamp = GST_CLOCK_TIME_NONE; pay->duration = 0; /* set discont and marker */ if (pay->discont) { GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); gst_rtp_buffer_set_marker (outbuf, TRUE); pay->discont = FALSE; } ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (pay), outbuf); return ret; }
static GstFlowReturn gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay, GstClockTime timestamp, GstClockTime duration) { GstBuffer *outbuf; GstFlowReturn ret; guint avail; guint8 *payload; gint packet_size; gint payload_size; avail = gst_adapter_available (rtpmpvpay->adapter); packet_size = gst_rtp_buffer_calc_packet_len (4 + avail, 0, 0); /* check for the maximum size of the rtp buffer */ if (packet_size > GST_BASE_RTP_PAYLOAD_MTU (rtpmpvpay)) { payload_size = GST_BASE_RTP_PAYLOAD_MTU (rtpmpvpay) - gst_rtp_buffer_calc_packet_len (4, 0, 0); } else { payload_size = avail; } outbuf = gst_rtp_buffer_new_allocate (4 + payload_size, 0, 0); /* 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 */ payload = gst_rtp_buffer_get_payload (outbuf); /* fill in the MPEG Video-specific header */ memset (payload, 0x0, 4); /* copy stuff from adapter to payload */ gst_adapter_copy (rtpmpvpay->adapter, payload + 4, 0, payload_size); GST_BUFFER_TIMESTAMP (outbuf) = rtpmpvpay->first_ts; GST_BUFFER_DURATION (outbuf) = rtpmpvpay->duration; GST_DEBUG_OBJECT (rtpmpvpay, "pushing buffer of size %d", GST_BUFFER_SIZE (outbuf)); ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmpvpay), outbuf); gst_adapter_flush (rtpmpvpay->adapter, payload_size); /* update the timestamp and duration */ rtpmpvpay->first_ts = timestamp; rtpmpvpay->duration = duration; /* check if there is enough data for another rtp buffer */ avail = gst_adapter_available (rtpmpvpay->adapter); packet_size = gst_rtp_buffer_calc_packet_len (4 + avail, 0, 0); if (packet_size >= GST_BASE_RTP_PAYLOAD_MTU (rtpmpvpay) && ret == GST_FLOW_OK) { GST_DEBUG_OBJECT (rtpmpvpay, "Have enough data for another rtp packet"); ret = gst_rtp_mpv_pay_flush (rtpmpvpay, timestamp, duration); } return ret; }
static GstFlowReturn gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay) { GstBuffer *outbuf; GstFlowReturn ret; guint avail; guint8 *payload; avail = gst_adapter_available (rtpmpvpay->adapter); ret = GST_FLOW_OK; while (avail > 0) { guint towrite; guint packet_len; guint payload_len; packet_len = gst_rtp_buffer_calc_packet_len (avail, 4, 0); towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmpvpay)); payload_len = gst_rtp_buffer_calc_payload_len (towrite, 4, 0); outbuf = gst_rtp_buffer_new_allocate (payload_len, 4, 0); payload = gst_rtp_buffer_get_payload (outbuf); /* 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); gst_adapter_copy (rtpmpvpay->adapter, payload + 4, 0, payload_len); gst_adapter_flush (rtpmpvpay->adapter, payload_len); avail -= payload_len; gst_rtp_buffer_set_marker (outbuf, avail == 0); GST_BUFFER_TIMESTAMP (outbuf) = rtpmpvpay->first_ts; ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmpvpay), outbuf); } return ret; }
static GstFlowReturn gst_rtp_celt_pay_flush_queued (GstRtpCELTPay * rtpceltpay) { GstFlowReturn ret; GstBuffer *buf, *outbuf; guint8 *payload, *spayload; guint payload_len; GstClockTime duration; payload_len = rtpceltpay->bytes + rtpceltpay->sbytes; duration = rtpceltpay->qduration; GST_DEBUG_OBJECT (rtpceltpay, "flushing out %u, duration %" GST_TIME_FORMAT, payload_len, GST_TIME_ARGS (rtpceltpay->qduration)); /* get a big enough packet for the sizes + payloads */ outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); GST_BUFFER_DURATION (outbuf) = duration; /* point to the payload for size headers and data */ spayload = gst_rtp_buffer_get_payload (outbuf); payload = spayload + rtpceltpay->sbytes; while ((buf = g_queue_pop_head (rtpceltpay->queue))) { guint size; /* copy first timestamp to output */ if (GST_BUFFER_TIMESTAMP (outbuf) == -1) GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); /* write the size to the header */ size = GST_BUFFER_SIZE (buf); while (size > 0xff) { *spayload++ = 0xff; size -= 0xff; } *spayload++ = size; size = GST_BUFFER_SIZE (buf); /* copy payload */ memcpy (payload, GST_BUFFER_DATA (buf), size); payload += size; gst_buffer_unref (buf); } /* we consumed it all */ rtpceltpay->bytes = 0; rtpceltpay->sbytes = 0; rtpceltpay->qduration = 0; ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpceltpay), outbuf); return ret; }
static GstFlowReturn gst_rtp_gsm_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRTPGSMPay *rtpgsmpay; guint size, payload_len; GstBuffer *outbuf; guint8 *payload, *data; GstClockTime timestamp, duration; GstFlowReturn ret; rtpgsmpay = GST_RTP_GSM_PAY (basepayload); size = GST_BUFFER_SIZE (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); /* FIXME, only one GSM frame per RTP packet for now */ payload_len = size; /* FIXME, just error out for now */ if (payload_len > GST_BASE_RTP_PAYLOAD_MTU (rtpgsmpay)) { GST_ELEMENT_ERROR (rtpgsmpay, STREAM, ENCODE, (NULL), ("payload_len %u > mtu %u", payload_len, GST_BASE_RTP_PAYLOAD_MTU (rtpgsmpay))); return GST_FLOW_ERROR; } outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); /* copy timestamp and duration */ GST_BUFFER_TIMESTAMP (outbuf) = timestamp; GST_BUFFER_DURATION (outbuf) = duration; /* get payload */ payload = gst_rtp_buffer_get_payload (outbuf); data = GST_BUFFER_DATA (buffer); /* copy data in payload */ memcpy (&payload[0], data, size); gst_buffer_unref (buffer); GST_DEBUG ("gst_rtp_gsm_pay_chain: pushing buffer of size %d", GST_BUFFER_SIZE (outbuf)); ret = gst_basertppayload_push (basepayload, outbuf); return ret; }
static GstFlowReturn gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay) { guint avail; 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 MP4V data * over multiple packets. */ avail = gst_adapter_available (rtpmp4vpay->adapter); ret = GST_FLOW_OK; while (avail > 0) { guint towrite; guint8 *payload; guint payload_len; guint packet_len; /* 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 */ towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmp4vpay)); /* 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 (payload_len, 0, 0); /* copy payload */ payload = gst_rtp_buffer_get_payload (outbuf); gst_adapter_copy (rtpmp4vpay->adapter, payload, 0, payload_len); gst_adapter_flush (rtpmp4vpay->adapter, payload_len); avail -= payload_len; gst_rtp_buffer_set_marker (outbuf, avail == 0); GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp; ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp4vpay), outbuf); } return ret; }
static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) { guint available; guint max_payload; GstBuffer *outbuf; guint8 *payload_data; guint frame_count; guint payload_length; struct rtp_payload *payload; if (sbcpay->frame_length == 0) { GST_ERROR_OBJECT(sbcpay, "Frame length is 0"); return GST_FLOW_ERROR; } available = gst_adapter_available(sbcpay->adapter); max_payload = gst_rtp_buffer_calc_payload_len( GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); max_payload = MIN(max_payload, available); frame_count = max_payload / sbcpay->frame_length; payload_length = frame_count * sbcpay->frame_length; if (payload_length == 0) /* Nothing to send */ return GST_FLOW_OK; outbuf = gst_rtp_buffer_new_allocate(payload_length + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); gst_rtp_buffer_set_payload_type(outbuf, GST_BASE_RTP_PAYLOAD_PT(sbcpay)); payload_data = gst_rtp_buffer_get_payload(outbuf); payload = (struct rtp_payload *) payload_data; memset(payload, 0, sizeof(struct rtp_payload)); payload->frame_count = frame_count; gst_adapter_copy(sbcpay->adapter, payload_data + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length); gst_adapter_flush(sbcpay->adapter, payload_length); GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp; GST_DEBUG_OBJECT(sbcpay, "Pushing %d bytes", payload_length); return gst_basertppayload_push(GST_BASE_RTP_PAYLOAD(sbcpay), outbuf); }
static GstFlowReturn gst_rtp_vraw_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) { GstRtpVRawPay *rtpvrawpay; GstFlowReturn ret = GST_FLOW_OK; guint line, offset; guint8 *data, *yp, *up, *vp; guint ystride, uvstride; guint size, pgroup; guint mtu; guint width, height; gint field; rtpvrawpay = GST_RTP_VRAW_PAY (payload); data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); GST_LOG_OBJECT (rtpvrawpay, "new frame of %u bytes", size); /* get pointer and strides of the planes */ yp = data + rtpvrawpay->yp; up = data + rtpvrawpay->up; vp = data + rtpvrawpay->vp; ystride = rtpvrawpay->ystride; uvstride = rtpvrawpay->uvstride; mtu = GST_BASE_RTP_PAYLOAD_MTU (payload); /* amount of bytes for one pixel */ pgroup = rtpvrawpay->pgroup; width = rtpvrawpay->width; height = rtpvrawpay->height; /* start with line 0, offset 0 */ for (field = 0; field < 1 + rtpvrawpay->interlaced; field++) { line = field; offset = 0; /* write all lines */ while (line < height) { guint left; GstBuffer *out; guint8 *outdata, *headers; gboolean next_line; 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_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer); } else { GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer) / 2; } outdata = gst_rtp_buffer_get_payload (out); 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; /* 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) / rtpvrawpay->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) * rtpvrawpay->xinc; length = (pixels * pgroup) / rtpvrawpay->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 += rtpvrawpay->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 (rtpvrawpay->sampling) { 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: offs /= rtpvrawpay->xinc; memcpy (outdata, yp + (lin * ystride) + (offs * pgroup), length); outdata += length; break; case GST_VIDEO_FORMAT_AYUV: { gint i; guint8 *datap; datap = yp + (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 / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->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 / rtpvrawpay->yinc * uvstride) + (offs / rtpvrawpay->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_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 (out, TRUE); } if (left > 0) { GST_LOG_OBJECT (rtpvrawpay, "we have %u bytes left", left); GST_BUFFER_SIZE (out) -= left; } /* push buffer */ ret = gst_basertppayload_push (payload, out); } } gst_buffer_unref (buffer); return ret; /* ERRORS */ unknown_sampling: { GST_ELEMENT_ERROR (payload, STREAM, FORMAT, (NULL), ("unimplemented sampling")); gst_buffer_unref (buffer); return GST_FLOW_NOT_SUPPORTED; } }
static GstFlowReturn gst_rtp_amr_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRtpAMRPay *rtpamrpay; GstFlowReturn ret; guint size, payload_len; GstBuffer *outbuf; guint8 *payload, *data, *payload_amr; GstClockTime timestamp, duration; guint packet_len, mtu; gint i, num_packets, num_nonempty_packets; gint amr_len; gint *frame_size; rtpamrpay = GST_RTP_AMR_PAY (basepayload); mtu = GST_BASE_RTP_PAYLOAD_MTU (rtpamrpay); size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); /* setup frame size pointer */ if (rtpamrpay->mode == GST_RTP_AMR_P_MODE_NB) frame_size = nb_frame_size; else frame_size = wb_frame_size; GST_DEBUG_OBJECT (basepayload, "got %d bytes", size); /* FIXME, only * octet aligned, no interleaving, single channel, no CRC, * no robust-sorting. To fix this you need to implement the downstream * negotiation function. */ /* first count number of packets and total amr frame size */ amr_len = num_packets = num_nonempty_packets = 0; for (i = 0; i < size; i++) { guint8 FT; gint fr_size; FT = (data[i] & 0x78) >> 3; fr_size = frame_size[FT]; GST_DEBUG_OBJECT (basepayload, "frame size %d", fr_size); /* FIXME, we don't handle this yet.. */ if (fr_size <= 0) goto wrong_size; amr_len += fr_size; num_nonempty_packets++; num_packets++; i += fr_size; } if (amr_len > size) goto incomplete_frame; /* we need one extra byte for the CMR, the ToC is in the input * data */ payload_len = size + 1; /* get packet len to check against MTU */ packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0); if (packet_len > mtu) goto too_big; /* now alloc output buffer */ outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); /* copy timestamp, or fabricate one */ if (timestamp != GST_CLOCK_TIME_NONE) GST_BUFFER_TIMESTAMP (outbuf) = timestamp; else { /* AMR (nb) and AMR-WB both have 20 ms per frame */ /* FIXME: when we do more than one AMR frame per packet, fix this */ gint count = basepayload->seqnum - basepayload->seqnum_base; GST_BUFFER_TIMESTAMP (outbuf) = count * 20 * GST_MSECOND; } if (duration != GST_CLOCK_TIME_NONE) GST_BUFFER_DURATION (outbuf) = duration; else { GST_BUFFER_DURATION (outbuf) = 20 * GST_MSECOND; } /* get payload, this is now writable */ payload = gst_rtp_buffer_get_payload (outbuf); /* 0 1 2 3 4 5 6 7 * +-+-+-+-+-+-+-+-+ * | CMR |R|R|R|R| * +-+-+-+-+-+-+-+-+ */ payload[0] = 0xF0; /* CMR, no specific mode requested */ /* this is where we copy the AMR data, after num_packets FTs and the * CMR. */ payload_amr = payload + num_packets + 1; /* copy data in payload, first we copy all the FTs then all * the AMR data. The last FT has to have the F flag cleared. */ for (i = 1; i <= num_packets; i++) { guint8 FT; gint fr_size; /* 0 1 2 3 4 5 6 7 * +-+-+-+-+-+-+-+-+ * |F| FT |Q|P|P| more FT... * +-+-+-+-+-+-+-+-+ */ FT = (*data & 0x78) >> 3; fr_size = frame_size[FT]; if (i == num_packets) /* last packet, clear F flag */ payload[i] = *data & 0x7f; else /* set F flag */ payload[i] = *data | 0x80; memcpy (payload_amr, &data[1], fr_size); /* all sizes are > 0 since we checked for that above */ data += fr_size + 1; payload_amr += fr_size; } gst_buffer_unref (buffer); ret = gst_basertppayload_push (basepayload, outbuf); return ret; /* ERRORS */ wrong_size: { GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, (NULL), ("received AMR frame with size <= 0")); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } incomplete_frame: { GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, (NULL), ("received incomplete AMR frames")); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } too_big: { GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, (NULL), ("received too many AMR frames for MTU")); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } }
static GstFlowReturn gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay) { guint avail; GstBuffer *outbuf; GstFlowReturn ret; guint16 frag_offset; /* 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 MPA data * over multiple packets. The frag_offset in each packet header * needs to be updated with the position in the MPA frame. */ avail = gst_adapter_available (rtpmpapay->adapter); ret = GST_FLOW_OK; frag_offset = 0; while (avail > 0) { guint towrite; guint8 *payload; guint payload_len; guint packet_len; /* this will be the total lenght of the packet */ packet_len = gst_rtp_buffer_calc_packet_len (4 + avail, 0, 0); /* fill one MTU or all available bytes */ towrite = MIN (packet_len, GST_BASE_RTP_PAYLOAD_MTU (rtpmpapay)); /* 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 (payload_len, 0, 0); payload_len -= 4; gst_rtp_buffer_set_payload_type (outbuf, GST_RTP_PAYLOAD_MPA); /* * 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 | Frag_offset | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ payload = gst_rtp_buffer_get_payload (outbuf); payload[0] = 0; payload[1] = 0; payload[2] = frag_offset >> 8; payload[3] = frag_offset & 0xff; gst_adapter_copy (rtpmpapay->adapter, &payload[4], 0, payload_len); gst_adapter_flush (rtpmpapay->adapter, payload_len); avail -= payload_len; frag_offset += payload_len; if (avail == 0) gst_rtp_buffer_set_marker (outbuf, TRUE); GST_BUFFER_TIMESTAMP (outbuf) = rtpmpapay->first_ts; GST_BUFFER_DURATION (outbuf) = rtpmpapay->duration; ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmpapay), outbuf); } 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; 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_peek (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++; } } /* 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_BASE_RTP_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); /* last fragment gets the marker bit set */ gst_rtp_buffer_set_marker (outbuf, avail > towrite ? 0 : 1); payload = gst_rtp_buffer_get_payload (outbuf); 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_adapter_flush (rtph263ppay->adapter, towrite); ret = gst_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtph263ppay), outbuf); avail -= towrite; fragmented = TRUE; } return ret; }
/** * gst_base_rtp_audio_payload_flush: * @baseaudiopayload: a #GstBaseRTPPayload * @payload_len: length of payload * @timestamp: a #GstClockTime * * Create an RTP buffer and store @payload_len bytes of the adapter as the * payload. Set the timestamp on the new buffer to @timestamp before pushing * the buffer downstream. * * If @payload_len is -1, all pending bytes will be flushed. If @timestamp is * -1, the timestamp will be calculated automatically. * * Returns: a #GstFlowReturn * * Since: 0.10.25 */ GstFlowReturn gst_base_rtp_audio_payload_flush (GstBaseRTPAudioPayload * baseaudiopayload, guint payload_len, GstClockTime timestamp) { GstBaseRTPPayload *basepayload; GstBaseRTPAudioPayloadPrivate *priv; GstBuffer *outbuf; guint8 *payload; GstFlowReturn ret; GstAdapter *adapter; guint64 distance; priv = baseaudiopayload->priv; adapter = priv->adapter; basepayload = GST_BASE_RTP_PAYLOAD (baseaudiopayload); if (payload_len == -1) payload_len = gst_adapter_available (adapter); /* nothing to do, just return */ if (payload_len == 0) return GST_FLOW_OK; if (timestamp == -1) { /* calculate the timestamp */ timestamp = gst_adapter_prev_timestamp (adapter, &distance); GST_LOG_OBJECT (baseaudiopayload, "last timestamp %" GST_TIME_FORMAT ", distance %" G_GUINT64_FORMAT, GST_TIME_ARGS (timestamp), distance); if (GST_CLOCK_TIME_IS_VALID (timestamp) && distance > 0) { /* convert the number of bytes since the last timestamp to time and add to * the last seen timestamp */ timestamp += priv->bytes_to_time (baseaudiopayload, distance); } } GST_DEBUG_OBJECT (baseaudiopayload, "Pushing %d bytes ts %" GST_TIME_FORMAT, payload_len, GST_TIME_ARGS (timestamp)); if (priv->buffer_list && gst_adapter_available_fast (adapter) >= payload_len) { GstBuffer *buffer; /* we can quickly take a buffer out of the adapter without having to copy * anything. */ buffer = gst_adapter_take_buffer (adapter, payload_len); ret = gst_base_rtp_audio_payload_push_buffer (baseaudiopayload, buffer); } else { /* create buffer to hold the payload */ outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); /* copy payload */ payload = gst_rtp_buffer_get_payload (outbuf); gst_adapter_copy (adapter, payload, 0, payload_len); gst_adapter_flush (adapter, payload_len); /* set metadata */ gst_base_rtp_audio_payload_set_meta (baseaudiopayload, outbuf, payload_len, timestamp); ret = gst_basertppayload_push (basepayload, outbuf); } return ret; }
static GstFlowReturn gst_base_rtp_audio_payload_push_buffer (GstBaseRTPAudioPayload * baseaudiopayload, GstBuffer * buffer) { GstBaseRTPPayload *basepayload; GstBaseRTPAudioPayloadPrivate *priv; GstBuffer *outbuf; GstClockTime timestamp; guint8 *payload; guint payload_len; GstFlowReturn ret; priv = baseaudiopayload->priv; basepayload = GST_BASE_RTP_PAYLOAD (baseaudiopayload); payload_len = GST_BUFFER_SIZE (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); GST_DEBUG_OBJECT (baseaudiopayload, "Pushing %d bytes ts %" GST_TIME_FORMAT, payload_len, GST_TIME_ARGS (timestamp)); if (priv->buffer_list) { /* create just the RTP header buffer */ outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); } else { /* create buffer to hold the payload */ outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); } /* set metadata */ gst_base_rtp_audio_payload_set_meta (baseaudiopayload, outbuf, payload_len, timestamp); if (priv->buffer_list) { GstBufferList *list; GstBufferListIterator *it; list = gst_buffer_list_new (); it = gst_buffer_list_iterate (list); /* add both buffers to the buffer list */ gst_buffer_list_iterator_add_group (it); gst_buffer_list_iterator_add (it, outbuf); gst_buffer_list_iterator_add (it, buffer); gst_buffer_list_iterator_free (it); GST_DEBUG_OBJECT (baseaudiopayload, "Pushing list %p", list); ret = gst_basertppayload_push_list (basepayload, list); } else { /* copy payload */ payload = gst_rtp_buffer_get_payload (outbuf); memcpy (payload, GST_BUFFER_DATA (buffer), payload_len); gst_buffer_unref (buffer); GST_DEBUG_OBJECT (baseaudiopayload, "Pushing buffer %p", outbuf); ret = gst_basertppayload_push (basepayload, outbuf); } 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_BASE_RTP_PAYLOAD_MTU (rtpmp4gpay); while (avail > 0) { guint towrite; guint8 *payload; guint payload_len; guint packet_len; /* 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); /* copy payload */ payload = gst_rtp_buffer_get_payload (outbuf); /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+ * |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 (outbuf, avail <= payload_len); 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_basertppayload_push (GST_BASE_RTP_PAYLOAD (rtpmp4gpay), outbuf); avail -= payload_len; } return ret; }
static GstFlowReturn gst_rtp_speex_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRtpSPEEXPay *rtpspeexpay; guint size, payload_len; GstBuffer *outbuf; guint8 *payload, *data; GstClockTime timestamp, duration; GstFlowReturn ret; rtpspeexpay = GST_RTP_SPEEX_PAY (basepayload); size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); switch (rtpspeexpay->packet) { case 0: /* ident packet. We need to parse the headers to construct the RTP * properties. */ if (!gst_rtp_speex_pay_parse_ident (rtpspeexpay, data, size)) goto parse_error; ret = GST_FLOW_OK; goto done; case 1: /* comment packet, we ignore it */ ret = GST_FLOW_OK; goto done; default: /* other packets go in the payload */ break; } if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP)) { ret = GST_FLOW_OK; goto done; } timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); /* FIXME, only one SPEEX frame per RTP packet for now */ payload_len = size; outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); /* FIXME, assert for now */ g_assert (payload_len <= GST_BASE_RTP_PAYLOAD_MTU (rtpspeexpay)); /* copy timestamp and duration */ GST_BUFFER_TIMESTAMP (outbuf) = timestamp; GST_BUFFER_DURATION (outbuf) = duration; /* get payload */ payload = gst_rtp_buffer_get_payload (outbuf); /* copy data in payload */ memcpy (&payload[0], data, size); ret = gst_basertppayload_push (basepayload, outbuf); done: gst_buffer_unref (buffer); rtpspeexpay->packet++; return ret; /* ERRORS */ parse_error: { GST_ELEMENT_ERROR (rtpspeexpay, STREAM, DECODE, (NULL), ("Error parsing first identification packet.")); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } }
/* Get a DV frame, chop it up in pieces, and push the pieces to the RTP layer. */ static GstFlowReturn gst_rtp_dv_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRTPDVPay *rtpdvpay; guint max_payload_size; GstBuffer *outbuf; GstFlowReturn ret = GST_FLOW_OK; gint hdrlen; guint size; guint8 *data; guint8 *dest; guint filled; rtpdvpay = GST_RTP_DV_PAY (basepayload); hdrlen = gst_rtp_buffer_calc_header_len (0); /* DV frames are made up from a bunch of DIF blocks. DIF blocks are 80 bytes * each, and we should put an integral number of them in each RTP packet. * Therefore, we round the available room down to the nearest multiple of 80. * * The available room is just the packet MTU, minus the RTP header length. */ max_payload_size = ((GST_BASE_RTP_PAYLOAD_MTU (rtpdvpay) - hdrlen) / 80) * 80; /* The length of the buffer to transmit. */ size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); GST_DEBUG_OBJECT (rtpdvpay, "DV RTP payloader got buffer of %u bytes, splitting in %u byte " "payload fragments, at time %" GST_TIME_FORMAT, size, max_payload_size, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); if (!rtpdvpay->negotiated) { gst_dv_pay_negotiate (rtpdvpay, data, size); /* if we have not yet scanned the stream for its type, do so now */ rtpdvpay->negotiated = TRUE; } outbuf = NULL; dest = NULL; filled = 0; /* while we have a complete DIF chunks left */ while (size >= 80) { /* Allocate a new buffer, set the timestamp */ if (outbuf == NULL) { outbuf = gst_rtp_buffer_new_allocate (max_payload_size, 0, 0); GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer); dest = gst_rtp_buffer_get_payload (outbuf); filled = 0; } /* inspect the DIF chunk, if we don't need to include it, skip to the next one. */ if (include_dif (rtpdvpay, data)) { /* copy data in packet */ memcpy (dest, data, 80); dest += 80; filled += 80; } /* go to next dif chunk */ size -= 80; data += 80; /* push out the buffer if the next one would exceed the max packet size or * when we are pushing the last packet */ if (filled + 80 > max_payload_size || size < 80) { if (size < 160) { guint hlen; /* set marker */ gst_rtp_buffer_set_marker (outbuf, TRUE); /* shrink buffer to last packet */ hlen = gst_rtp_buffer_get_header_len (outbuf); gst_rtp_buffer_set_packet_len (outbuf, hlen + filled); } /* Push out the created piece, and check for errors. */ ret = gst_basertppayload_push (basepayload, outbuf); if (ret != GST_FLOW_OK) break; outbuf = NULL; } } gst_buffer_unref (buffer); return ret; }