Blame tests/check/pipelines/theoraenc.c

Packit 971217
/* GStreamer
Packit 971217
 *
Packit 971217
 * unit test for theoraenc
Packit 971217
 *
Packit 971217
 * Copyright (C) 2006 Andy Wingo <wingo at pobox.com>
Packit 971217
 *
Packit 971217
 * This library is free software; you can redistribute it and/or
Packit 971217
 * modify it under the terms of the GNU Library General Public
Packit 971217
 * License as published by the Free Software Foundation; either
Packit 971217
 * version 2 of the License, or (at your option) any later version.
Packit 971217
 *
Packit 971217
 * This library is distributed in the hope that it will be useful,
Packit 971217
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 971217
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 971217
 * Library General Public License for more details.
Packit 971217
 *
Packit 971217
 * You should have received a copy of the GNU Library General Public
Packit 971217
 * License along with this library; if not, write to the
Packit 971217
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 971217
 * Boston, MA 02110-1301, USA.
Packit 971217
 */
Packit 971217
#ifdef HAVE_CONFIG_H
Packit 971217
#include "config.h"
Packit 971217
#endif
Packit 971217
Packit 971217
#include <gst/check/gstcheck.h>
Packit 971217
#include <gst/check/gstbufferstraw.h>
Packit 971217
Packit 971217
#include <theora/theora.h>
Packit 971217
Packit 971217
#ifndef GST_DISABLE_PARSE
Packit 971217
Packit 971217
#define TIMESTAMP_OFFSET G_GINT64_CONSTANT(3249870963)
Packit 971217
#define FRAMERATE 10
Packit 971217
Packit 971217
/* I know all of these have a shift of 6 bits */
Packit 971217
#define GRANULEPOS_SHIFT 6
Packit 971217
Packit 971217
Packit 971217
#define check_buffer_is_header(buffer,is_header) \
Packit 971217
  fail_unless (GST_BUFFER_FLAG_IS_SET (buffer,   \
Packit 971217
          GST_BUFFER_FLAG_HEADER) == is_header, \
Packit 971217
      "GST_BUFFER_IN_CAPS is set to %d but expected %d", \
Packit 971217
      GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER), is_header)
Packit 971217
Packit 971217
#define check_buffer_timestamp(buffer,timestamp) \
Packit 971217
  fail_unless (GST_BUFFER_TIMESTAMP (buffer) == timestamp, \
Packit 971217
      "expected timestamp %" GST_TIME_FORMAT \
Packit 971217
      ", but got timestamp %" GST_TIME_FORMAT, \
Packit 971217
      GST_TIME_ARGS (timestamp), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)))
Packit 971217
Packit 971217
#define check_buffer_duration(buffer,duration) \
Packit 971217
  fail_unless (GST_BUFFER_DURATION (buffer) == duration, \
Packit 971217
      "expected duration %" GST_TIME_FORMAT \
Packit 971217
      ", but got duration %" GST_TIME_FORMAT, \
Packit 971217
      GST_TIME_ARGS (duration), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)))
Packit 971217
Packit 971217
static gboolean old_libtheora;
Packit 971217
Packit 971217
static void
Packit 971217
check_libtheora (void)
Packit 971217
{
Packit 971217
  old_libtheora = (theora_version_number () <= 0x00030200);
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
Packit 971217
{
Packit 971217
  GstClockTime clocktime;
Packit 971217
  int framecount;
Packit 971217
Packit 971217
  /* With old versions of libtheora, the granulepos represented the
Packit 971217
   * start time, not end time. Adapt for that. */
Packit 971217
  if (old_libtheora) {
Packit 971217
    if (granulepos >> GRANULEPOS_SHIFT)
Packit 971217
      granulepos -= 1 << GRANULEPOS_SHIFT;
Packit 971217
    else if (granulepos)
Packit 971217
      granulepos -= 1;
Packit 971217
  }
Packit 971217
Packit 971217
  fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
Packit 971217
      "expected granulepos %" G_GUINT64_FORMAT
Packit 971217
      ", but got granulepos %" G_GUINT64_FORMAT,
Packit 971217
      granulepos, GST_BUFFER_OFFSET_END (buffer));
Packit 971217
Packit 971217
  /* contrary to what we record as TIMESTAMP, we can use OFFSET to check
Packit 971217
   * the granulepos correctly here */
Packit 971217
  framecount = GST_BUFFER_OFFSET_END (buffer);
Packit 971217
  framecount = granulepos >> GRANULEPOS_SHIFT;
Packit 971217
  framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1);
