/**
 * gst_amr_parse_sink_setcaps:
 * @sinkpad: GstPad
 * @caps: GstCaps
 *
 * Returns: TRUE on success.
 */
static gboolean
gst_amr_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
{
    GstAmrParse *amrparse;
    GstStructure *structure;
    const gchar *name;

    amrparse = GST_AMR_PARSE (parse);
    structure = gst_caps_get_structure (caps, 0);
    name = gst_structure_get_name (structure);

    GST_DEBUG_OBJECT (amrparse, "setcaps: %s", name);

    if (!strncmp (name, "audio/x-amr-wb-sh", 17)) {
        amrparse->block_size = block_size_wb;
        amrparse->wide = 1;
    } else if (!strncmp (name, "audio/x-amr-nb-sh", 17)) {
        amrparse->block_size = block_size_nb;
        amrparse->wide = 0;
    } else {
        GST_WARNING ("Unknown caps");
        return FALSE;
    }

    amrparse->need_header = FALSE;
    gst_base_parse_set_frame_rate (GST_BASE_PARSE (amrparse), 50, 1, 2, 2);
    gst_amr_parse_set_src_caps (amrparse);
    return TRUE;
}
static GstFlowReturn
gst_amr_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
{
  GstAmrParse *amrparse = GST_AMR_PARSE (parse);

  if (!amrparse->sent_codec_tag) {
    GstTagList *taglist;
    GstCaps *caps;

    /* codec tag */
    caps = gst_pad_get_current_caps (GST_BASE_PARSE_SRC_PAD (parse));
    if (G_UNLIKELY (caps == NULL)) {
      if (GST_PAD_IS_FLUSHING (GST_BASE_PARSE_SRC_PAD (parse))) {
        GST_INFO_OBJECT (parse, "Src pad is flushing");
        return GST_FLOW_FLUSHING;
      } else {
        GST_INFO_OBJECT (parse, "Src pad is not negotiated!");
        return GST_FLOW_NOT_NEGOTIATED;
      }
    }

    taglist = gst_tag_list_new_empty ();
    gst_pb_utils_add_codec_description_to_tag_list (taglist,
        GST_TAG_AUDIO_CODEC, caps);
    gst_caps_unref (caps);

    gst_base_parse_merge_tags (parse, taglist, GST_TAG_MERGE_REPLACE);
    gst_tag_list_unref (taglist);

    /* also signals the end of first-frame processing */
    amrparse->sent_codec_tag = TRUE;
  }

  return GST_FLOW_OK;
}
/**
 * gst_amr_parse_stop:
 * @parse: #GstBaseParse.
 *
 * Implementation of "stop" vmethod in #GstBaseParse class.
 *
 * Returns: TRUE on success.
 */
static gboolean
gst_amr_parse_stop (GstBaseParse * parse)
{
  GstAmrParse *amrparse;

  amrparse = GST_AMR_PARSE (parse);
  GST_DEBUG ("stop");
  amrparse->need_header = TRUE;
  amrparse->header = 0;
  return TRUE;
}
/**
 * gst_amr_parse_start:
 * @parse: #GstBaseParse.
 *
 * Implementation of "start" vmethod in #GstBaseParse class.
 *
 * Returns: TRUE on success.
 */
static gboolean
gst_amr_parse_start (GstBaseParse * parse)
{
  GstAmrParse *amrparse;

  amrparse = GST_AMR_PARSE (parse);
  GST_DEBUG ("start");
  amrparse->need_header = TRUE;
  amrparse->header = 0;
  amrparse->sent_codec_tag = FALSE;
  return TRUE;
}
/**
 * gst_amr_parse_check_valid_frame:
 * @parse: #GstBaseParse.
 * @buffer: #GstBuffer.
 * @framesize: Output variable where the found frame size is put.
 * @skipsize: Output variable which tells how much data needs to be skipped
 *            until a frame header is found.
 *
 * Implementation of "check_valid_frame" vmethod in #GstBaseParse class.
 *
 * Returns: TRUE if the given data contains valid frame.
 */
