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_mp2t_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buffer) { GstRTPMP2TPay *rtpmp2tpay; guint size, avail, packet_len; GstClockTime timestamp, duration; GstFlowReturn ret; rtpmp2tpay = GST_RTP_MP2T_PAY (basepayload); size = gst_buffer_get_size (buffer); timestamp = GST_BUFFER_PTS (buffer); duration = GST_BUFFER_DURATION (buffer); again: ret = GST_FLOW_OK; avail = gst_adapter_available (rtpmp2tpay->adapter); /* Initialize new RTP payload */ if (avail == 0) { rtpmp2tpay->first_ts = timestamp; rtpmp2tpay->duration = duration; } /* get packet length of previous data and this new data */ packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0); /* if this buffer is going to overflow the packet, flush what we have, * or if upstream is handing us several packets, to keep latency low */ if (!size || gst_rtp_base_payload_is_filled (basepayload, packet_len, rtpmp2tpay->duration + duration)) { ret = gst_rtp_mp2t_pay_flush (rtpmp2tpay); rtpmp2tpay->first_ts = timestamp; rtpmp2tpay->duration = duration; /* keep filling the payload */ } else { if (GST_CLOCK_TIME_IS_VALID (duration)) rtpmp2tpay->duration += duration; } /* copy buffer to adapter */ if (buffer) { gst_adapter_push (rtpmp2tpay->adapter, buffer); buffer = NULL; } if (size >= (188 * 2)) { size = 0; goto again; } 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_mp2t_pay_flush (GstRTPMP2TPay * rtpmp2tpay) { guint avail, mtu; GstFlowReturn ret = GST_FLOW_OK; GstBuffer *outbuf; avail = gst_adapter_available (rtpmp2tpay->adapter); mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp2tpay); while (avail > 0 && (ret == GST_FLOW_OK)) { guint towrite; guint payload_len; guint packet_len; GstBuffer *paybuf; /* this will be the total length 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, mtu); /* this is the payload length */ payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); payload_len -= payload_len % 188; /* need whole packets */ if (!payload_len) break; /* create buffer to hold the payload */ outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); /* get payload */ paybuf = gst_adapter_take_buffer_fast (rtpmp2tpay->adapter, payload_len); gst_rtp_copy_meta (GST_ELEMENT_CAST (rtpmp2tpay), outbuf, paybuf, 0); outbuf = gst_buffer_append (outbuf, paybuf); avail -= payload_len; GST_BUFFER_PTS (outbuf) = rtpmp2tpay->first_ts; GST_BUFFER_DURATION (outbuf) = rtpmp2tpay->duration; GST_DEBUG_OBJECT (rtpmp2tpay, "pushing buffer of size %u", (guint) gst_buffer_get_size (outbuf)); ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp2tpay), outbuf); } return ret; }
static GstFlowReturn gst_rtp_mpv_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buffer) { GstRTPMPVPay *rtpmpvpay; guint avail, packet_len; GstClockTime timestamp, duration; GstFlowReturn ret = GST_FLOW_OK; rtpmpvpay = GST_RTP_MPV_PAY (basepayload); timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); if (GST_BUFFER_IS_DISCONT (buffer)) { GST_DEBUG_OBJECT (rtpmpvpay, "DISCONT"); gst_rtp_mpv_pay_reset (rtpmpvpay); } avail = gst_adapter_available (rtpmpvpay->adapter); if (duration == -1) duration = 0; if (rtpmpvpay->first_ts == GST_CLOCK_TIME_NONE || avail == 0) rtpmpvpay->first_ts = timestamp; if (avail == 0) { rtpmpvpay->duration = duration; } else { rtpmpvpay->duration += duration; } gst_adapter_push (rtpmpvpay->adapter, buffer); avail = gst_adapter_available (rtpmpvpay->adapter); /* get packet length of previous data and this new data, * payload length includes a 4 byte MPEG video-specific header */ packet_len = gst_rtp_buffer_calc_packet_len (avail, 4, 0); GST_LOG_OBJECT (rtpmpvpay, "available %d, rtp packet length %d", avail, packet_len); if (gst_rtp_base_payload_is_filled (basepayload, packet_len, rtpmpvpay->duration)) { ret = gst_rtp_mpv_pay_flush (rtpmpvpay); } else { rtpmpvpay->first_ts = timestamp; } return ret; }
static GstFlowReturn gst_rtp_mpa_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buffer) { GstRtpMPAPay *rtpmpapay; GstFlowReturn ret; guint size, avail; guint packet_len; GstClockTime duration, timestamp; rtpmpapay = GST_RTP_MPA_PAY (basepayload); size = gst_buffer_get_size (buffer); duration = GST_BUFFER_DURATION (buffer); timestamp = GST_BUFFER_PTS (buffer); if (GST_BUFFER_IS_DISCONT (buffer)) { GST_DEBUG_OBJECT (rtpmpapay, "DISCONT"); gst_rtp_mpa_pay_reset (rtpmpapay); } avail = gst_adapter_available (rtpmpapay->adapter); /* get packet length of previous data and this new data, * payload length includes a 4 byte header */ packet_len = gst_rtp_buffer_calc_packet_len (4 + avail + size, 0, 0); /* if this buffer is going to overflow the packet, flush what we * have. */ if (gst_rtp_base_payload_is_filled (basepayload, packet_len, rtpmpapay->duration + duration)) { ret = gst_rtp_mpa_pay_flush (rtpmpapay); avail = 0; } else { ret = GST_FLOW_OK; } if (avail == 0) { GST_DEBUG_OBJECT (rtpmpapay, "first packet, save timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); rtpmpapay->first_ts = timestamp; rtpmpapay->duration = 0; } gst_adapter_push (rtpmpapay->adapter, buffer); rtpmpapay->duration = duration; return ret; }
static GstFlowReturn gst_rtp_mp2t_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRTPMP2TPay *rtpmp2tpay; guint size, avail, packet_len; GstClockTime timestamp, duration; GstFlowReturn ret; rtpmp2tpay = GST_RTP_MP2T_PAY (basepayload); size = GST_BUFFER_SIZE (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); ret = GST_FLOW_OK; avail = gst_adapter_available (rtpmp2tpay->adapter); /* Initialize new RTP payload */ if (avail == 0) { rtpmp2tpay->first_ts = timestamp; rtpmp2tpay->duration = duration; } /* get packet length of previous data and this new data, * payload length includes a 4 byte header */ packet_len = gst_rtp_buffer_calc_packet_len (4 + avail + size, 0, 0); /* if this buffer is going to overflow the packet, flush what we * have. */ if (gst_basertppayload_is_filled (basepayload, packet_len, rtpmp2tpay->duration + duration)) { ret = gst_rtp_mp2t_pay_flush (rtpmp2tpay); rtpmp2tpay->first_ts = timestamp; rtpmp2tpay->duration = duration; /* keep filling the payload */ } else { if (GST_CLOCK_TIME_IS_VALID (duration)) rtpmp2tpay->duration += duration; } /* copy buffer to adapter */ gst_adapter_push (rtpmp2tpay->adapter, buffer); return ret; }
static GstFlowReturn gst_rtp_mpa_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRtpMPAPay *rtpmpapay; GstFlowReturn ret; guint size, avail; guint packet_len; GstClockTime duration; rtpmpapay = GST_RTP_MPA_PAY (basepayload); size = GST_BUFFER_SIZE (buffer); duration = GST_BUFFER_DURATION (buffer); avail = gst_adapter_available (rtpmpapay->adapter); if (avail == 0) { rtpmpapay->first_ts = GST_BUFFER_TIMESTAMP (buffer); rtpmpapay->duration = 0; } /* get packet length of previous data and this new data, * payload length includes a 4 byte header */ packet_len = gst_rtp_buffer_calc_packet_len (4 + avail + size, 0, 0); /* if this buffer is going to overflow the packet, flush what we * have. */ if (gst_basertppayload_is_filled (basepayload, packet_len, rtpmpapay->duration + duration)) { ret = gst_rtp_mpa_pay_flush (rtpmpapay); rtpmpapay->first_ts = GST_BUFFER_TIMESTAMP (buffer); rtpmpapay->duration = 0; } else { ret = GST_FLOW_OK; } gst_adapter_push (rtpmpapay->adapter, buffer); rtpmpapay->duration += duration; return ret; }
static GstFlowReturn gst_rtp_mpv_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRTPMPVPay *rtpmpvpay; guint size, avail, packet_len; guint8 *data; GstClockTime timestamp, duration; GstFlowReturn ret; rtpmpvpay = GST_RTP_MPV_PAY (basepayload); size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); gst_adapter_push (rtpmpvpay->adapter, buffer); avail = gst_adapter_available (rtpmpvpay->adapter); /* Initialize new RTP payload */ if (avail == 0) { rtpmpvpay->first_ts = timestamp; rtpmpvpay->duration = duration; } /* get packet length of previous data and this new data, * payload length includes a 4 byte MPEG video-specific header */ packet_len = gst_rtp_buffer_calc_packet_len (4 + avail, 0, 0); if (gst_basertppayload_is_filled (basepayload, packet_len, rtpmpvpay->duration + duration)) { ret = gst_rtp_mpv_pay_flush (rtpmpvpay, timestamp, duration); } else { if (GST_CLOCK_TIME_IS_VALID (duration)) rtpmpvpay->duration += duration; ret = GST_FLOW_OK; } return ret; }
static GstFlowReturn gst_rtp_celt_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstFlowReturn ret; GstRtpCELTPay *rtpceltpay; guint size, payload_len; guint8 *data; GstClockTime duration, packet_dur; guint i, ssize, packet_len; rtpceltpay = GST_RTP_CELT_PAY (basepayload); ret = GST_FLOW_OK; size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); switch (rtpceltpay->packet) { case 0: /* ident packet. We need to parse the headers to construct the RTP * properties. */ if (!gst_rtp_celt_pay_parse_ident (rtpceltpay, data, size)) goto parse_error; goto done; case 1: /* comment packet, we ignore it */ goto done; default: /* other packets go in the payload */ break; } duration = GST_BUFFER_DURATION (buffer); GST_DEBUG_OBJECT (rtpceltpay, "got buffer of duration %" GST_TIME_FORMAT ", size %u", GST_TIME_ARGS (duration), size); /* calculate the size of the size field and the payload */ ssize = 1; for (i = size; i > 0xff; i -= 0xff) ssize++; GST_DEBUG_OBJECT (rtpceltpay, "bytes for size %u", ssize); /* calculate the size and duration of the packet */ payload_len = ssize + size + rtpceltpay->bytes + rtpceltpay->sbytes; packet_dur = rtpceltpay->qduration + duration; packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0); if (gst_basertppayload_is_filled (basepayload, packet_len, packet_dur)) { /* size or duration would overflow the packet, flush the queued data */ ret = gst_rtp_celt_pay_flush_queued (rtpceltpay); } /* queue the packet */ gst_rtp_celt_pay_add_queued (rtpceltpay, buffer, ssize, size, duration); done: rtpceltpay->packet++; return ret; /* ERRORS */ parse_error: { GST_ELEMENT_ERROR (rtpceltpay, STREAM, DECODE, (NULL), ("Error parsing first identification packet.")); return GST_FLOW_ERROR; } }
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_mpa_pay_flush (GstRtpMPAPay * rtpmpapay) { guint avail; GstBuffer *outbuf; GstFlowReturn ret; guint16 frag_offset; GstBufferList *list; /* 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; list = gst_buffer_list_new_sized (avail / (GST_RTP_BASE_PAYLOAD_MTU (rtpmpapay) - RTP_HEADER_LEN) + 1); frag_offset = 0; while (avail > 0) { guint towrite; guint8 *payload; guint payload_len; guint packet_len; GstRTPBuffer rtp = { NULL }; GstBuffer *paybuf; /* this will be the total length 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_RTP_BASE_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 (4, 0, 0); gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); payload_len -= 4; gst_rtp_buffer_set_payload_type (&rtp, 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 (&rtp); payload[0] = 0; payload[1] = 0; payload[2] = frag_offset >> 8; payload[3] = frag_offset & 0xff; avail -= payload_len; frag_offset += payload_len; if (avail == 0) gst_rtp_buffer_set_marker (&rtp, TRUE); gst_rtp_buffer_unmap (&rtp); paybuf = gst_adapter_take_buffer_fast (rtpmpapay->adapter, payload_len); gst_rtp_copy_meta (GST_ELEMENT_CAST (rtpmpapay), outbuf, paybuf, g_quark_from_static_string (GST_META_TAG_AUDIO_STR)); outbuf = gst_buffer_append (outbuf, paybuf); GST_BUFFER_PTS (outbuf) = rtpmpapay->first_ts; GST_BUFFER_DURATION (outbuf) = rtpmpapay->duration; gst_buffer_list_add (list, outbuf); } ret = gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmpapay), list); return ret; }
static GstFlowReturn gst_rtp_ac3_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buffer) { GstRtpAC3Pay *rtpac3pay; GstFlowReturn ret; gsize avail, left, NF; GstMapInfo map; guint8 *p; guint packet_len; GstClockTime duration, timestamp; rtpac3pay = GST_RTP_AC3_PAY (basepayload); gst_buffer_map (buffer, &map, GST_MAP_READ); duration = GST_BUFFER_DURATION (buffer); timestamp = GST_BUFFER_PTS (buffer); if (GST_BUFFER_IS_DISCONT (buffer)) { GST_DEBUG_OBJECT (rtpac3pay, "DISCONT"); gst_rtp_ac3_pay_reset (rtpac3pay); } /* count the amount of incomming packets */ NF = 0; left = map.size; p = map.data; while (TRUE) { guint bsid, fscod, frmsizecod, frame_size; if (left < 6) break; if (p[0] != 0x0b || p[1] != 0x77) break; bsid = p[5] >> 3; if (bsid > 8) break; frmsizecod = p[4] & 0x3f; fscod = p[4] >> 6; GST_DEBUG_OBJECT (rtpac3pay, "fscod %u, %u", fscod, frmsizecod); if (fscod >= 3 || frmsizecod >= 38) break; frame_size = frmsizecod_tbl[frmsizecod].frm_size[fscod] * 2; if (frame_size > left) break; NF++; GST_DEBUG_OBJECT (rtpac3pay, "found frame %" G_GSIZE_FORMAT " of size %u", NF, frame_size); p += frame_size; left -= frame_size; } gst_buffer_unmap (buffer, &map); if (NF == 0) goto no_frames; avail = gst_adapter_available (rtpac3pay->adapter); /* get packet length of previous data and this new data, * payload length includes a 4 byte header */ packet_len = gst_rtp_buffer_calc_packet_len (2 + avail + map.size, 0, 0); /* if this buffer is going to overflow the packet, flush what we * have. */ if (gst_rtp_base_payload_is_filled (basepayload, packet_len, rtpac3pay->duration + duration)) { ret = gst_rtp_ac3_pay_flush (rtpac3pay); avail = 0; } else { ret = GST_FLOW_OK; } if (avail == 0) { GST_DEBUG_OBJECT (rtpac3pay, "first packet, save timestamp %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp)); rtpac3pay->first_ts = timestamp; rtpac3pay->duration = 0; rtpac3pay->NF = 0; } gst_adapter_push (rtpac3pay->adapter, buffer); rtpac3pay->duration += duration; rtpac3pay->NF += NF; return ret; /* ERRORS */ no_frames: { GST_WARNING_OBJECT (rtpac3pay, "no valid AC3 frames found"); return GST_FLOW_OK; } }
static GstFlowReturn gst_rtp_g723_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf) { GstFlowReturn ret = GST_FLOW_OK; GstMapInfo map; guint8 HDR; GstRTPG723Pay *pay; GstClockTime packet_dur, timestamp; guint payload_len, packet_len; pay = GST_RTP_G723_PAY (payload); gst_buffer_map (buf, &map, GST_MAP_READ); timestamp = GST_BUFFER_PTS (buf); if (GST_BUFFER_IS_DISCONT (buf)) { /* flush everything on discont */ gst_adapter_clear (pay->adapter); pay->timestamp = GST_CLOCK_TIME_NONE; pay->duration = 0; pay->discont = TRUE; } /* should be one of these sizes */ if (map.size != 4 && map.size != 20 && map.size != 24) goto invalid_size; /* check size by looking at the header bits */ HDR = map.data[0] & 0x3; if (size_tab[HDR] != map.size) goto wrong_size; /* calculate packet size and duration */ payload_len = gst_adapter_available (pay->adapter) + map.size; packet_dur = pay->duration + G723_FRAME_DURATION; packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0); if (gst_rtp_base_payload_is_filled (payload, packet_len, packet_dur)) { /* size or duration would overflow the packet, flush the queued data */ ret = gst_rtp_g723_pay_flush (pay); } /* update timestamp, we keep the timestamp for the first packet in the adapter * but are able to calculate it from next packets. */ if (timestamp != GST_CLOCK_TIME_NONE && pay->timestamp == GST_CLOCK_TIME_NONE) { if (timestamp > pay->duration) pay->timestamp = timestamp - pay->duration; else pay->timestamp = 0; } gst_buffer_unmap (buf, &map); /* add packet to the queue */ gst_adapter_push (pay->adapter, buf); pay->duration = packet_dur; /* check if we can flush now */ if (pay->duration >= payload->min_ptime) { ret = gst_rtp_g723_pay_flush (pay); } return ret; /* WARNINGS */ invalid_size: { GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE, ("Invalid input buffer size"), ("Input size should be 4, 20 or 24, got %" G_GSIZE_FORMAT, map.size)); gst_buffer_unmap (buf, &map); gst_buffer_unref (buf); return GST_FLOW_OK; } wrong_size: { GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE, ("Wrong input buffer size"), ("Expected input buffer size %u but got %" G_GSIZE_FORMAT, size_tab[HDR], map.size)); gst_buffer_unmap (buf, &map); gst_buffer_unref (buf); return GST_FLOW_OK; } }
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_amr_pay_handle_buffer (GstRTPBasePayload * basepayload, GstBuffer * buffer) { GstRtpAMRPay *rtpamrpay; const gint *frame_size; GstFlowReturn ret; guint payload_len; GstMapInfo map; GstBuffer *outbuf; guint8 *payload, *ptr, *payload_amr; GstClockTime timestamp, duration; guint packet_len, mtu; gint i, num_packets, num_nonempty_packets; gint amr_len; gboolean sid = FALSE; GstRTPBuffer rtp = { NULL }; rtpamrpay = GST_RTP_AMR_PAY (basepayload); mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpamrpay); gst_buffer_map (buffer, &map, GST_MAP_READ); 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 %" G_GSIZE_FORMAT " bytes", map.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 < map.size; i++) { guint8 FT; gint fr_size; FT = (map.data[i] & 0x78) >> 3; fr_size = frame_size[FT]; GST_DEBUG_OBJECT (basepayload, "frame type %d, frame size %d", FT, fr_size); /* FIXME, we don't handle this yet.. */ if (fr_size <= 0) goto wrong_size; if (fr_size == 5) sid = TRUE; amr_len += fr_size; num_nonempty_packets++; num_packets++; i += fr_size; } if (amr_len > map.size) goto incomplete_frame; /* we need one extra byte for the CMR, the ToC is in the input * data */ payload_len = map.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); gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); /* copy timestamp */ GST_BUFFER_TIMESTAMP (outbuf) = timestamp; if (duration != GST_CLOCK_TIME_NONE) GST_BUFFER_DURATION (outbuf) = duration; else { GST_BUFFER_DURATION (outbuf) = num_packets * 20 * GST_MSECOND; } if (GST_BUFFER_IS_DISCONT (buffer)) { GST_DEBUG_OBJECT (basepayload, "discont, setting marker bit"); GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); gst_rtp_buffer_set_marker (&rtp, TRUE); gst_rtp_amr_pay_recalc_rtp_time (rtpamrpay, timestamp); } if (G_UNLIKELY (sid)) { gst_rtp_amr_pay_recalc_rtp_time (rtpamrpay, timestamp); } /* perfect rtptime */ if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (rtpamrpay->first_ts))) { rtpamrpay->first_ts = timestamp; rtpamrpay->first_rtp_time = rtpamrpay->next_rtp_time; } GST_BUFFER_OFFSET (outbuf) = rtpamrpay->next_rtp_time; rtpamrpay->next_rtp_time += (num_packets * 160) << (rtpamrpay->mode == GST_RTP_AMR_P_MODE_WB); /* get payload, this is now writable */ payload = gst_rtp_buffer_get_payload (&rtp); /* 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. */ ptr = map.data; 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 = (*ptr & 0x78) >> 3; fr_size = frame_size[FT]; if (i == num_packets) /* last packet, clear F flag */ payload[i] = *ptr & 0x7f; else /* set F flag */ payload[i] = *ptr | 0x80; memcpy (payload_amr, &ptr[1], fr_size); /* all sizes are > 0 since we checked for that above */ ptr += fr_size + 1; payload_amr += fr_size; } gst_buffer_unmap (buffer, &map); gst_buffer_unref (buffer); gst_rtp_buffer_unmap (&rtp); ret = gst_rtp_base_payload_push (basepayload, outbuf); return ret; /* ERRORS */ wrong_size: { GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, (NULL), ("received AMR frame with size <= 0")); gst_buffer_unmap (buffer, &map); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } incomplete_frame: { GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, (NULL), ("received incomplete AMR frames")); gst_buffer_unmap (buffer, &map); 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_unmap (buffer, &map); gst_buffer_unref (buffer); return GST_FLOW_ERROR; } }
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 }; GstBuffer *paybuf; /* 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 (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 */ /* marker only if the packet is complete */ gst_rtp_buffer_set_marker (&rtp, avail <= payload_len); gst_rtp_buffer_unmap (&rtp); paybuf = gst_adapter_take_buffer_fast (rtpmp4gpay->adapter, payload_len); outbuf = gst_buffer_append (outbuf, paybuf); 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; } if (rtpmp4gpay->discont) { GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); /* Only the first outputted buffer has the DISCONT flag */ rtpmp4gpay->discont = FALSE; } ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), outbuf); avail -= payload_len; } return ret; }
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; }
/* we expect buffers starting on startcodes. */ static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstBaseRTPPayload * basepayload, GstBuffer * buffer) { GstRtpMP4VPay *rtpmp4vpay; GstFlowReturn ret; guint size, avail; guint packet_len; guint8 *data; gboolean flush; gint strip; GstClockTime timestamp, duration; ret = GST_FLOW_OK; rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload); size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); avail = gst_adapter_available (rtpmp4vpay->adapter); if (duration == -1) duration = 0; /* empty buffer, take timestamp */ if (avail == 0) { rtpmp4vpay->first_timestamp = timestamp; rtpmp4vpay->duration = 0; } /* depay incomming data and see if we need to start a new RTP * packet */ flush = gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, data, size, &strip); if (strip) { /* strip off config if requested */ if (!rtpmp4vpay->send_config) { GstBuffer *subbuf; /* strip off header */ subbuf = gst_buffer_create_sub (buffer, strip, size - strip); GST_BUFFER_TIMESTAMP (subbuf) = timestamp; gst_buffer_unref (buffer); buffer = subbuf; size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); } } /* if we need to flush, do so now */ if (flush) { ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay); rtpmp4vpay->first_timestamp = timestamp; rtpmp4vpay->duration = 0; avail = 0; } /* get packet length of data and see if we exceeded MTU. */ packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0); if (gst_basertppayload_is_filled (basepayload, packet_len, rtpmp4vpay->duration + duration)) { ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay); rtpmp4vpay->first_timestamp = timestamp; rtpmp4vpay->duration = 0; } /* push new data */ gst_adapter_push (rtpmp4vpay->adapter, buffer); rtpmp4vpay->duration += duration; return ret; }
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; }