Packit 971217
  clocktime = gst_util_uint64_scale (framecount, GST_SECOND, FRAMERATE);
Packit 971217
Packit 971217
  fail_unless (clocktime == GST_BUFFER_OFFSET (buffer),
Packit 971217
      "expected OFFSET set to clocktime %" GST_TIME_FORMAT
Packit 971217
      ", but got %" GST_TIME_FORMAT,
Packit 971217
      GST_TIME_ARGS (clocktime), GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)));
Packit 971217
}
Packit 971217
Packit 971217
/* this check is here to check that the granulepos we derive from the
Packit 971217
   timestamp is about correct. This is "about correct" because you can't
Packit 971217
   precisely go from timestamp to granulepos due to the downward-rounding
Packit 971217
   characteristics of gst_util_uint64_scale, so you check if granulepos is
Packit 971217
   equal to the number, or the number plus one. */
Packit 971217
/* should be from_endtime, but theora's granulepos mapping is "special" */
Packit 971217
static void
Packit 971217
check_buffer_granulepos_from_starttime (GstBuffer * buffer,
Packit 971217
    GstClockTime starttime)
Packit 971217
{
Packit 971217
  gint64 granulepos, expected, framecount;
Packit 971217
Packit 971217
  granulepos = GST_BUFFER_OFFSET_END (buffer);
Packit 971217
  /* Now convert to 'granulepos for start time', depending on libtheora 
Packit 971217
   * version */
Packit 971217
  if (!old_libtheora) {
Packit 971217
    if (granulepos & ((1 << GRANULEPOS_SHIFT) - 1))
Packit 971217
      granulepos -= 1;
Packit 971217
    else if (granulepos)
Packit 971217
      granulepos -= 1 << GRANULEPOS_SHIFT;
Packit 971217
  }
Packit 971217
Packit 971217
  framecount = granulepos >> GRANULEPOS_SHIFT;
Packit 971217
  framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1);
Packit 971217
  expected = gst_util_uint64_scale (starttime, FRAMERATE, GST_SECOND);
Packit 971217
Packit 971217
  fail_unless (framecount == expected || framecount == expected + 1,
Packit 971217
      "expected frame count %" G_GUINT64_FORMAT
Packit 971217
      " or %" G_GUINT64_FORMAT
Packit 971217
      ", but got frame count %" G_GUINT64_FORMAT,
Packit 971217
      expected, expected + 1, framecount);