gboolean
gst_amr_parse_check_valid_frame (GstBaseParse * parse,
                                 GstBaseParseFrame * frame, guint * framesize, gint * skipsize)
{
    GstBuffer *buffer;
    const guint8 *data;
    gint fsize, mode, dsize;
    GstAmrParse *amrparse;

    amrparse = GST_AMR_PARSE (parse);
    buffer = frame->buffer;
    data = GST_BUFFER_DATA (buffer);
    dsize = GST_BUFFER_SIZE (buffer);

    GST_LOG ("buffer: %d bytes", dsize);

    if (amrparse->need_header) {
        if (dsize >= AMR_MIME_HEADER_SIZE &&
                gst_amr_parse_parse_header (amrparse, data, skipsize)) {
            amrparse->need_header = FALSE;
            gst_base_parse_set_frame_rate (GST_BASE_PARSE (amrparse), 50, 1, 2, 2);
        } else {
            GST_WARNING ("media doesn't look like a AMR format");
        }
        /* We return FALSE, so this frame won't get pushed forward. Instead,
           the "skip" value is set, so next time we will receive a valid frame. */
        return FALSE;
    }

    /* Does this look like a possible frame header candidate? */
    if ((data[0] & 0x83) == 0) {
        /* Yep. Retrieve the frame size */
        mode = (data[0] >> 3) & 0x0F;
        fsize = amrparse->block_size[mode] + 1;     /* +1 for the header byte */

        /* We recognize this data as a valid frame when:
         *     - We are in sync. There is no need for extra checks then
         *     - We are in EOS. There might not be enough data to check next frame
         *     - Sync is lost, but the following data after this frame seem
         *       to contain a valid header as well (and there is enough data to
         *       perform this check)
         */
        if (fsize &&
                (!GST_BASE_PARSE_LOST_SYNC (parse) || GST_BASE_PARSE_DRAINING (parse)
                 || (dsize > fsize && (data[fsize] & 0x83) == 0))) {
            *framesize = fsize;
            return TRUE;
        }
    }
/**
 * gst_amr_parse_check_valid_frame:
 * @parse: #GstBaseParse.
 * @buffer: #GstBuffer.
 * @framesize: Output variable where the found frame size is put.
 * @skipsize: Output variable which tells how much data needs to be skipped
 *            until a frame header is found.
 *
 * Implementation of "check_valid_frame" vmethod in #GstBaseParse class.
 *
 * Returns: TRUE if the given data contains valid frame.
 */
static GstFlowReturn
gst_amr_parse_handle_frame (GstBaseParse * parse,
    GstBaseParseFrame * frame, gint * skipsize)
{
  GstBuffer *buffer;
  GstMapInfo map;
  gint fsize = 0, mode, dsize;
  GstAmrParse *amrparse;
  GstFlowReturn ret = GST_FLOW_OK;
  gboolean found = FALSE;

  amrparse = GST_AMR_PARSE (parse);
  buffer = frame->buffer;

  gst_buffer_map (buffer, &map, GST_MAP_READ);
  dsize = map.size;

  GST_LOG ("buffer: %d bytes", dsize);

  if (amrparse->need_header) {
    if (dsize >= AMR_MIME_HEADER_SIZE &&
        gst_amr_parse_parse_header (amrparse, map.data, skipsize)) {
      amrparse->need_header = FALSE;
      gst_base_parse_set_frame_rate (GST_BASE_PARSE (amrparse), 50, 1, 2, 2);
    } else {
      GST_WARNING ("media doesn't look like a AMR format");
    }
    /* We return FALSE, so this frame won't get pushed forward. Instead,
       the "skip" value is set, so next time we will receive a valid frame. */
    goto done;
  }

  *skipsize = 1;
  /* Does this look like a possible frame header candidate? */
  if ((map.data[0] & 0x83) == 0) {
    /* Yep. Retrieve the frame size */
    mode = (map.data[0] >> 3) & 0x0F;
    fsize = amrparse->block_size[mode] + 1;     /* +1 for the header byte */

    /* We recognize this data as a valid frame when:
     *     - We are in sync. There is no need for extra checks then
     *     - We are in EOS. There might not be enough data to check next frame
     *     - Sync is lost, but the following data after this frame seem
     *       to contain a valid header as well (and there is enough data to
     *       perform this check)
     */
    if (fsize) {
      *skipsize = 0;
      /* in sync, no further check */
      if (!GST_BASE_PARSE_LOST_SYNC (parse)) {
        found = TRUE;
      } else if (dsize > fsize) {
        /* enough data, check for next sync */
        if ((map.data[fsize] & 0x83) == 0)
          found = TRUE;
      } else if (GST_BASE_PARSE_DRAINING (parse)) {
        /* not enough, but draining, so ok */
        found = TRUE;
      }
    }
  }