static gboolean gst_rtp_amr_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) { GstRtpAMRPay *rtpamrpay; gboolean res; const GstStructure *s; const gchar *str; rtpamrpay = GST_RTP_AMR_PAY (basepayload); /* figure out the mode Narrow or Wideband */ s = gst_caps_get_structure (caps, 0); if ((str = gst_structure_get_name (s))) { if (strcmp (str, "audio/AMR") == 0) rtpamrpay->mode = GST_RTP_AMR_P_MODE_NB; else if (strcmp (str, "audio/AMR-WB") == 0) rtpamrpay->mode = GST_RTP_AMR_P_MODE_WB; else goto wrong_type; } else goto wrong_type; if (rtpamrpay->mode == GST_RTP_AMR_P_MODE_NB) gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "AMR", 8000); else gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "AMR-WB", 16000); res = gst_rtp_base_payload_set_outcaps (basepayload, "encoding-params", G_TYPE_STRING, "1", "octet-align", G_TYPE_STRING, "1", /* don't set the defaults * * "crc", G_TYPE_STRING, "0", * "robust-sorting", G_TYPE_STRING, "0", * "interleaving", G_TYPE_STRING, "0", */ NULL); return res; /* ERRORS */ wrong_type: { GST_ERROR_OBJECT (rtpamrpay, "unsupported media type '%s'", GST_STR_NULL (str)); return FALSE; } }
static GstStateChangeReturn gst_rtp_amr_pay_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; /* handle upwards state changes here */ switch (transition) { default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); /* handle downwards state changes */ switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: gst_rtp_amr_pay_reset (GST_RTP_AMR_PAY (element)); break; default: break; } return ret; }
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; } }