Packit 971217
}
Packit 971217
Packit 971217
GST_START_TEST (test_granulepos_offset)
Packit 971217
{
Packit 971217
  GstElement *bin;
Packit 971217
  GstPad *pad;
Packit 971217
  gchar *pipe_str;
Packit 971217
  GstBuffer *buffer;
Packit 971217
  GError *error = NULL;
Packit 971217
Packit 971217
  pipe_str = g_strdup_printf ("videotestsrc timestamp-offset=%" G_GUINT64_FORMAT
Packit 971217
      " num-buffers=10 ! video/x-raw,format=(string)I420,framerate=10/1"
Packit 971217
      " ! theoraenc ! fakesink name=fs0", TIMESTAMP_OFFSET);
Packit 971217
Packit 971217
  bin = gst_parse_launch (pipe_str, &error);
Packit 971217
  fail_unless (bin != NULL, "Error parsing pipeline: %s",
Packit 971217
      error ? error->message : "(invalid error)");
Packit 971217
  g_free (pipe_str);
Packit 971217
Packit 971217
  /* get the pad */
Packit 971217
  {
Packit 971217
    GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fs0");
Packit 971217
Packit 971217
    fail_unless (sink != NULL, "Could not get fakesink out of bin");
Packit 971217
    pad = gst_element_get_static_pad (sink, "sink");
Packit 971217
    fail_unless (pad != NULL, "Could not get pad out of fakesink");
Packit 971217
    gst_object_unref (sink);
Packit 971217
  }
Packit 971217
Packit 971217
  gst_buffer_straw_start_pipeline (bin, pad);
Packit 971217
Packit 971217
  /* header packets should have timestamp == NONE, granulepos 0, IN_CAPS */
Packit 971217
  buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
  check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_granulepos (buffer, 0);
Packit 971217
  check_buffer_is_header (buffer, TRUE);
Packit 971217
  gst_buffer_unref (buffer);
Packit 971217
Packit 971217
  buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
  check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_granulepos (buffer, 0);
Packit 971217
  check_buffer_is_header (buffer, TRUE);
Packit 971217
  gst_buffer_unref (buffer);
Packit 971217
Packit 971217
  buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
  check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_granulepos (buffer, 0);
Packit 971217
  check_buffer_is_header (buffer, TRUE);
Packit 971217
  gst_buffer_unref (buffer);
Packit 971217
Packit 971217
  {
Packit 971217
    GstClockTime next_timestamp;
Packit 971217
    gint64 last_granulepos;
Packit 971217
Packit 971217
    /* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
Packit 971217
     * match the timestamp of the end of the last sample in the output buffer.
Packit 971217
     * Note that one cannot go timestamp->granulepos->timestamp and get the
Packit 971217
     * same value due to loss of precision with granulepos. theoraenc does
Packit 971217
     * take care to timestamp correctly based on the offset of the input data
Packit 971217
     * however, so it does do sub-granulepos timestamping. */
Packit 971217
    buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
    last_granulepos = GST_BUFFER_OFFSET_END (buffer);
Packit 971217
    check_buffer_timestamp (buffer, TIMESTAMP_OFFSET);
Packit 971217
    /* don't really have a good way of checking duration... */
Packit 971217
    check_buffer_granulepos_from_starttime (buffer, TIMESTAMP_OFFSET);
Packit 971217
    check_buffer_is_header (buffer, FALSE);
Packit 971217
Packit 971217
    next_timestamp = TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer);
Packit 971217
Packit 971217
    gst_buffer_unref (buffer);
Packit 971217
Packit 971217
    /* check continuity with the next buffer */
Packit 971217
    buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
    check_buffer_timestamp (buffer, next_timestamp);
Packit 971217
    check_buffer_duration (buffer,
Packit 971217
        gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
Packit 971217
            FRAMERATE)
Packit 971217
        - gst_util_uint64_scale (last_granulepos, GST_SECOND, FRAMERATE));
Packit 971217
    check_buffer_granulepos_from_starttime (buffer, next_timestamp);
Packit 971217
    check_buffer_is_header (buffer, FALSE);
Packit 971217
Packit 971217
    gst_buffer_unref (buffer);
Packit 971217
  }
Packit 971217
Packit 971217
  gst_buffer_straw_stop_pipeline (bin, pad);
Packit 971217
Packit 971217
  gst_object_unref (pad);
Packit 971217
  gst_object_unref (bin);
Packit 971217
}
Packit 971217
Packit 971217
GST_END_TEST;
Packit 971217
Packit 971217
GST_START_TEST (test_continuity)
Packit 971217
{
Packit 971217
  GstElement *bin;
Packit 971217
  GstPad *pad;
Packit 971217
  gchar *pipe_str;
Packit 971217
  GstBuffer *buffer;
Packit 971217
  GError *error = NULL;
Packit 971217
Packit 971217
  pipe_str = g_strdup_printf ("videotestsrc num-buffers=10"
Packit 971217
      " ! video/x-raw,format=(string)I420,framerate=10/1"
Packit 971217
      " ! theoraenc ! fakesink name=fs0");
Packit 971217
Packit 971217
  bin = gst_parse_launch (pipe_str, &error);
Packit 971217
  fail_unless (bin != NULL, "Error parsing pipeline: %s",
Packit 971217
      error ? error->message : "(invalid error)");
Packit 971217
  g_free (pipe_str);
Packit 971217
Packit 971217
  /* get the pad */
Packit 971217
  {
Packit 971217
    GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fs0");
Packit 971217
Packit 971217
    fail_unless (sink != NULL, "Could not get fakesink out of bin");
Packit 971217
    pad = gst_element_get_static_pad (sink, "sink");
Packit 971217
    fail_unless (pad != NULL, "Could not get pad out of fakesink");
Packit 971217
    gst_object_unref (sink);
Packit 971217
  }
Packit 971217
Packit 971217
  gst_buffer_straw_start_pipeline (bin, pad);
Packit 971217
Packit 971217
  /* header packets should have timestamp == NONE, granulepos 0 */
Packit 971217
  buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
  check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_granulepos (buffer, 0);
Packit 971217
  check_buffer_is_header (buffer, TRUE);
Packit 971217
  gst_buffer_unref (buffer);
Packit 971217
Packit 971217
  buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
  check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_granulepos (buffer, 0);
Packit 971217
  check_buffer_is_header (buffer, TRUE);
Packit 971217
  gst_buffer_unref (buffer);
Packit 971217
Packit 971217
  buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
  check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
Packit 971217
  check_buffer_granulepos (buffer, 0);
Packit 971217
  check_buffer_is_header (buffer, TRUE);
Packit 971217
  gst_buffer_unref (buffer);
Packit 971217
Packit 971217
  {
Packit 971217
    GstClockTime next_timestamp;
Packit 971217
Packit 971217
    /* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
Packit 971217
     * match the timestamp of the end of the last sample in the output buffer.
Packit 971217
     * Note that one cannot go timestamp->granulepos->timestamp and get the
Packit 971217
     * same value due to loss of precision with granulepos. theoraenc does
Packit 971217
     * take care to timestamp correctly based on the offset of the input data
Packit 971217
     * however, so it does do sub-granulepos timestamping. */
Packit 971217
    buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
    check_buffer_timestamp (buffer, 0);
Packit 971217
    /* plain division because I know the answer is exact */
Packit 971217
    check_buffer_duration (buffer, GST_SECOND / 10);
Packit 971217
    check_buffer_granulepos (buffer, 1 << GRANULEPOS_SHIFT);
Packit 971217
    check_buffer_is_header (buffer, FALSE);
Packit 971217
Packit 971217
    next_timestamp = GST_BUFFER_DURATION (buffer);
Packit 971217
Packit 971217
    gst_buffer_unref (buffer);
Packit 971217
Packit 971217
    /* check continuity with the next buffer */
Packit 971217
    buffer = gst_buffer_straw_get_buffer (bin, pad);
Packit 971217
    check_buffer_timestamp (buffer, next_timestamp);
Packit 971217
    check_buffer_duration (buffer, GST_SECOND / 10);
Packit 971217
    check_buffer_granulepos (buffer, (1 << GRANULEPOS_SHIFT) | 1);
Packit 971217
    check_buffer_is_header (buffer, FALSE);
Packit 971217
Packit 971217
    gst_buffer_unref (buffer);
Packit 971217
  }
Packit 971217
Packit 971217
  gst_buffer_straw_stop_pipeline (bin, pad);
Packit 971217
Packit 971217
  gst_object_unref (pad);
Packit 971217
  gst_object_unref (bin);
Packit 971217
}
Packit 971217
Packit 971217
GST_END_TEST;
Packit 971217
Packit 971217
#endif /* #ifndef GST_DISABLE_PARSE */
Packit 971217
Packit 971217
static Suite *
Packit 971217
theoraenc_suite (void)
Packit 971217
{
Packit 971217
  Suite *s = suite_create ("theoraenc");
Packit 971217
  TCase *tc_chain = tcase_create ("general");
Packit 971217
Packit 971217
  suite_add_tcase (s, tc_chain);
Packit 971217
Packit 971217
  check_libtheora ();
Packit 971217
Packit 971217
#ifndef GST_DISABLE_PARSE
Packit 971217
  tcase_add_test (tc_chain, test_granulepos_offset);
Packit 971217
  tcase_add_test (tc_chain, test_continuity);
Packit 971217
#endif
Packit 971217
Packit 971217
  return s;
Packit 971217
}
Packit 971217
Packit 971217
GST_CHECK_MAIN (theoraenc);