gstoggmux.c \
gstogmparse.c \
gstoggaviparse.c \
- gstoggparse.c
+ gstoggparse.c \
+ gstoggstream.c \
+ gstoggstream.h \
+ dirac_parse.c \
+ dirac_parse.h \
+ vorbis_parse.c
noinst_HEADERS = \
gstoggdemux.h gstoggmux.h
--- /dev/null
+
+#include "dirac_parse.h"
+#include <string.h>
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+
+typedef struct _Unpack Unpack;
+
+struct _Unpack
+{
+ unsigned char *data;
+ int n_bits_left;
+ int index;
+ int guard_bit;
+};
+
+static void schro_unpack_init_with_data (Unpack * unpack, unsigned char *data,
+ int n_bytes, unsigned int guard_bit);
+
+static unsigned int schro_unpack_decode_bit (Unpack * unpack);
+static unsigned int schro_unpack_decode_uint (Unpack * unpack);
+
+
+void schro_video_format_set_std_video_format (DiracSequenceHeader * format,
+ int index);
+void schro_video_format_set_std_frame_rate (DiracSequenceHeader * format,
+ int index);
+void schro_video_format_set_std_aspect_ratio (DiracSequenceHeader * format,
+ int index);
+void schro_video_format_set_std_signal_range (DiracSequenceHeader * format,
+ int index);
+void schro_video_format_set_std_colour_spec (DiracSequenceHeader * format,
+ int index);
+
+
+
+
+int
+dirac_sequence_header_parse (DiracSequenceHeader * header,
+ unsigned char *data, int n_bytes)
+{
+ int bit;
+ int index;
+ Unpack _unpack;
+ Unpack *unpack = &_unpack;
+ int major_version;
+ int minor_version;
+ int profile;
+ int level;
+
+ memset (header, 0, sizeof (*header));
+
+ schro_unpack_init_with_data (unpack, data, n_bytes, 1);
+
+ /* parse parameters */
+ major_version = schro_unpack_decode_uint (unpack);
+ minor_version = schro_unpack_decode_uint (unpack);
+ profile = schro_unpack_decode_uint (unpack);
+ level = schro_unpack_decode_uint (unpack);
+
+ /* base video header */
+ index = schro_unpack_decode_uint (unpack);
+ schro_video_format_set_std_video_format (header, index);
+
+ header->major_version = major_version;
+ header->minor_version = minor_version;
+ header->profile = profile;
+ header->level = level;
+
+ /* source parameters */
+ /* frame dimensions */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ header->width = schro_unpack_decode_uint (unpack);
+ header->height = schro_unpack_decode_uint (unpack);
+ }
+
+ /* chroma header */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ header->chroma_format = schro_unpack_decode_uint (unpack);
+ }
+
+ /* scan header */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ header->interlaced = schro_unpack_decode_bit (unpack);
+ if (header->interlaced) {
+ header->top_field_first = schro_unpack_decode_bit (unpack);
+ }
+ }
+
+ /* frame rate */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ index = schro_unpack_decode_uint (unpack);
+ if (index == 0) {
+ header->frame_rate_numerator = schro_unpack_decode_uint (unpack);
+ header->frame_rate_denominator = schro_unpack_decode_uint (unpack);
+ } else {
+ schro_video_format_set_std_frame_rate (header, index);
+ }
+ }
+
+ /* aspect ratio */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ index = schro_unpack_decode_uint (unpack);
+ if (index == 0) {
+ header->aspect_ratio_numerator = schro_unpack_decode_uint (unpack);
+ header->aspect_ratio_denominator = schro_unpack_decode_uint (unpack);
+ } else {
+ schro_video_format_set_std_aspect_ratio (header, index);
+ }
+ }
+
+ /* clean area */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ header->clean_width = schro_unpack_decode_uint (unpack);
+ header->clean_height = schro_unpack_decode_uint (unpack);
+ header->left_offset = schro_unpack_decode_uint (unpack);
+ header->top_offset = schro_unpack_decode_uint (unpack);
+ }
+
+ /* signal range */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ index = schro_unpack_decode_uint (unpack);
+ if (index == 0) {
+ header->luma_offset = schro_unpack_decode_uint (unpack);
+ header->luma_excursion = schro_unpack_decode_uint (unpack);
+ header->chroma_offset = schro_unpack_decode_uint (unpack);
+ header->chroma_excursion = schro_unpack_decode_uint (unpack);
+ } else {
+ schro_video_format_set_std_signal_range (header, index);
+ }
+ }
+
+ /* colour spec */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ index = schro_unpack_decode_uint (unpack);
+ schro_video_format_set_std_colour_spec (header, index);
+ if (index == 0) {
+ /* colour primaries */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ header->colour_primaries = schro_unpack_decode_uint (unpack);
+ }
+ /* colour matrix */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ header->colour_matrix = schro_unpack_decode_uint (unpack);
+ }
+ /* transfer function */
+ bit = schro_unpack_decode_bit (unpack);
+ if (bit) {
+ header->transfer_function = schro_unpack_decode_uint (unpack);
+ }
+ }
+ }
+
+ header->interlaced_coding = schro_unpack_decode_uint (unpack);
+
+ return 1;
+}
+
+/* standard stuff */
+
+static DiracSequenceHeader schro_video_formats[] = {
+ {0, 0, 0, 0,
+ 0, /* custom */
+ 640, 480, SCHRO_CHROMA_420,
+ FALSE, FALSE,
+ 24000, 1001, 1, 1,
+ 640, 480, 0, 0,
+ 0, 255, 128, 255,
+ 0, 0, 0},
+ {0, 0, 0, 0,
+ 1, /* QSIF525 */
+ 176, 120, SCHRO_CHROMA_420,
+ FALSE, FALSE,
+ 15000, 1001, 10, 11,
+ 176, 120, 0, 0,
+ 0, 255, 128, 255,
+ 1, 1, 0},
+ {0, 0, 0, 0,
+ 2, /* QCIF */
+ 176, 144, SCHRO_CHROMA_420,
+ FALSE, TRUE,
+ 25, 2, 12, 11,
+ 176, 144, 0, 0,
+ 0, 255, 128, 255,
+ 2, 1, 0},
+ {0, 0, 0, 0,
+ 3, /* SIF525 */
+ 352, 240, SCHRO_CHROMA_420,
+ FALSE, FALSE,
+ 15000, 1001, 10, 11,
+ 352, 240, 0, 0,
+ 0, 255, 128, 255,
+ 1, 1, 0},
+ {0, 0, 0, 0,
+ 4, /* CIF */
+ 352, 288, SCHRO_CHROMA_420,
+ FALSE, TRUE,
+ 25, 2, 12, 11,
+ 352, 288, 0, 0,
+ 0, 255, 128, 255,
+ 2, 1, 0},
+ {0, 0, 0, 0,
+ 5, /* 4SIF525 */
+ 704, 480, SCHRO_CHROMA_420,
+ FALSE, FALSE,
+ 15000, 1001, 10, 11,
+ 704, 480, 0, 0,
+ 0, 255, 128, 255,
+ 1, 1, 0},
+ {0, 0, 0, 0,
+ 6, /* 4CIF */
+ 704, 576, SCHRO_CHROMA_420,
+ FALSE, TRUE,
+ 25, 2, 12, 11,
+ 704, 576, 0, 0,
+ 0, 255, 128, 255,
+ 2, 1, 0},
+ {0, 0, 0, 0,
+ 7, /* SD480I-60 */
+ 720, 480, SCHRO_CHROMA_422,
+ TRUE, FALSE,
+ 30000, 1001, 10, 11,
+ 704, 480, 8, 0,
+ 64, 876, 512, 896,
+ 1, 1, 0},
+ {0, 0, 0, 0,
+ 8, /* SD576I-50 */
+ 720, 576, SCHRO_CHROMA_422,
+ TRUE, TRUE,
+ 25, 1, 12, 11,
+ 704, 576, 8, 0,
+ 64, 876, 512, 896,
+ 2, 1, 0},
+ {0, 0, 0, 0,
+ 9, /* HD720P-60 */
+ 1280, 720, SCHRO_CHROMA_422,
+ FALSE, TRUE,
+ 60000, 1001, 1, 1,
+ 1280, 720, 0, 0,
+ 64, 876, 512, 896,
+ 0, 0, 0},
+ {0, 0, 0, 0,
+ 10, /* HD720P-50 */
+ 1280, 720, SCHRO_CHROMA_422,
+ FALSE, TRUE,
+ 50, 1, 1, 1,
+ 1280, 720, 0, 0,
+ 64, 876, 512, 896,
+ 0, 0, 0},
+ {0, 0, 0, 0,
+ 11, /* HD1080I-60 */
+ 1920, 1080, SCHRO_CHROMA_422,
+ TRUE, TRUE,
+ 30000, 1001, 1, 1,
+ 1920, 1080, 0, 0,
+ 64, 876, 512, 896,
+ 0, 0, 0},
+ {0, 0, 0, 0,
+ 12, /* HD1080I-50 */
+ 1920, 1080, SCHRO_CHROMA_422,
+ TRUE, TRUE,
+ 25, 1, 1, 1,
+ 1920, 1080, 0, 0,
+ 64, 876, 512, 896,
+ 0, 0, 0},
+ {0, 0, 0, 0,
+ 13, /* HD1080P-60 */
+ 1920, 1080, SCHRO_CHROMA_422,
+ FALSE, TRUE,
+ 60000, 1001, 1, 1,
+ 1920, 1080, 0, 0,
+ 64, 876, 512, 896,
+ 0, 0, 0},
+ {0, 0, 0, 0,
+ 14, /* HD1080P-50 */
+ 1920, 1080, SCHRO_CHROMA_422,
+ FALSE, TRUE,
+ 50, 1, 1, 1,
+ 1920, 1080, 0, 0,
+ 64, 876, 512, 896,
+ 0, 0, 0},
+ {0, 0, 0, 0,
+ 15, /* DC2K */
+ 2048, 1080, SCHRO_CHROMA_444,
+ FALSE, TRUE,
+ 24, 1, 1, 1,
+ 2048, 1080, 0, 0,
+ 256, 3504, 2048, 3584,
+ 3, 0, 0},
+ {0, 0, 0, 0,
+ 16, /* DC4K */
+ 4096, 2160, SCHRO_CHROMA_444,
+ FALSE, TRUE,
+ 24, 1, 1, 1,
+ 2048, 1536, 0, 0,
+ 256, 3504, 2048, 3584,
+ 3, 0, 0},
+};
+
+void
+schro_video_format_set_std_video_format (DiracSequenceHeader * format,
+ int index)
+{
+
+ if (index < 0 || index >= ARRAY_SIZE (schro_video_formats)) {
+ return;
+ }
+
+ memcpy (format, schro_video_formats + index, sizeof (DiracSequenceHeader));
+}
+
+typedef struct _SchroFrameRate SchroFrameRate;
+struct _SchroFrameRate
+{
+ int numerator;
+ int denominator;
+};
+
+static SchroFrameRate schro_frame_rates[] = {
+ {0, 0},
+ {24000, 1001},
+ {24, 1},
+ {25, 1},
+ {30000, 1001},
+ {30, 1},
+ {50, 1},
+ {60000, 1001},
+ {60, 1},
+ {15000, 1001},
+ {25, 2}
+};
+
+void
+schro_video_format_set_std_frame_rate (DiracSequenceHeader * format, int index)
+{
+ if (index < 1 || index >= ARRAY_SIZE (schro_frame_rates)) {
+ return;
+ }
+
+ format->frame_rate_numerator = schro_frame_rates[index].numerator;
+ format->frame_rate_denominator = schro_frame_rates[index].denominator;
+}
+
+typedef struct _SchroPixelAspectRatio SchroPixelAspectRatio;
+struct _SchroPixelAspectRatio
+{
+ int numerator;
+ int denominator;
+};
+
+static const SchroPixelAspectRatio schro_aspect_ratios[] = {
+ {0, 0},
+ {1, 1},
+ {10, 11},
+ {12, 11},
+ {40, 33},
+ {16, 11},
+ {4, 3}
+};
+
+void
+schro_video_format_set_std_aspect_ratio (DiracSequenceHeader * format,
+ int index)
+{
+ if (index < 1 || index >= ARRAY_SIZE (schro_aspect_ratios)) {
+ return;
+ }
+
+ format->aspect_ratio_numerator = schro_aspect_ratios[index].numerator;
+ format->aspect_ratio_denominator = schro_aspect_ratios[index].denominator;
+
+}
+
+typedef struct _SchroSignalRangeStruct SchroSignalRangeStruct;
+struct _SchroSignalRangeStruct
+{
+ int luma_offset;
+ int luma_excursion;
+ int chroma_offset;
+ int chroma_excursion;
+};
+
+static const SchroSignalRangeStruct schro_signal_ranges[] = {
+ {0, 0, 0, 0},
+ {0, 255, 128, 255},
+ {16, 219, 128, 224},
+ {64, 876, 512, 896},
+ {256, 3504, 2048, 3584}
+};
+
+void
+schro_video_format_set_std_signal_range (DiracSequenceHeader * format, int i)
+{
+ if (i < 1 || i >= ARRAY_SIZE (schro_signal_ranges)) {
+ return;
+ }
+
+ format->luma_offset = schro_signal_ranges[i].luma_offset;
+ format->luma_excursion = schro_signal_ranges[i].luma_excursion;
+ format->chroma_offset = schro_signal_ranges[i].chroma_offset;
+ format->chroma_excursion = schro_signal_ranges[i].chroma_excursion;
+}
+
+typedef struct _SchroColourSpecStruct SchroColourSpecStruct;
+struct _SchroColourSpecStruct
+{
+ int colour_primaries;
+ int colour_matrix;
+ int transfer_function;
+};
+
+static const SchroColourSpecStruct schro_colour_specs[] = {
+ { /* Custom */
+ SCHRO_COLOUR_PRIMARY_HDTV,
+ SCHRO_COLOUR_MATRIX_HDTV,
+ SCHRO_TRANSFER_CHAR_TV_GAMMA},
+ { /* SDTV 525 */
+ SCHRO_COLOUR_PRIMARY_SDTV_525,
+ SCHRO_COLOUR_MATRIX_SDTV,
+ SCHRO_TRANSFER_CHAR_TV_GAMMA},
+ { /* SDTV 625 */
+ SCHRO_COLOUR_PRIMARY_SDTV_625,
+ SCHRO_COLOUR_MATRIX_SDTV,
+ SCHRO_TRANSFER_CHAR_TV_GAMMA},
+ { /* HDTV */
+ SCHRO_COLOUR_PRIMARY_HDTV,
+ SCHRO_COLOUR_MATRIX_HDTV,
+ SCHRO_TRANSFER_CHAR_TV_GAMMA},
+ { /* Cinema */
+ SCHRO_COLOUR_PRIMARY_CINEMA,
+ SCHRO_COLOUR_MATRIX_HDTV,
+ SCHRO_TRANSFER_CHAR_TV_GAMMA}
+};
+
+void
+schro_video_format_set_std_colour_spec (DiracSequenceHeader * format, int i)
+{
+ if (i < 0 || i >= ARRAY_SIZE (schro_colour_specs)) {
+ return;
+ }
+
+ format->colour_primaries = schro_colour_specs[i].colour_primaries;
+ format->colour_matrix = schro_colour_specs[i].colour_matrix;
+ format->transfer_function = schro_colour_specs[i].transfer_function;
+}
+
+
+/* unpack */
+
+static void
+schro_unpack_init_with_data (Unpack * unpack, unsigned char *data,
+ int n_bytes, unsigned int guard_bit)
+{
+ memset (unpack, 0, sizeof (Unpack));
+
+ unpack->data = data;
+ unpack->n_bits_left = 8 * n_bytes;
+ unpack->guard_bit = guard_bit;
+}
+
+static unsigned int
+schro_unpack_decode_bit (Unpack * unpack)
+{
+ int bit;
+
+ if (unpack->n_bits_left < 1) {
+ return unpack->guard_bit;
+ }
+ bit = (unpack->data[unpack->index >> 3] >> (7 - (unpack->index & 7))) & 1;
+ unpack->index++;
+ unpack->n_bits_left--;
+
+ return bit;
+}
+
+static unsigned int
+schro_unpack_decode_uint (Unpack * unpack)
+{
+ int count;
+ int value;
+
+ count = 0;
+ value = 0;
+ while (!schro_unpack_decode_bit (unpack)) {
+ count++;
+ value <<= 1;
+ value |= schro_unpack_decode_bit (unpack);
+ }
+
+ return (1 << count) - 1 + value;
+}
--- /dev/null
+
+#ifndef __DIRAC_PARSE_H__
+#define __DIRAC_PARSE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef enum _SchroParseCode {
+ SCHRO_PARSE_CODE_SEQUENCE_HEADER = 0x00,
+ SCHRO_PARSE_CODE_END_OF_SEQUENCE = 0x10,
+ SCHRO_PARSE_CODE_AUXILIARY_DATA = 0x20,
+ SCHRO_PARSE_CODE_PADDING = 0x30,
+
+ SCHRO_PARSE_CODE_INTRA_REF = 0x0c,
+ SCHRO_PARSE_CODE_INTRA_NON_REF = 0x08,
+ SCHRO_PARSE_CODE_INTRA_REF_NOARITH = 0x4c,
+ SCHRO_PARSE_CODE_INTRA_NON_REF_NOARITH = 0x48,
+
+ SCHRO_PARSE_CODE_INTER_REF_1 = 0x0d,
+ SCHRO_PARSE_CODE_INTER_REF_1_NOARITH = 0x4d,
+ SCHRO_PARSE_CODE_INTER_REF_2 = 0x0e,
+ SCHRO_PARSE_CODE_INTER_REF_2_NOARITH = 0x4e,
+
+ SCHRO_PARSE_CODE_INTER_NON_REF_1 = 0x09,
+ SCHRO_PARSE_CODE_INTER_NON_REF_1_NOARITH = 0x49,
+ SCHRO_PARSE_CODE_INTER_NON_REF_2 = 0x0a,
+ SCHRO_PARSE_CODE_INTER_NON_REF_2_NOARITH = 0x4a,
+
+ SCHRO_PARSE_CODE_LD_INTRA_REF = 0xcc,
+ SCHRO_PARSE_CODE_LD_INTRA_NON_REF = 0xc8
+} SchroParseCode;
+
+#define SCHRO_PARSE_CODE_PICTURE(is_ref,n_refs,is_lowdelay,is_noarith) \
+ (8 | ((is_ref)<<2) | (n_refs) | ((is_lowdelay)<<7) | ((is_noarith)<<6))
+
+#define SCHRO_PARSE_CODE_IS_SEQ_HEADER(x) ((x) == SCHRO_PARSE_CODE_SEQUENCE_HEADER)
+#define SCHRO_PARSE_CODE_IS_END_OF_SEQUENCE(x) ((x) == SCHRO_PARSE_CODE_END_OF_SEQUENCE)
+#define SCHRO_PARSE_CODE_IS_AUXILIARY_DATA(x) ((x) == SCHRO_PARSE_CODE_AUXILIARY_DATA)
+#define SCHRO_PARSE_CODE_IS_PADDING(x) ((x) == SCHRO_PARSE_CODE_PADDING)
+#define SCHRO_PARSE_CODE_IS_PICTURE(x) ((x) & 0x8)
+#define SCHRO_PARSE_CODE_IS_LOW_DELAY(x) (((x) & 0x88) == 0x88)
+#define SCHRO_PARSE_CODE_IS_CORE_SYNTAX(x) (((x) & 0x88) == 0x08)
+#define SCHRO_PARSE_CODE_USING_AC(x) (((x) & 0x48) == 0x08)
+#define SCHRO_PARSE_CODE_IS_REFERENCE(x) (((x) & 0xc) == 0x0c)
+#define SCHRO_PARSE_CODE_IS_NON_REFERENCE(x) (((x) & 0xc) == 0x08)
+#define SCHRO_PARSE_CODE_NUM_REFS(x) ((x) & 0x3)
+#define SCHRO_PARSE_CODE_IS_INTRA(x) (SCHRO_PARSE_CODE_IS_PICTURE(x) && SCHRO_PARSE_CODE_NUM_REFS(x) == 0)
+#define SCHRO_PARSE_CODE_IS_INTER(x) (SCHRO_PARSE_CODE_IS_PICTURE(x) && SCHRO_PARSE_CODE_NUM_REFS(x) > 0)
+
+#define SCHRO_PARSE_HEADER_SIZE (4+1+4+4)
+
+typedef enum _SchroVideoFormatEnum {
+ SCHRO_VIDEO_FORMAT_CUSTOM = 0,
+ SCHRO_VIDEO_FORMAT_QSIF,
+ SCHRO_VIDEO_FORMAT_QCIF,
+ SCHRO_VIDEO_FORMAT_SIF,
+ SCHRO_VIDEO_FORMAT_CIF,
+ SCHRO_VIDEO_FORMAT_4SIF,
+ SCHRO_VIDEO_FORMAT_4CIF,
+ SCHRO_VIDEO_FORMAT_SD480I_60,
+ SCHRO_VIDEO_FORMAT_SD576I_50,
+ SCHRO_VIDEO_FORMAT_HD720P_60,
+ SCHRO_VIDEO_FORMAT_HD720P_50,
+ SCHRO_VIDEO_FORMAT_HD1080I_60,
+ SCHRO_VIDEO_FORMAT_HD1080I_50,
+ SCHRO_VIDEO_FORMAT_HD1080P_60,
+ SCHRO_VIDEO_FORMAT_HD1080P_50,
+ SCHRO_VIDEO_FORMAT_DC2K_24,
+ SCHRO_VIDEO_FORMAT_DC4K_24
+} SchroVideoFormatEnum;
+
+typedef enum _SchroChromaFormat {
+ SCHRO_CHROMA_444 = 0,
+ SCHRO_CHROMA_422,
+ SCHRO_CHROMA_420
+} SchroChromaFormat;
+
+#define SCHRO_CHROMA_FORMAT_H_SHIFT(format) (((format) == SCHRO_CHROMA_444)?0:1)
+#define SCHRO_CHROMA_FORMAT_V_SHIFT(format) (((format) == SCHRO_CHROMA_420)?1:0)
+
+typedef enum _SchroSignalRange {
+ SCHRO_SIGNAL_RANGE_CUSTOM = 0,
+ SCHRO_SIGNAL_RANGE_8BIT_FULL = 1,
+ SCHRO_SIGNAL_RANGE_8BIT_VIDEO = 2,
+ SCHRO_SIGNAL_RANGE_10BIT_VIDEO = 3,
+ SCHRO_SIGNAL_RANGE_12BIT_VIDEO = 4
+} SchroSignalRange;
+
+typedef enum _SchroColourSpec {
+ SCHRO_COLOUR_SPEC_CUSTOM = 0,
+ SCHRO_COLOUR_SPEC_SDTV_525 = 1,
+ SCHRO_COLOUR_SPEC_SDTV_625 = 2,
+ SCHRO_COLOUR_SPEC_HDTV = 3,
+ SCHRO_COLOUR_SPEC_CINEMA = 4
+} SchroColourSpec;
+
+typedef enum _SchroColourPrimaries {
+ SCHRO_COLOUR_PRIMARY_HDTV = 0,
+ SCHRO_COLOUR_PRIMARY_SDTV_525 = 1,
+ SCHRO_COLOUR_PRIMARY_SDTV_625 = 2,
+ SCHRO_COLOUR_PRIMARY_CINEMA = 3
+} SchroColourPrimaries;
+
+typedef enum _SchroColourMatrix {
+ SCHRO_COLOUR_MATRIX_HDTV = 0,
+ SCHRO_COLOUR_MATRIX_SDTV = 1,
+ SCHRO_COLOUR_MATRIX_REVERSIBLE = 2
+}SchroColourMatrix;
+
+typedef enum _SchroTransferFunction {
+ SCHRO_TRANSFER_CHAR_TV_GAMMA = 0,
+ SCHRO_TRANSFER_CHAR_EXTENDED_GAMUT = 1,
+ SCHRO_TRANSFER_CHAR_LINEAR = 2,
+ SCHRO_TRANSFER_CHAR_DCI_GAMMA = 3
+} SchroTransferFunction;
+
+
+
+typedef struct _DiracSequenceHeader DiracSequenceHeader;
+
+struct _DiracSequenceHeader {
+ int major_version;
+ int minor_version;
+ int profile;
+ int level;
+
+ int index;
+ int width;
+ int height;
+ int chroma_format;
+
+ int interlaced;
+ int top_field_first;
+
+ int frame_rate_numerator;
+ int frame_rate_denominator;
+ int aspect_ratio_numerator;
+ int aspect_ratio_denominator;
+
+ int clean_width;
+ int clean_height;
+ int left_offset;
+ int top_offset;
+
+ int luma_offset;
+ int luma_excursion;
+ int chroma_offset;
+ int chroma_excursion;
+
+ int colour_primaries;
+ int colour_matrix;
+ int transfer_function;
+
+ int interlaced_coding;
+
+ int unused0;
+ int unused1;
+ int unused2;
+};
+
+
+int dirac_sequence_header_parse (DiracSequenceHeader *header,
+ unsigned char *data, int length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
#endif
#include <string.h>
#include <gst/gst-i18n-plugin.h>
-#include <gst/base/gsttypefindhelper.h>
#include "gstoggdemux.h"
#define GST_CHAIN_LOCK(ogg) g_mutex_lock((ogg)->chain_lock)
#define GST_CHAIN_UNLOCK(ogg) g_mutex_unlock((ogg)->chain_lock)
-GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_debug);
-GST_DEBUG_CATEGORY_STATIC (gst_ogg_demux_setup_debug);
+GST_DEBUG_CATEGORY (gst_ogg_demux_debug);
+GST_DEBUG_CATEGORY (gst_ogg_demux_setup_debug);
#define GST_CAT_DEFAULT gst_ogg_demux_debug
static ogg_page *
g_free (page);
}
-static GstStaticPadTemplate internaltemplate =
-GST_STATIC_PAD_TEMPLATE ("internal",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS_ANY);
-
static gboolean gst_ogg_demux_collect_chain_info (GstOggDemux * ogg,
GstOggChain * chain);
static gboolean gst_ogg_demux_activate_chain (GstOggDemux * ogg,
static void gst_ogg_pad_dispose (GObject * object);
static void gst_ogg_pad_finalize (GObject * object);
-#if 0
-static const GstFormat *gst_ogg_pad_formats (GstPad * pad);
-static const GstEventMask *gst_ogg_pad_event_masks (GstPad * pad);
-#endif
static const GstQueryType *gst_ogg_pad_query_types (GstPad * pad);
static gboolean gst_ogg_pad_src_query (GstPad * pad, GstQuery * query);
static gboolean gst_ogg_pad_event (GstPad * pad, GstEvent * event);
static GstOggPad *gst_ogg_chain_get_stream (GstOggChain * chain,
glong serialno);
-static gboolean gst_ogg_pad_query_convert (GstOggPad * pad,
- GstFormat src_format, gint64 src_val,
- GstFormat * dest_format, gint64 * dest_val);
-static GstClockTime gst_annodex_granule_to_time (gint64 granulepos,
- gint64 granulerate_n, gint64 granulerate_d, guint8 granuleshift);
-
static GstFlowReturn gst_ogg_demux_combine_flows (GstOggDemux * ogg,
GstOggPad * pad, GstFlowReturn ret);
static void gst_ogg_demux_sync_streams (GstOggDemux * ogg);
pad->mode = GST_OGG_PAD_MODE_INIT;
- pad->first_granule = -1;
pad->current_granule = -1;
pad->start_time = GST_CLOCK_TIME_NONE;
- pad->first_time = GST_CLOCK_TIME_NONE;
pad->last_stop = GST_CLOCK_TIME_NONE;
pad->have_type = FALSE;
pad->continued = NULL;
- pad->headers = NULL;
+ pad->map.headers = NULL;
}
static void
gst_ogg_pad_dispose (GObject * object)
{
GstOggPad *pad = GST_OGG_PAD (object);
- GstPad **elem_pad_p;
- GstElement **element_p;
- GstPad **elem_out_p;
-
- if (pad->element)
- gst_element_set_state (pad->element, GST_STATE_NULL);
-
- elem_pad_p = &pad->elem_pad;
- element_p = &pad->element;
- elem_out_p = &pad->elem_out;
- gst_object_replace ((GstObject **) elem_pad_p, NULL);
- gst_object_replace ((GstObject **) element_p, NULL);
- gst_object_replace ((GstObject **) elem_out_p, NULL);
pad->chain = NULL;
pad->ogg = NULL;
- g_list_foreach (pad->headers, (GFunc) gst_mini_object_unref, NULL);
- g_list_free (pad->headers);
- pad->headers = NULL;
+ g_list_foreach (pad->map.headers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (pad->map.headers);
+ pad->map.headers = NULL;
/* clear continued pages */
g_list_foreach (pad->continued, (GFunc) gst_ogg_page_free, NULL);
g_list_free (pad->continued);
pad->continued = NULL;
- ogg_stream_reset (&pad->stream);
+ ogg_stream_reset (&pad->map.stream);
G_OBJECT_CLASS (gst_ogg_pad_parent_class)->dispose (object);
}
{
GstOggPad *pad = GST_OGG_PAD (object);
- ogg_stream_clear (&pad->stream);
+ ogg_stream_clear (&pad->map.stream);
G_OBJECT_CLASS (gst_ogg_pad_parent_class)->finalize (object);
}
-#if 0
-static const GstFormat *
-gst_ogg_pad_formats (GstPad * pad)
-{
- static GstFormat src_formats[] = {
- GST_FORMAT_DEFAULT, /* time */
- GST_FORMAT_TIME, /* granulepos */
- 0
- };
- static GstFormat sink_formats[] = {
- GST_FORMAT_BYTES,
- GST_FORMAT_DEFAULT, /* bytes */
- 0
- };
-
- return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
-}
-#endif
-
-#if 0
-static const GstEventMask *
-gst_ogg_pad_event_masks (GstPad * pad)
-{
- static const GstEventMask src_event_masks[] = {
- {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
- {0,}
- };
-
- return src_event_masks;
-}
-#endif
-
static const GstQueryType *
gst_ogg_pad_query_types (GstPad * pad)
{
static void
gst_ogg_pad_reset (GstOggPad * pad)
{
- ogg_stream_reset (&pad->stream);
+ ogg_stream_reset (&pad->map.stream);
GST_DEBUG_OBJECT (pad, "doing reset");
pad->last_stop = GST_CLOCK_TIME_NONE;
}
-/* the filter function for selecting the elements we can use in
- * autoplugging */
-static gboolean
-gst_ogg_demux_factory_filter (GstPluginFeature * feature, GstCaps * caps)
-{
- guint rank;
- const gchar *klass;
-
- /* we only care about element factories */
- if (!GST_IS_ELEMENT_FACTORY (feature))
- return FALSE;
-
- klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
- /* only demuxers and decoders can play */
- if (strstr (klass, "Demux") == NULL &&
- strstr (klass, "Decoder") == NULL && strstr (klass, "Parse") == NULL) {
- return FALSE;
- }
-
- /* only select elements with autoplugging rank */
- rank = gst_plugin_feature_get_rank (feature);
- if (rank < GST_RANK_MARGINAL)
- return FALSE;
-
- GST_DEBUG ("checking factory %s", GST_PLUGIN_FEATURE_NAME (feature));
- /* now see if it is compatible with the caps */
- {
- GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
- const GList *templates;
- GList *walk;
-
- /* get the templates from the element factory */
- templates = gst_element_factory_get_static_pad_templates (factory);
-
- for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
- GstStaticPadTemplate *templ = walk->data;
-
- /* we only care about the sink templates */
- if (templ->direction == GST_PAD_SINK) {
- GstCaps *intersect;
- GstCaps *scaps;
- gboolean empty;
-
- /* try to intersect the caps with the caps of the template */
- scaps = gst_static_caps_get (&templ->static_caps);
- intersect = gst_caps_intersect (caps, scaps);
- gst_caps_unref (scaps);
-
- empty = gst_caps_is_empty (intersect);
- gst_caps_unref (intersect);
-
- /* check if the intersection is empty */
- if (!empty) {
- /* non empty intersection, we can use this element */
- goto found;
- }
- }
- }
- }
- return FALSE;
-
-found:
- return TRUE;
-}
-
-/* function used to sort element features */
-static gint
-compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
-{
- gint diff;
-
- diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
- if (diff != 0)
- return diff;
- return strcmp (gst_plugin_feature_get_name (f2),
- gst_plugin_feature_get_name (f1));
-}
-
/* called when the skeleton fishead is found. Caller ensures the packet is
* precisely the correct size; we don't re-check this here. */
static void
gint64 basetime_n, basetime_d;
/* skip "fishead\0" */
- data += 8;
- major = GST_READ_UINT16_LE (data);
- data += 2;
- minor = GST_READ_UINT16_LE (data);
- data += 2;
- prestime_n = (gint64) GST_READ_UINT64_LE (data);
- data += 8;
- prestime_d = (gint64) GST_READ_UINT64_LE (data);
- data += 8;
- basetime_n = (gint64) GST_READ_UINT64_LE (data);
- data += 8;
- basetime_d = (gint64) GST_READ_UINT64_LE (data);
- data += 8;
+ major = GST_READ_UINT16_LE (data + 8);
+ minor = GST_READ_UINT16_LE (data + 10);
+ prestime_n = (gint64) GST_READ_UINT64_LE (data + 12);
+ prestime_d = (gint64) GST_READ_UINT64_LE (data + 20);
+ basetime_n = (gint64) GST_READ_UINT64_LE (data + 28);
+ basetime_d = (gint64) GST_READ_UINT64_LE (data + 36);
ogg->basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
ogg->prestime = gst_util_uint64_scale (GST_SECOND, prestime_n, prestime_d);
ogg->have_fishead = TRUE;
- pad->is_skeleton = TRUE;
+ pad->map.is_skeleton = TRUE;
pad->start_time = GST_CLOCK_TIME_NONE;
- pad->first_granule = -1;
- pad->first_time = GST_CLOCK_TIME_NONE;
GST_INFO_OBJECT (ogg, "skeleton fishead parsed (basetime: %"
GST_TIME_FORMAT ", prestime: %" GST_TIME_FORMAT ")",
GST_TIME_ARGS (ogg->basetime), GST_TIME_ARGS (ogg->prestime));
static void
gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet)
{
- GstOggDemux *ogg = pad->ogg;
GstOggPad *fisbone_pad;
gint64 start_granule;
guint32 serialno;
guint8 *data = packet->packet;
- /* skip "fisbone\0" */
- data += 8;
- /* skip headers offset */
- data += 4;
- serialno = GST_READ_UINT32_LE (data);
+ serialno = GST_READ_UINT32_LE (data + 12);
fisbone_pad = gst_ogg_chain_get_stream (pad->chain, serialno);
if (fisbone_pad) {
- if (fisbone_pad->have_fisbone)
+ if (fisbone_pad->map.have_fisbone)
/* already parsed */
return;
- fisbone_pad->have_fisbone = TRUE;
-
- data += 4;
- /* skip number of headers */
- data += 4;
- fisbone_pad->granulerate_n = GST_READ_UINT64_LE (data);
- data += 8;
- fisbone_pad->granulerate_d = GST_READ_UINT64_LE (data);
- data += 8;
- start_granule = GST_READ_UINT64_LE (data);
- data += 8;
- fisbone_pad->preroll = GST_READ_UINT32_LE (data);
- data += 4;
- fisbone_pad->granuleshift = GST_READ_UINT8 (data);
- data += 1;
- /* padding */
- data += 3;
-
- fisbone_pad->start_time = ogg->prestime - ogg->basetime;
+ fisbone_pad->map.have_fisbone = TRUE;
+
+ fisbone_pad->map.granulerate_n = GST_READ_UINT64_LE (data + 20);
+ fisbone_pad->map.granulerate_d = GST_READ_UINT64_LE (data + 28);
+ start_granule = GST_READ_UINT64_LE (data + 36);
+ fisbone_pad->map.preroll = GST_READ_UINT32_LE (data + 44);
+ fisbone_pad->map.granuleshift = GST_READ_UINT8 (data + 48);
GST_INFO_OBJECT (pad->ogg, "skeleton fisbone parsed "
"(serialno: %08x start time: %" GST_TIME_FORMAT
- " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
+ " granulerate_n: %d granulerate_d: %d "
" preroll: %" G_GUINT32_FORMAT " granuleshift: %d)",
serialno, GST_TIME_ARGS (fisbone_pad->start_time),
- fisbone_pad->granulerate_n, fisbone_pad->granulerate_d,
- fisbone_pad->preroll, fisbone_pad->granuleshift);
+ fisbone_pad->map.granulerate_n, fisbone_pad->map.granulerate_d,
+ fisbone_pad->map.preroll, fisbone_pad->map.granuleshift);
} else {
GST_WARNING_OBJECT (pad->ogg,
"found skeleton fisbone for an unknown stream %" G_GUINT32_FORMAT,
}
}
-/* function called to convert a granulepos to a timestamp */
-static gboolean
-gst_ogg_pad_query_convert (GstOggPad * pad, GstFormat src_format,
- gint64 src_val, GstFormat * dest_format, gint64 * dest_val)
-{
- gboolean res;
-
- if (src_val == -1) {
- *dest_val = -1;
- return TRUE;
- }
-
- if (!pad->have_fisbone && pad->elem_pad == NULL)
- return FALSE;
-
- switch (src_format) {
- case GST_FORMAT_DEFAULT:
- if (pad->have_fisbone && *dest_format == GST_FORMAT_TIME) {
- *dest_val = gst_annodex_granule_to_time (src_val,
- pad->granulerate_n, pad->granulerate_d, pad->granuleshift);
-
- res = TRUE;
- } else {
- if (pad->elem_pad == NULL)
- res = FALSE;
- else
- res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
- dest_format, dest_val);
- }
-
- break;
- default:
- if (pad->elem_pad == NULL)
- res = FALSE;
- else
- res = gst_pad_query_convert (pad->elem_pad, src_format, src_val,
- dest_format, dest_val);
- }
-
- return res;
-}
-
-/* function called by the internal decoder elements when it outputs
- * a buffer. We use it to get the first timestamp of the stream
- */
-static GstFlowReturn
-gst_ogg_pad_internal_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstOggPad *oggpad;
- GstClockTime timestamp;
-
- oggpad = gst_pad_get_element_private (pad);
-
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- GST_DEBUG_OBJECT (oggpad, "received buffer from internal pad, TS=%"
- GST_TIME_FORMAT "=%" G_GINT64_FORMAT, GST_TIME_ARGS (timestamp),
- timestamp);
-
- if (oggpad->start_time == GST_CLOCK_TIME_NONE) {
- oggpad->start_time = timestamp;
- GST_DEBUG_OBJECT (oggpad, "new start time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (timestamp));
- }
-
- gst_buffer_unref (buffer);
-
- return GST_FLOW_OK;
-}
-
-static void
-internal_element_pad_added_cb (GstElement * element, GstPad * pad,
- GstOggPad * oggpad)
-{
- if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
- if (!(gst_pad_link (pad, oggpad->elem_out) == GST_PAD_LINK_OK)) {
- GST_ERROR ("Really couldn't find a valid pad");
- }
- oggpad->dynamic = FALSE;
- g_signal_handler_disconnect (element, oggpad->padaddedid);
- oggpad->padaddedid = 0;
- }
-}
-
-/* runs typefind on the packet, which is assumed to be the first
- * packet in the stream.
- *
- * Based on the type returned from the typefind function, an element
- * is created to help in conversion between granulepos and timestamps
- * so that we can do decent seeking.
- */
-static gboolean
-gst_ogg_pad_typefind (GstOggPad * pad, ogg_packet * packet)
-{
- GstBuffer *buf;
- GstCaps *caps;
- GstElement *element = NULL;
-
-#ifndef GST_DISABLE_GST_DEBUG
- GstOggDemux *ogg = pad->ogg;
-#endif
-
- if (GST_PAD_CAPS (pad) != NULL)
- return TRUE;
-
- /* The ogg spec defines that the first packet of an ogg stream must identify
- * the stream. Therefore ogg can use a simplified approach to typefinding
- * and only needs to check the first packet */
- buf = gst_buffer_new ();
- GST_BUFFER_DATA (buf) = packet->packet;
- GST_BUFFER_SIZE (buf) = packet->bytes;
- GST_BUFFER_OFFSET (buf) = 0;
-
- caps = gst_type_find_helper_for_buffer (GST_OBJECT (pad), buf, NULL);
- gst_buffer_unref (buf);
-
- if (caps == NULL) {
- GST_WARNING_OBJECT (ogg,
- "couldn't find caps for stream with serial %08x", pad->serialno);
- caps = gst_caps_new_simple ("application/octet-stream", NULL);
- } else {
- /* ogg requires you to use a decoder element to define the
- * meaning of granulepos etc so we make one. We also do this if
- * we are in the streaming mode to calculate the first timestamp. */
- GList *factories;
-
- GST_LOG_OBJECT (ogg, "found caps: %" GST_PTR_FORMAT, caps);
-
- /* first filter out the interesting element factories */
- factories = gst_default_registry_feature_filter (
- (GstPluginFeatureFilter) gst_ogg_demux_factory_filter, FALSE, caps);
-
- /* sort them according to their ranks */
- factories = g_list_sort (factories, (GCompareFunc) compare_ranks);
-
- /* then pick the first factory to create an element */
- if (factories) {
- element =
- gst_element_factory_create (GST_ELEMENT_FACTORY (factories->data),
- NULL);
- if (element) {
- GstPadTemplate *template;
-
- /* this is ours */
- gst_object_ref (element);
- gst_object_sink (GST_OBJECT (element));
-
- /* FIXME, it might not be named "sink" */
- pad->elem_pad = gst_element_get_static_pad (element, "sink");
- gst_element_set_state (element, GST_STATE_PAUSED);
- template = gst_static_pad_template_get (&internaltemplate);
- pad->elem_out = gst_pad_new_from_template (template, "internal");
- gst_pad_set_chain_function (pad->elem_out, gst_ogg_pad_internal_chain);
- gst_pad_set_element_private (pad->elem_out, pad);
- gst_pad_set_active (pad->elem_out, TRUE);
- gst_object_unref (template);
-
- /* and this pad may not be named src.. */
- /* And it might also not exist at this time... */
- {
- GstPad *p;
-
- p = gst_element_get_static_pad (element, "src");
- if (p) {
- gst_pad_link (p, pad->elem_out);
- gst_object_unref (p);
- } else {
- pad->dynamic = TRUE;
- pad->padaddedid = g_signal_connect (G_OBJECT (element),
- "pad-added", G_CALLBACK (internal_element_pad_added_cb), pad);
- }
- }
- }
- }
- gst_plugin_feature_list_free (factories);
- }
- pad->element = element;
-
- gst_pad_set_caps (GST_PAD (pad), caps);
- gst_caps_unref (caps);
-
- return TRUE;
-}
-
-/* send packet to internal element */
-static GstFlowReturn
-gst_ogg_demux_chain_elem_pad (GstOggPad * pad, ogg_packet * packet)
-{
- GstBuffer *buf;
- GstFlowReturn ret;
-
-#ifndef GST_DISABLE_GST_DEBUG
- GstOggDemux *ogg = pad->ogg;
-#endif
-
- /* initialize our internal decoder with packets */
- if (!pad->elem_pad)
- goto no_decoder;
-
- GST_DEBUG_OBJECT (ogg, "%p init decoder serial %08x", pad, pad->serialno);
-
- buf = gst_buffer_new_and_alloc (packet->bytes);
- memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes);
- gst_buffer_set_caps (buf, GST_PAD_CAPS (pad));
- GST_BUFFER_OFFSET (buf) = -1;
- GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
-
- ret = gst_pad_chain (pad->elem_pad, buf);
- if (GST_FLOW_IS_FATAL (ret))
- goto decoder_error;
-
- return ret;
-
-no_decoder:
- {
- GST_WARNING_OBJECT (ogg,
- "pad %p does not have elem_pad, no decoder ?", pad);
- return GST_FLOW_ERROR;
- }
-decoder_error:
- {
- GST_WARNING_OBJECT (ogg, "internal decoder error");
- return ret;
- }
-}
-
/* queue data, basically takes the packet, puts it in a buffer and store the
* buffer in the headers list.
*/
GstOggDemux *ogg = pad->ogg;
#endif
- GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad, pad->serialno);
+ GST_DEBUG_OBJECT (ogg, "%p queueing data serial %08x", pad,
+ pad->map.serialno);
buf = gst_buffer_new_and_alloc (packet->bytes);
memcpy (buf->data, packet->packet, packet->bytes);
GST_BUFFER_OFFSET (buf) = -1;
GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
- pad->headers = g_list_append (pad->headers, buf);
+ pad->map.headers = g_list_append (pad->map.headers, buf);
/* we are ok now */
return GST_FLOW_OK;
GstBuffer *buf;
GstFlowReturn ret, cret;
GstOggDemux *ogg = pad->ogg;
- GstFormat format;
gint64 current_time;
GstOggChain *chain;
GST_DEBUG_OBJECT (ogg,
- "%p streaming to peer serial %08x", pad, pad->serialno);
+ "%p streaming to peer serial %08x", pad, pad->map.serialno);
ret =
gst_pad_alloc_buffer_and_set_caps (GST_PAD_CAST (pad),
/* copy packet in buffer */
memcpy (buf->data, packet->packet, packet->bytes);
+ GST_BUFFER_TIMESTAMP (buf) = gst_ogg_stream_get_packet_start_time (&pad->map,
+ packet);
+ GST_BUFFER_DURATION (buf) = gst_ogg_stream_get_packet_duration (&pad->map,
+ packet);
GST_BUFFER_OFFSET (buf) = -1;
GST_BUFFER_OFFSET_END (buf) = packet->granulepos;
cret = gst_ogg_demux_combine_flows (ogg, pad, ret);
/* we're done with skeleton stuff */
- if (pad->is_skeleton)
+ if (pad->map.is_skeleton)
goto done;
/* check if valid granulepos, then we can calculate the current
ogg->current_granule = packet->granulepos;
/* convert to time */
- format = GST_FORMAT_TIME;
- if (!gst_ogg_pad_query_convert (pad,
- GST_FORMAT_DEFAULT, packet->granulepos, &format,
- (gint64 *) & current_time))
- goto convert_failed;
+ current_time = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
+ packet->granulepos);
/* convert to stream time */
if ((chain = pad->chain)) {
{
GST_DEBUG_OBJECT (ogg,
"%p could not get buffer from peer %08x, %d (%s), combined %d (%s)",
- pad, pad->serialno, ret, gst_flow_get_name (ret),
+ pad, pad->map.serialno, ret, gst_flow_get_name (ret),
cret, gst_flow_get_name (cret));
goto done;
}
-convert_failed:
- {
- GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
- goto done;
- }
}
/* submit a packet to the oggpad, this function will run the
GstOggDemux *ogg = pad->ogg;
- GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad, pad->serialno);
+ GST_DEBUG_OBJECT (ogg, "%p submit packet serial %08x", pad,
+ pad->map.serialno);
if (!pad->have_type) {
if (!ogg->have_fishead && packet->bytes == SKELETON_FISHEAD_SIZE &&
!memcmp (packet->packet, "fishead\0", 8)) {
gst_ogg_pad_parse_skeleton_fishead (pad, packet);
}
- gst_ogg_pad_typefind (pad, packet);
- pad->have_type = TRUE;
+ pad->have_type = gst_ogg_stream_setup_map (&pad->map, packet);
+ if (pad->map.caps) {
+ gst_pad_set_caps (GST_PAD (pad), pad->map.caps);
+ }
}
if (ogg->have_fishead && packet->bytes >= SKELETON_FISBONE_MIN_SIZE &&
GST_DEBUG_OBJECT (ogg, "%p has granulepos %" G_GINT64_FORMAT, pad, granule);
ogg->current_granule = granule;
pad->current_granule = granule;
- /* granulepos 0 and -1 are considered header packets.
- * Note that since theora is busted, it can create non-header
- * packets with 0 granulepos. We will correct for this when our
- * internal decoder produced a frame and we don't have a
- * granulepos because in that case the granulpos must have been 0 */
- if (pad->first_granule == -1 && granule != 0) {
- GST_DEBUG_OBJECT (ogg, "%p found first granulepos %" G_GINT64_FORMAT, pad,
- granule);
- pad->first_granule = granule;
- }
}
- if (granule != -1 && memcmp (packet->packet, "KW-DIRAC", 8) == 0) {
- return GST_FLOW_OK;
- }
+ if (!gst_ogg_stream_packet_is_header (&pad->map, packet)) {
+ if (pad->start_time == GST_CLOCK_TIME_NONE) {
+ gint64 duration = gst_ogg_stream_get_packet_duration (&pad->map, packet);
+ if (duration != -1) {
+ pad->map.accumulated_granule += duration;
+ }
- /* no start time known, stream to internal plugin to
- * get time. always stream to the skeleton decoder */
- if (pad->start_time == GST_CLOCK_TIME_NONE || pad->is_skeleton) {
- ret = gst_ogg_demux_chain_elem_pad (pad, packet);
+ if (packet->granulepos != -1) {
+ ogg_int64_t start_granule;
+
+ start_granule =
+ gst_ogg_stream_granulepos_to_granule (&pad->map,
+ packet->granulepos) - pad->map.accumulated_granule;
+
+ pad->start_time = gst_ogg_stream_granule_to_time (&pad->map,
+ start_granule);
+ }
+ }
+ } else {
+ pad->map.n_header_packets_seen++;
}
+
/* we know the start_time of the pad data, see if we
* can activate the complete chain if this is a dynamic
* chain. */
if (pad->start_time != GST_CLOCK_TIME_NONE) {
GstOggChain *chain = pad->chain;
- /* correction for busted ogg, if the internal decoder outputed
- * a timestamp but we did not get a granulepos, it must have
- * been 0 and the time is therefore also 0 */
- if (pad->first_granule == -1) {
- GST_DEBUG_OBJECT (ogg, "%p found start time without granulepos", pad);
- pad->first_granule = 0;
- pad->first_time = 0;
- }
-
/* check if complete chain has start time */
if (chain == ogg->building_chain) {
int ret;
ogg_packet packet;
- ret = ogg_stream_packetout (&pad->stream, &packet);
+ ret = ogg_stream_packetout (&pad->map.stream, &packet);
switch (ret) {
case 0:
GST_LOG_OBJECT (ogg, "packetout done");
could_not_submit:
{
GST_WARNING_OBJECT (ogg,
- "could not submit packet for stream %08x, error: %d", pad->serialno,
+ "could not submit packet for stream %08x, error: %d", pad->map.serialno,
result);
gst_ogg_pad_reset (pad);
return result;
}
}
- if (ogg_stream_pagein (&pad->stream, page) != 0)
+ if (ogg_stream_pagein (&pad->map.stream, page) != 0)
goto choked;
/* flush all packets in the stream layer, this might not give a packet if
ogg_page *p = (ogg_page *) pad->continued->data;
GST_LOG_OBJECT (ogg, "submitting continued page %p", p);
- if (ogg_stream_pagein (&pad->stream, p) != 0)
+ if (ogg_stream_pagein (&pad->map.stream, p) != 0)
goto choked;
pad->continued = g_list_delete_link (pad->continued, pad->continued);
/* flush all remaining packets, we pushed them in the previous round.
* We don't use _reset() because we still want to get the discont when
* we submit a next page. */
- while (ogg_stream_packetout (&pad->stream, &packet) != 0);
+ while (ogg_stream_packetout (&pad->map.stream, &packet) != 0);
}
done:
{
GST_WARNING_OBJECT (ogg,
"ogg stream choked on page (serial %08x), resetting stream",
- pad->serialno);
+ pad->map.serialno);
gst_ogg_pad_reset (pad);
/* we continue to recover */
return GST_FLOW_OK;
ret->chain = chain;
ret->ogg = chain->ogg;
- ret->serialno = serialno;
- if (ogg_stream_init (&ret->stream, serialno) != 0)
+ ret->map.serialno = serialno;
+ if (ogg_stream_init (&ret->map.stream, serialno) != 0)
goto init_failed;
name = g_strdup_printf ("serial_%08lx", serialno);
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
- if (pad->serialno == serialno)
+ if (pad->map.serialno == serialno)
return pad;
}
return NULL;
static void gst_ogg_demux_finalize (GObject * object);
-//static const GstEventMask *gst_ogg_demux_get_event_masks (GstPad * pad);
-//static const GstQueryType *gst_ogg_demux_get_query_types (GstPad * pad);
static GstFlowReturn gst_ogg_demux_read_chain (GstOggDemux * ogg,
GstOggChain ** chain);
static GstFlowReturn gst_ogg_demux_read_end_chain (GstOggDemux * ogg,
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
GstEvent *event;
- if (pad->is_skeleton)
+ if (pad->map.is_skeleton)
continue;
event = gst_event_new_eos ();
pad = g_array_index (chain->streams, GstOggPad *, i);
- if (pad->is_skeleton)
+ if (pad->map.is_skeleton)
continue;
GST_DEBUG_OBJECT (ogg, "adding pad %" GST_PTR_FORMAT, pad);
pad = g_array_index (chain->streams, GstOggPad *, i);
- for (headers = pad->headers; headers; headers = g_list_next (headers)) {
+ for (headers = pad->map.headers; headers; headers = g_list_next (headers)) {
GstBuffer *buffer = GST_BUFFER (headers->data);
if (pad->discont) {
gst_pad_push (GST_PAD_CAST (pad), buffer);
}
/* and free the headers */
- g_list_free (pad->headers);
- pad->headers = NULL;
+ g_list_free (pad->map.headers);
+ pad->map.headers = NULL;
}
return TRUE;
}
/* found offset of next ogg page */
gint64 granulepos;
GstClockTime granuletime;
- GstFormat format;
GstOggPad *pad;
GST_LOG_OBJECT (ogg, "found next ogg page at %" G_GINT64_FORMAT,
}
pad = gst_ogg_chain_get_stream (chain, ogg_page_serialno (&og));
- if (pad == NULL || pad->is_skeleton)
+ if (pad == NULL || pad->map.is_skeleton)
continue;
- format = GST_FORMAT_TIME;
- if (!gst_ogg_pad_query_convert (pad,
- GST_FORMAT_DEFAULT, granulepos, &format,
- (gint64 *) & granuletime)) {
- GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
- granuletime = target;
- } else {
- if (granuletime < pad->start_time)
- continue;
- GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
- GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
+ granuletime = gst_ogg_stream_get_end_time_for_granulepos (&pad->map,
+ granulepos);
+ if (granuletime < pad->start_time)
+ continue;
+ GST_LOG_OBJECT (ogg, "granulepos %" G_GINT64_FORMAT " maps to time %"
+ GST_TIME_FORMAT, granulepos, GST_TIME_ARGS (granuletime));
- granuletime -= pad->start_time;
- }
+ granuletime -= pad->start_time;
GST_DEBUG_OBJECT (ogg,
"found page with granule %" G_GINT64_FORMAT " and time %"
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
- GST_LOG_OBJECT (ogg, "serial %08x time %" GST_TIME_FORMAT, pad->serialno,
- GST_TIME_ARGS (pad->start_time));
+ GST_LOG_OBJECT (ogg, "serial %08x time %" GST_TIME_FORMAT,
+ pad->map.serialno, GST_TIME_ARGS (pad->start_time));
- if (pad->serialno == serial) {
+ if (pad->map.serialno == serial) {
known_serial = TRUE;
/* submit the page now, this will fill in the start_time when the
* internal decoder finds it */
gst_ogg_pad_submit_page (pad, &op);
- if (!pad->is_skeleton && pad->start_time == -1 && ogg_page_eos (&op)) {
+ if (!pad->map.is_skeleton && pad->start_time == -1
+ && ogg_page_eos (&op)) {
/* got EOS on a pad before we could find its start_time.
* We have no chance of finding a start_time for every pad so
* stop searching for the other start_time(s).
}
}
/* the timestamp will be filled in when we submit the pages */
- if (!pad->is_skeleton)
+ if (!pad->map.is_skeleton)
done &= (pad->start_time != GST_CLOCK_TIME_NONE);
- GST_LOG_OBJECT (ogg, "done %08x now %d", pad->serialno, done);
+ GST_LOG_OBJECT (ogg, "done %08x now %d", pad->map.serialno, done);
}
/* we read a page not belonging to the current chain: seek back to the
/* now we can fill in the missing info using queries */
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
- GstFormat target;
- if (pad->is_skeleton)
+ if (pad->map.is_skeleton)
continue;
- GST_LOG_OBJECT (ogg, "convert first granule %" G_GUINT64_FORMAT " to time ",
- pad->first_granule);
-
- target = GST_FORMAT_TIME;
- if (!gst_ogg_pad_query_convert (pad,
- GST_FORMAT_DEFAULT, pad->first_granule, &target,
- (gint64 *) & pad->first_time)) {
- GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
- } else {
- GST_LOG_OBJECT (ogg, "converted to first time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (pad->first_time));
- }
-
pad->mode = GST_OGG_PAD_MODE_STREAMING;
}
gint64 end = begin;
gint64 last_granule = -1;
GstOggPad *last_pad = NULL;
- GstFormat target;
GstFlowReturn ret;
gboolean done = FALSE;
ogg_page og;
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
- if (pad->is_skeleton)
+ if (pad->map.is_skeleton)
continue;
- if (pad->serialno == ogg_page_serialno (&og)) {
+ if (pad->map.serialno == ogg_page_serialno (&og)) {
gint64 granulepos = ogg_page_granulepos (&og);
- if (last_granule < granulepos) {
+ if (last_granule == -1 || last_granule < granulepos) {
last_granule = granulepos;
last_pad = pad;
}
}
}
- target = GST_FORMAT_TIME;
- if (last_granule == -1 || !gst_ogg_pad_query_convert (last_pad,
- GST_FORMAT_DEFAULT, last_granule, &target,
- (gint64 *) & chain->segment_stop)) {
- GST_WARNING_OBJECT (ogg, "could not convert granulepos to time");
+ if (last_pad) {
+ chain->segment_stop =
+ gst_ogg_stream_get_end_time_for_granulepos (&last_pad->map,
+ last_granule);
+ } else {
chain->segment_stop = GST_CLOCK_TIME_NONE;
}
+ GST_INFO ("segment stop %lld", chain->segment_stop);
+
return GST_FLOW_OK;
}
for (i = 0; i < chain->streams->len; i++) {
GstOggPad *pad = g_array_index (chain->streams, GstOggPad *, i);
- if (pad->is_skeleton)
+ if (pad->map.is_skeleton)
continue;
/* can do this if the pad start time is not defined */
&& chain->segment_start != G_MAXUINT64)
chain->total_time = chain->segment_stop - chain->segment_start;
+ GST_DEBUG ("total time %lld", chain->total_time);
+
GST_DEBUG_OBJECT (ogg, "return %d", res);
return res;
return result;
}
-static GstClockTime
-gst_annodex_granule_to_time (gint64 granulepos, gint64 granulerate_n,
- gint64 granulerate_d, guint8 granuleshift)
-{
- gint64 keyindex, keyoffset;
- gint64 granulerate;
- GstClockTime res;
-
- if (granulepos == 0 || granulerate_n == 0 || granulerate_d == 0)
- return 0;
-
- if (granuleshift != 0) {
- keyindex = granulepos >> granuleshift;
- keyoffset = granulepos - (keyindex << granuleshift);
- granulepos = keyindex + keyoffset;
- }
-
- /* GST_SECOND / (granulerate_n / granulerate_d) */
- granulerate = gst_util_uint64_scale (GST_SECOND,
- granulerate_d, granulerate_n);
- res = gst_util_uint64_scale (granulepos, granulerate, 1);
- return res;
-}
-
gboolean
gst_ogg_demux_plugin_init (GstPlugin * plugin)
{
for (j = 0; j < chain->streams->len; j++) {
GstOggPad *stream = g_array_index (chain->streams, GstOggPad *, j);
- GST_INFO_OBJECT (ogg, " stream %08x:", stream->serialno);
+ GST_INFO_OBJECT (ogg, " stream %08x:", stream->map.serialno);
GST_INFO_OBJECT (ogg, " start time: %" GST_TIME_FORMAT,
GST_TIME_ARGS (stream->start_time));
- GST_INFO_OBJECT (ogg, " first granulepos: %" G_GINT64_FORMAT,
- stream->first_granule);
- GST_INFO_OBJECT (ogg, " first time: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (stream->first_time));
}
}
}
#include <gst/gst.h>
+#include "gstoggstream.h"
+
G_BEGIN_DECLS
#define GST_TYPE_OGG_PAD (gst_ogg_pad_get_type())
#define GST_IS_OGG_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OGG_DEMUX))
#define GST_IS_OGG_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OGG_DEMUX))
-static GType gst_ogg_demux_get_type (void);
+GType gst_ogg_demux_get_type (void);
typedef struct _GstOggDemux GstOggDemux;
typedef struct _GstOggDemuxClass GstOggDemuxClass;
gboolean have_type;
GstOggPadMode mode;
+#if 0
GstPad *elem_pad; /* sinkpad of internal element */
GstElement *element; /* internal element */
GstPad *elem_out; /* our sinkpad to receive buffers form the internal element */
+#endif
GstOggChain *chain; /* the chain we are part of */
GstOggDemux *ogg; /* the ogg demuxer we are part of */
- GList *headers;
-
+ //GList *headers;
+ GstOggStream map;
+#if 0
+ gint map;
gboolean is_skeleton;
gboolean have_fisbone;
- gint64 granulerate_n;
- gint64 granulerate_d;
+ gint granulerate_n;
+ gint granulerate_d;
guint32 preroll;
guint granuleshift;
+ gint n_header_packets;
+ gint n_header_packets_seen;
+ gint64 accumulated_granule;
+ gint frame_size;
+#endif
- gint serialno;
+ //gint serialno;
gint64 packetno;
gint64 current_granule;
GstClockTime last_stop; /* last_stop when last push occured; used to detect when we
* need to send a newsegment update event for sparse streams */
- ogg_stream_state stream;
GList *continued;
gboolean discont;
GstElementClass parent_class;
};
+
G_END_DECLS
#endif /* __GST_OGG_DEMUX_H__ */
#include <ogg/ogg.h>
#include <string.h>
+#include "gstoggstream.h"
+
static const GstElementDetails gst_ogg_parse_details =
GST_ELEMENT_DETAILS ("Ogg parser",
"Codec/Parser",
typedef struct _GstOggParse GstOggParse;
typedef struct _GstOggParseClass GstOggParseClass;
-/* Each ogg logical stream has a GstOggStream associated with it */
-typedef struct
-{
- /*ogg_stream_state stream; *//* We need this to get the packets out in order
- to do codec identification, for various
- codec-specific tasks */
-
- gboolean in_headers; /* Initially true, false once we've read all the
- headers for this logical stream */
-
- guint32 serialno; /* Unique serial number of this stream */
-
- GSList *headers; /* List of ogg pages that we'll set on caps */
- GSList *unknown_pages; /* List of pages we haven't yet classified */
-} GstOggStream;
-
struct _GstOggParse
{
GstElement element;
static void
free_stream (GstOggStream * stream)
{
- g_slist_foreach (stream->headers, (GFunc) gst_mini_object_unref, NULL);
- g_slist_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL);
+ g_list_foreach (stream->headers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_foreach (stream->unknown_pages, (GFunc) gst_mini_object_unref, NULL);
g_free (stream);
}
}
static GstOggStream *
-gst_ogg_parse_new_stream (GstOggParse * parser, guint32 serialno)
+gst_ogg_parse_new_stream (GstOggParse * parser, ogg_page * page)
{
- GstOggStream *ret;
+ GstOggStream *stream;
+ ogg_packet packet;
+ int ret;
+ guint32 serialno;
+
+ serialno = ogg_page_serialno (page);
GST_DEBUG_OBJECT (parser, "creating new stream %08x", serialno);
- ret = g_new0 (GstOggStream, 1);
+ stream = g_new0 (GstOggStream, 1);
- ret->serialno = serialno;
- ret->in_headers = 1;
+ stream->serialno = serialno;
+ stream->in_headers = 1;
- /*
- if (ogg_stream_init (&ret->stream, serialno) != 0) {
- GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
- serialno);
- return NULL;
- }
- */
+ if (ogg_stream_init (&stream->stream, serialno) != 0) {
+ GST_ERROR ("Could not initialize ogg_stream struct for serial %08lx.",
+ serialno);
+ return NULL;
+ }
- parser->oggstreams = g_slist_append (parser->oggstreams, ret);
+ /* FIXME check return */
+ ogg_stream_pagein (&stream->stream, page);
- return ret;
+ /* FIXME check return */
+ ret = ogg_stream_packetout (&stream->stream, &packet);
+ if (ret == 1) {
+ gst_ogg_stream_setup_map (stream, &packet);
+ }
+
+ parser->oggstreams = g_slist_append (parser->oggstreams, stream);
+
+ return stream;
}
static GstOggStream *
/* discontinuity; track how many bytes we skipped (-ret) */
ogg->offset -= ret;
} else {
-#ifndef GST_DISABLE_GST_DEBUG
gint64 granule = ogg_page_granulepos (&page);
+#ifndef GST_DISABLE_GST_DEBUG
int bos = ogg_page_bos (&page);
#endif
guint64 startoffset = ogg->offset;
+ GstOggStream *stream;
+
+ serialno = ogg_page_serialno (&page);
+ stream = gst_ogg_parse_find_stream (ogg, serialno);
GST_LOG_OBJECT (ogg, "Timestamping outgoing buffer as %" GST_TIME_FORMAT,
GST_TIME_ARGS (buffertimestamp));
- /* Turn our page into a GstBuffer TODO: better timestamps? Requires format
- * parsing. */
+
+ buffertimestamp = gst_ogg_stream_get_end_time_for_granulepos (stream,
+ granule);
pagebuffer = gst_ogg_parse_buffer_from_page (&page, startoffset, FALSE,
buffertimestamp);
/* We read out 'ret' bytes, so we set the next offset appropriately */
ogg->offset += ret;
- serialno = ogg_page_serialno (&page);
-
GST_LOG_OBJECT (ogg,
"processing ogg page (serial %08x, pageno %ld, "
"granule pos %" G_GUINT64_FORMAT ", bos %d, offset %"
gst_ogg_parse_delete_all_streams (ogg);
}
- stream = gst_ogg_parse_new_stream (ogg, serialno);
+ stream = gst_ogg_parse_new_stream (ogg, &page);
ogg->last_page_not_bos = FALSE;
gst_buffer_ref (pagebuffer);
- stream->headers = g_slist_append (stream->headers, pagebuffer);
+ stream->headers = g_list_append (stream->headers, pagebuffer);
if (!ogg->in_headers) {
GST_LOG_OBJECT (ogg,
if (type == PAGE_PENDING && ogg->in_headers) {
gst_buffer_ref (pagebuffer);
- stream->unknown_pages = g_slist_append (stream->unknown_pages,
+ stream->unknown_pages = g_list_append (stream->unknown_pages,
pagebuffer);
} else if (type == PAGE_HEADER) {
if (!ogg->in_headers) {
/* Append the header to the buffer list, after any unknown previous
* pages
*/
- stream->headers = g_slist_concat (stream->headers,
+ stream->headers = g_list_concat (stream->headers,
stream->unknown_pages);
- g_slist_free (stream->unknown_pages);
+ g_list_free (stream->unknown_pages);
gst_buffer_ref (pagebuffer);
- stream->headers = g_slist_append (stream->headers, pagebuffer);
+ stream->headers = g_list_append (stream->headers, pagebuffer);
}
} else { /* PAGE_DATA, or PAGE_PENDING but outside headers */
if (ogg->in_headers) {
for (l = ogg->oggstreams; l != NULL; l = l->next) {
GstOggStream *stream = (GstOggStream *) l->data;
- if (g_slist_length (stream->headers) == 0) {
+ if (g_list_length (stream->headers) == 0) {
GST_LOG_OBJECT (ogg, "No primary header found for stream %u",
stream->serialno);
goto failure;
GstOggStream *stream = (GstOggStream *) l->data;
int j;
- for (j = 1; j < g_slist_length (stream->headers); j++) {
+ for (j = 1; j < g_list_length (stream->headers); j++) {
gst_ogg_parse_append_header (&array,
- GST_BUFFER (g_slist_nth_data (stream->headers, j)));
+ GST_BUFFER (g_list_nth_data (stream->headers, j)));
count++;
}
}
GstOggStream *stream = (GstOggStream *) l->data;
int j;
- for (j = 1; j < g_slist_length (stream->headers); j++) {
+ for (j = 1; j < g_list_length (stream->headers); j++) {
GstBuffer *buf =
- GST_BUFFER (g_slist_nth_data (stream->headers, j));
+ GST_BUFFER (g_list_nth_data (stream->headers, j));
gst_buffer_set_caps (buf, caps);
result = gst_pad_push (ogg->srcpad, buf);
/* And finally the pending data pages */
for (l = ogg->oggstreams; l != NULL; l = l->next) {
GstOggStream *stream = (GstOggStream *) l->data;
- GSList *k;
+ GList *k;
if (stream->unknown_pages == NULL)
continue;
found_pending_headers = TRUE;
GST_LOG_OBJECT (ogg, "Pushing %d pending pages after headers",
- g_slist_length (stream->unknown_pages) + 1);
+ g_list_length (stream->unknown_pages) + 1);
for (k = stream->unknown_pages; k != NULL; k = k->next) {
GstBuffer *buf;
if (result != GST_FLOW_OK)
return result;
}
- g_slist_foreach (stream->unknown_pages,
+ g_list_foreach (stream->unknown_pages,
(GFunc) gst_mini_object_unref, NULL);
- g_slist_free (stream->unknown_pages);
+ g_list_free (stream->unknown_pages);
stream->unknown_pages = NULL;
}
--- /dev/null
+/* GStreamer Ogg Granulepos Mapping Utility Functions
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2009 David Schleef <ds@schleef.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "gstoggstream.h"
+#include "dirac_parse.h"
+
+#include <string.h>
+
+GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
+GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug);
+#define GST_CAT_DEFAULT gst_ogg_demux_debug
+
+typedef struct _GstOggMap GstOggMap;
+
+typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad,
+ ogg_packet * packet);
+typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad,
+ gint64 granulepos);
+typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad,
+ gint64 granulepos);
+
+/* returns TRUE if the granulepos denotes a key frame */
+typedef gboolean (*GstOggMapIsKeyFrameFunc) (GstOggStream * pad,
+ gint64 granulepos);
+
+/* returns TRUE if the given packet is a stream header packet */
+typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad,
+ ogg_packet * packet);
+typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad,
+ ogg_packet * packet);
+
+
+
+#define SKELETON_FISBONE_MIN_SIZE 52
+
+
+struct _GstOggMap
+{
+ const gchar *id;
+ int id_length;
+ int min_packet_size;
+ const gchar *media_type;
+ GstOggMapSetupFunc setup_func;
+ GstOggMapToGranuleFunc granulepos_to_granule_func;
+ GstOggMapIsKeyFrameFunc is_key_frame_func;
+ GstOggMapIsHeaderPacketFunc is_header_func;
+ GstOggMapPacketDurationFunc packet_duration_func;
+};
+
+static const GstOggMap mappers[];
+
+GstClockTime
+gst_ogg_stream_get_packet_start_time (GstOggStream * pad, ogg_packet * packet)
+{
+ int duration;
+
+ if (packet->granulepos == -1) {
+ return GST_CLOCK_TIME_NONE;
+ }
+
+ duration = gst_ogg_stream_get_packet_duration (pad, packet);
+ if (duration == -1) {
+ return GST_CLOCK_TIME_NONE;
+ }
+
+ return gst_ogg_stream_granule_to_time (pad,
+ gst_ogg_stream_granulepos_to_granule (pad,
+ packet->granulepos) - duration);
+}
+
+GstClockTime
+gst_ogg_stream_get_start_time_for_granulepos (GstOggStream * pad,
+ gint64 granulepos)
+{
+ if (pad->frame_size == 0)
+ return GST_CLOCK_TIME_NONE;
+
+ return gst_ogg_stream_granule_to_time (pad,
+ gst_ogg_stream_granulepos_to_granule (pad, granulepos));
+}
+
+GstClockTime
+gst_ogg_stream_get_end_time_for_granulepos (GstOggStream * pad,
+ gint64 granulepos)
+{
+ return gst_ogg_stream_granule_to_time (pad,
+ gst_ogg_stream_granulepos_to_granule (pad, granulepos));
+}
+
+GstClockTime
+gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule)
+{
+ if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0)
+ return 0;
+
+ return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d,
+ pad->granulerate_n);
+}
+
+gint64
+gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos)
+{
+ if (granulepos == -1 || granulepos == 0) {
+ return granulepos;
+ }
+
+ if (mappers[pad->map].granulepos_to_granule_func == NULL) {
+ GST_WARNING ("Failed to convert granulepos to time");
+ return GST_CLOCK_TIME_NONE;
+ }
+
+ return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
+}
+
+gboolean
+gst_ogg_stream_packet_granulepos_is_key_frame (GstOggStream * pad,
+ gint64 granulepos)
+{
+ if (granulepos == -1) {
+ return FALSE;
+ }
+
+ if (mappers[pad->map].is_key_frame_func == NULL) {
+ GST_WARNING ("Failed to determine key frame");
+ return FALSE;
+ }
+
+ return mappers[pad->map].is_key_frame_func (pad, granulepos);
+}
+
+gboolean
+gst_ogg_stream_packet_is_header (GstOggStream * pad, ogg_packet * packet)
+{
+ if (mappers[pad->map].is_header_func == NULL) {
+ GST_WARNING ("Failed to determine header");
+ return FALSE;
+ }
+
+ return mappers[pad->map].is_header_func (pad, packet);
+}
+
+gint64
+gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet)
+{
+ if (mappers[pad->map].packet_duration_func == NULL) {
+ GST_WARNING ("Failed to determine packet duration");
+ return -1;
+ }
+
+ return mappers[pad->map].packet_duration_func (pad, packet);
+}
+
+
+
+
+/* some generic functions */
+
+static gboolean
+is_keyframe_true (GstOggStream * pad, gint64 granulepos)
+{
+ return TRUE;
+}
+
+static gint64
+granulepos_to_granule_default (GstOggStream * pad, gint64 granulepos)
+{
+ gint64 keyindex, keyoffset;
+
+ if (pad->granuleshift != 0) {
+ keyindex = granulepos >> pad->granuleshift;
+ keyoffset = granulepos - (keyindex << pad->granuleshift);
+ return keyindex + keyoffset;
+ } else {
+ return granulepos;
+ }
+}
+
+static gboolean
+is_header_unknown (GstOggStream * pad, ogg_packet * packet)
+{
+ GST_WARNING ("don't know how to detect header");
+ return FALSE;
+}
+
+static gboolean
+is_header_true (GstOggStream * pad, ogg_packet * packet)
+{
+ return TRUE;
+}
+
+static gboolean
+is_header_count (GstOggStream * pad, ogg_packet * packet)
+{
+ if (pad->n_header_packets_seen < pad->n_header_packets) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gint64
+packet_duration_constant (GstOggStream * pad, ogg_packet * packet)
+{
+ return pad->frame_size;
+}
+
+/* theora */
+
+static gboolean
+setup_theora_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+
+ pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
+ pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
+ GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT,
+ pad->granulerate_n, pad->granulerate_d);
+
+ /* 2 bits + 3 bits = 5 bits KFGSHIFT */
+ pad->granuleshift = ((GST_READ_UINT8 (data + 40) & 0x03) << 3) +
+ (GST_READ_UINT8 (data + 41) >> 5);
+
+ pad->n_header_packets = 3;
+ pad->frame_size = 1;
+
+ if (pad->granuleshift == 0 || pad->granulerate_n == 0
+ || pad->granulerate_d == 0)
+ return FALSE;
+
+ pad->caps = gst_caps_new_simple ("video/x-theora",
+ "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
+ pad->granulerate_d, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+is_keyframe_theora (GstOggStream * pad, gint64 granulepos)
+{
+ gint64 frame_mask;
+
+ if (granulepos == (gint64) - 1)
+ return FALSE;
+
+ frame_mask = (1 << (pad->granuleshift + 1)) - 1;
+
+ return ((granulepos & frame_mask) == 0);
+}
+
+static gboolean
+is_header_theora (GstOggStream * pad, ogg_packet * packet)
+{
+ return (packet->bytes > 0 && (packet->packet[0] & 0x80) == 0x80);
+}
+
+/* dirac */
+
+static gboolean
+setup_dirac_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ int ret;
+ DiracSequenceHeader header;
+
+ ret = dirac_sequence_header_parse (&header, packet->packet + 13,
+ packet->bytes - 13);
+ if (ret == 0) {
+ GST_DEBUG ("Failed to parse Dirac sequence header");
+ return FALSE;
+ }
+
+ pad->granulerate_n = header.frame_rate_numerator * 2;
+ pad->granulerate_d = header.frame_rate_denominator;
+ pad->granuleshift = 22;
+ pad->n_header_packets = 1;
+ pad->frame_size = 2;
+
+ if (header.interlaced_coding != 0) {
+ GST_DEBUG ("non-progressive Dirac coding not implemented");
+ return FALSE;
+ }
+
+ pad->caps = gst_caps_new_simple ("video/x-dirac",
+ "width", G_TYPE_INT, header.width,
+ "height", G_TYPE_INT, header.height,
+ "interlaced", G_TYPE_BOOLEAN, header.interlaced,
+ "pixel-aspect-ratio", GST_TYPE_FRACTION,
+ header.aspect_ratio_numerator, header.aspect_ratio_denominator,
+ "framerate", GST_TYPE_FRACTION, header.frame_rate_numerator,
+ header.frame_rate_denominator, NULL);
+
+ return TRUE;
+}
+
+#define OGG_DIRAC_GRANULE_LOW_MASK ((1<<22) - 1)
+static gboolean
+is_keyframe_dirac (GstOggStream * pad, gint64 granulepos)
+{
+ gint64 pt;
+ int dist_h;
+ int dist_l;
+ int dist;
+ int delay;
+ gint64 dt;
+
+ if (granulepos == -1)
+ return -1;
+
+ pt = ((granulepos >> 22) + (granulepos & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
+ dist_h = (granulepos >> 22) & 0xff;
+ dist_l = granulepos & 0xff;
+ dist = (dist_h << 8) | dist_l;
+ delay = (granulepos >> 9) & 0x1fff;
+ dt = pt - delay;
+
+ return (dist == 0);
+}
+
+static gint64
+granulepos_to_granule_dirac (GstOggStream * pad, gint64 gp)
+{
+ gint64 pt;
+ int dist_h;
+ int dist_l;
+ int dist;
+ int delay;
+ gint64 dt;
+
+ pt = ((gp >> 22) + (gp & OGG_DIRAC_GRANULE_LOW_MASK)) >> 9;
+ dist_h = (gp >> 22) & 0xff;
+ dist_l = gp & 0xff;
+ dist = (dist_h << 8) | dist_l;
+ delay = (gp >> 9) & 0x1fff;
+ dt = pt - delay;
+
+ GST_DEBUG ("pt %lld delay %d", pt, delay);
+
+ return dt + 4;
+}
+
+
+/* vorbis */
+
+void parse_vorbis_header_packet (GstOggStream * pad, ogg_packet * op);
+void parse_vorbis_setup_packet (GstOggStream * pad, ogg_packet * op);
+
+
+static gboolean
+setup_vorbis_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+
+ data += 1 + 6 + 4 + 1;
+ pad->granulerate_n = GST_READ_UINT32_LE (data);
+ pad->granulerate_d = 1;
+ pad->granuleshift = 0;
+ GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+ pad->n_header_packets = 3;
+
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ parse_vorbis_header_packet (pad, packet);
+
+ pad->caps = gst_caps_new_simple ("audio/x-vorbis",
+ "rate", G_TYPE_INT, pad->granulerate_n, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+is_header_vorbis (GstOggStream * pad, ogg_packet * packet)
+{
+ if (packet->bytes > 0 && (packet->packet[0] & 0x01) == 0)
+ return FALSE;
+
+ if (packet->packet[0] == 5) {
+ parse_vorbis_setup_packet (pad, packet);
+ }
+
+ return TRUE;
+}
+
+static gint64
+packet_duration_vorbis (GstOggStream * pad, ogg_packet * packet)
+{
+ int mode;
+ int size;
+ int duration;
+
+ mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
+ size = pad->vorbis_mode_sizes[mode];
+
+ if (size) {
+ switch ((packet->packet[0] >> (1 + pad->vorbis_log2_num_modes)) & 3) {
+ case 0:
+ case 3:
+ duration = pad->long_size / 2;
+ break;
+ case 1:
+ duration = (pad->long_size + pad->short_size) / 4;
+ break;
+ case 2:
+ duration = (3 * pad->long_size - pad->short_size) / 4;
+ break;
+ default:
+ duration = -1;
+ break;
+ }
+ } else {
+ duration = pad->short_size / 2;
+ }
+
+ return duration;
+}
+
+/* speex */
+
+
+static gboolean
+setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+
+ data += 8 + 20 + 4 + 4;
+ pad->granulerate_n = GST_READ_UINT32_LE (data);
+ pad->granulerate_d = 1;
+ pad->granuleshift = 0;
+ GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+ pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 68) + 2;
+ pad->frame_size = GST_READ_UINT32_LE (packet->packet + 64) *
+ GST_READ_UINT32_LE (packet->packet + 56);
+
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ pad->caps = gst_caps_new_simple ("audio/x-speex",
+ "rate", G_TYPE_INT, pad->granulerate_n, NULL);
+
+ return TRUE;
+}
+
+
+/* flac */
+
+static gboolean
+setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ /* FIXME punt on this for now */
+ return FALSE;
+}
+
+static gboolean
+setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+
+ /* see http://flac.sourceforge.net/ogg_mapping.html */
+
+ pad->granulerate_n = (GST_READ_UINT32_BE (data + 27) & 0xFFFFF000) >> 12;
+ pad->granulerate_d = 1;
+ pad->granuleshift = 0;
+ GST_DEBUG ("sample rate: %d", pad->granulerate_n);
+
+ pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
+
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ pad->caps = gst_caps_new_simple ("audio/x-flac",
+ "rate", G_TYPE_INT, pad->granulerate_n, NULL);
+
+ return TRUE;
+}
+
+static gint64
+packet_duration_flac (GstOggStream * pad, ogg_packet * packet)
+{
+ int block_size_index;
+
+ if (packet->bytes < 4)
+ return -1;
+
+ block_size_index = packet->packet[2] >> 4;
+ if (block_size_index == 1)
+ return 192;
+ if (block_size_index >= 2 && block_size_index <= 5) {
+ return 576 << (block_size_index - 2);
+ }
+ if (block_size_index >= 8) {
+ return 256 << (block_size_index - 8);
+ }
+ return -1;
+}
+
+/* fishead */
+
+static gboolean
+setup_fishead_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data;
+ gint64 prestime_n, prestime_d;
+ gint64 basetime_n, basetime_d;
+ gint64 basetime;
+
+ data = packet->packet;
+
+ data += 8 + 2 + 2; /* header + major/minor version */
+
+ prestime_n = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+ prestime_d = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+ basetime_n = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+ basetime_d = (gint64) GST_READ_UINT64_LE (data);
+ data += 8;
+
+ /* FIXME: we don't use basetime anywhere in the demuxer! */
+ basetime = gst_util_uint64_scale (GST_SECOND, basetime_n, basetime_d);
+ GST_INFO ("skeleton fishead parsed (basetime: %" GST_TIME_FORMAT ")",
+ GST_TIME_ARGS (basetime));
+
+ return TRUE;
+}
+
+gboolean
+gst_ogg_map_add_fisbone (GstOggStream * pad,
+ const guint8 * data, guint size, GstClockTime * p_start_time,
+ guint32 * p_preroll)
+{
+ GstClockTime start_time;
+ gint64 start_granule;
+ guint32 preroll;
+
+ if (size < SKELETON_FISBONE_MIN_SIZE || memcmp (data, "fisbone\0", 8) != 0) {
+ GST_WARNING ("invalid fisbone packet, ignoring");
+ return FALSE;
+ }
+
+ if (pad->have_fisbone) {
+ GST_DEBUG ("already have fisbone, ignoring second one");
+ return FALSE;
+ }
+
+ /* skip "fisbone\0" + headers offset + serialno + num headers */
+ data += 8 + 4 + 4 + 4;
+
+ pad->have_fisbone = TRUE;
+
+ /* we just overwrite whatever was set before by the format-specific setup */
+ pad->granulerate_n = GST_READ_UINT64_LE (data);
+ pad->granulerate_d = GST_READ_UINT64_LE (data + 8);
+
+ start_granule = GST_READ_UINT64_LE (data + 16);
+ preroll = GST_READ_UINT32_LE (data + 24);
+ pad->granuleshift = GST_READ_UINT8 (data + 28);
+
+ start_time = granulepos_to_granule_default (pad, start_granule);
+
+ if (p_start_time)
+ *p_start_time = start_time;
+
+ if (p_preroll)
+ *p_preroll = preroll;
+
+ return TRUE;
+}
+
+#define STREAMHEADER_OGM_AUDIO_MIN_SIZE 53
+#define STREAMHEADER_OGM_VIDEO_MIN_SIZE 53
+#define STREAMHEADER_OGM_TEXT_MIN_SIZE 9
+
+/* Do we need these for something?
+ * ogm->hdr.size = GST_READ_UINT32_LE (&data[13]);
+ * ogm->hdr.time_unit = GST_READ_UINT64_LE (&data[17]);
+ * ogm->hdr.samples_per_unit = GST_READ_UINT64_LE (&data[25]);
+ * ogm->hdr.default_len = GST_READ_UINT32_LE (&data[33]);
+ * ogm->hdr.buffersize = GST_READ_UINT32_LE (&data[37]);
+ * ogm->hdr.bits_per_sample = GST_READ_UINT32_LE (&data[41]);
+ */
+
+static gboolean
+setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+ guint size = packet->bytes;
+
+ if (size < STREAMHEADER_OGM_AUDIO_MIN_SIZE ||
+ memcmp (data, "\001audio\000\000\000", 9) != 0) {
+ GST_WARNING ("not an ogm audio identification header");
+ return FALSE;
+ }
+
+ pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
+ pad->granulerate_d = 1;
+
+ GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ /* FIXME */
+ pad->caps = gst_caps_new_simple ("audio/x-unknown",
+ "rate", G_TYPE_INT, pad->granulerate_n, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+ guint size = packet->bytes;
+
+ if (size < STREAMHEADER_OGM_VIDEO_MIN_SIZE ||
+ memcmp (data, "\001video\000\000\000", 9) != 0) {
+ GST_WARNING ("not an ogm video identification header");
+ return FALSE;
+ }
+
+ pad->granulerate_n = 10000000;
+ pad->granulerate_d = GST_READ_UINT64_LE (data + 17);
+
+ GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %.3f",
+ pad->granulerate_n, pad->granulerate_d,
+ (double) pad->granulerate_n / pad->granulerate_d);
+
+ if (pad->granulerate_d <= 0)
+ return FALSE;
+
+ /* FIXME */
+ pad->caps = gst_caps_new_simple ("video/x-unknown",
+ "framerate", GST_TYPE_FRACTION, pad->granulerate_n,
+ pad->granulerate_d, NULL);
+
+ return TRUE;
+}
+
+static gboolean
+setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+ guint size = packet->bytes;
+
+ if (size < STREAMHEADER_OGM_AUDIO_MIN_SIZE ||
+ memcmp (data, "\001text\000\000\000\000", 9) != 0) {
+ GST_WARNING ("not an ogm text identification header");
+ return FALSE;
+ }
+
+ pad->granulerate_n = 10000000;
+ pad->granulerate_d = GST_READ_UINT64_LE (data + 17);
+
+ GST_LOG ("fps = %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %.3f",
+ pad->granulerate_n, pad->granulerate_d,
+ (double) pad->granulerate_n / pad->granulerate_d);
+
+ if (pad->granulerate_d <= 0)
+ return FALSE;
+
+ /* FIXME */
+ pad->caps = gst_caps_new_simple ("text/x-unknown", NULL);
+
+ return TRUE;
+}
+
+/* PCM */
+
+#define OGGPCM_FMT_S8 0x00000000 /* Signed integer 8 bit */
+#define OGGPCM_FMT_U8 0x00000001 /* Unsigned integer 8 bit */
+#define OGGPCM_FMT_S16_LE 0x00000002 /* Signed integer 16 bit little endian */
+#define OGGPCM_FMT_S16_BE 0x00000003 /* Signed integer 16 bit big endian */
+#define OGGPCM_FMT_S24_LE 0x00000004 /* Signed integer 24 bit little endian */
+#define OGGPCM_FMT_S24_BE 0x00000005 /* Signed integer 24 bit big endian */
+#define OGGPCM_FMT_S32_LE 0x00000006 /* Signed integer 32 bit little endian */
+#define OGGPCM_FMT_S32_BE 0x00000007 /* Signed integer 32 bit big endian */
+
+#define OGGPCM_FMT_ULAW 0x00000010 /* G.711 u-law encoding (8 bit) */
+#define OGGPCM_FMT_ALAW 0x00000011 /* G.711 A-law encoding (8 bit) */
+
+#define OGGPCM_FMT_FLT32_LE 0x00000020 /* IEEE Float [-1,1] 32 bit little endian */
+#define OGGPCM_FMT_FLT32_BE 0x00000021 /* IEEE Float [-1,1] 32 bit big endian */
+#define OGGPCM_FMT_FLT64_LE 0x00000022 /* IEEE Float [-1,1] 64 bit little endian */
+#define OGGPCM_FMT_FLT64_BE 0x00000023 /* IEEE Float [-1,1] 64 bit big endian */
+
+
+static gboolean
+setup_pcm_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+ int format;
+ int channels;
+ GstCaps *caps;
+
+ pad->granulerate_n = GST_READ_UINT32_LE (data + 16);
+ pad->granulerate_d = 1;
+ GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+ format = GST_READ_UINT32_LE (data + 12);
+ channels = GST_READ_UINT8 (data + 21);
+
+ pad->n_header_packets = 2 + GST_READ_UINT32_LE (data + 24);
+
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ switch (format) {
+ case OGGPCM_FMT_S8:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 8,
+ "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+ break;
+ case OGGPCM_FMT_U8:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 8,
+ "width", G_TYPE_INT, 8, "signed", G_TYPE_BOOLEAN, FALSE, NULL);
+ break;
+ case OGGPCM_FMT_S16_LE:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 16,
+ "width", G_TYPE_INT, 16,
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+ "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+ break;
+ case OGGPCM_FMT_S16_BE:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 16,
+ "width", G_TYPE_INT, 16,
+ "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+ "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+ break;
+ case OGGPCM_FMT_S24_LE:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 24,
+ "width", G_TYPE_INT, 24,
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+ "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+ break;
+ case OGGPCM_FMT_S24_BE:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 24,
+ "width", G_TYPE_INT, 24,
+ "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+ "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+ break;
+ case OGGPCM_FMT_S32_LE:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 32,
+ "width", G_TYPE_INT, 32,
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
+ "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+ break;
+ case OGGPCM_FMT_S32_BE:
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "depth", G_TYPE_INT, 32,
+ "width", G_TYPE_INT, 32,
+ "endianness", G_TYPE_INT, G_BIG_ENDIAN,
+ "signed", G_TYPE_BOOLEAN, TRUE, NULL);
+ break;
+ case OGGPCM_FMT_ULAW:
+ caps = gst_caps_new_simple ("audio/x-mulaw", NULL);
+ break;
+ case OGGPCM_FMT_ALAW:
+ caps = gst_caps_new_simple ("audio/x-alaw", NULL);
+ break;
+ case OGGPCM_FMT_FLT32_LE:
+ caps = gst_caps_new_simple ("audio/x-raw-float",
+ "width", G_TYPE_INT, 32,
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
+ break;
+ case OGGPCM_FMT_FLT32_BE:
+ caps = gst_caps_new_simple ("audio/x-raw-float",
+ "width", G_TYPE_INT, 32,
+ "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
+ break;
+ case OGGPCM_FMT_FLT64_LE:
+ caps = gst_caps_new_simple ("audio/x-raw-float",
+ "width", G_TYPE_INT, 64,
+ "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, NULL);
+ break;
+ case OGGPCM_FMT_FLT64_BE:
+ caps = gst_caps_new_simple ("audio/x-raw-float",
+ "width", G_TYPE_INT, 64,
+ "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
+ break;
+ default:
+ return FALSE;
+ }
+
+ gst_caps_set_simple (caps, "audio/x-raw-int",
+ "rate", G_TYPE_INT, pad->granulerate_n,
+ "channels", G_TYPE_INT, channels, NULL);
+ pad->caps = caps;
+
+ return TRUE;
+}
+
+/* cmml */
+
+static gboolean
+setup_cmml_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+
+ pad->granulerate_n = GST_READ_UINT64_LE (data + 12);
+ pad->granulerate_d = GST_READ_UINT64_LE (data + 20);
+ pad->granuleshift = data[28];
+ GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+ pad->n_header_packets = 3;
+
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ data += 4 + (4 + 4 + 4);
+ GST_DEBUG ("blocksize0: %u", 1 << (data[0] >> 4));
+ GST_DEBUG ("blocksize1: %u", 1 << (data[0] & 0x0F));
+
+ pad->caps = gst_caps_new_simple ("text/x-cmml", NULL);
+
+ return TRUE;
+}
+
+/* celt */
+
+static gboolean
+setup_celt_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+
+ pad->granulerate_n = GST_READ_UINT32_LE (data + 36);
+ pad->granulerate_d = 1;
+ pad->granuleshift = 0;
+ GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+ pad->frame_size = GST_READ_UINT32_LE (packet->packet + 44);
+ pad->n_header_packets = GST_READ_UINT32_LE (packet->packet + 56) + 2;
+
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ pad->caps = gst_caps_new_simple ("audio/x-celt",
+ "rate", G_TYPE_INT, pad->granulerate_n, NULL);
+
+ return TRUE;
+}
+
+/* kate */
+
+static gboolean
+setup_kate_mapper (GstOggStream * pad, ogg_packet * packet)
+{
+ guint8 *data = packet->packet;
+
+ pad->granulerate_n = GST_READ_UINT32_LE (data + 24);
+ pad->granulerate_d = GST_READ_UINT32_LE (data + 28);
+ pad->granuleshift = GST_READ_UINT8 (data + 15);
+ GST_LOG ("sample rate: %" G_GUINT64_FORMAT, pad->granulerate_n);
+
+ pad->n_header_packets = GST_READ_UINT8 (data + 11);
+
+ if (pad->granulerate_n == 0)
+ return FALSE;
+
+ pad->caps = gst_caps_new_simple ("audio/x-kate",
+ "rate", G_TYPE_INT, pad->granulerate_n, NULL);
+
+ return TRUE;
+}
+
+
+/* *INDENT-OFF* */
+/* indent hates our freedoms */
+static const GstOggMap mappers[] = {
+ {
+ "\200theora", 7, 42,
+ "video/x-theora",
+ setup_theora_mapper,
+ granulepos_to_granule_default,
+ is_keyframe_theora,
+ is_header_theora,
+ packet_duration_constant
+ },
+ {
+ "\001vorbis", 7, 22,
+ "audio/x-vorbis",
+ setup_vorbis_mapper,
+ granulepos_to_granule_default,
+ is_keyframe_true,
+ is_header_vorbis,
+ packet_duration_vorbis
+ },
+ {
+ "Speex", 5, 80,
+ "audio/x-speex",
+ setup_speex_mapper,
+ granulepos_to_granule_default,
+ is_keyframe_true,
+ is_header_count,
+ packet_duration_constant
+ },
+ {
+ "PCM ", 8, 0,
+ "audio/x-raw-int",
+ setup_pcm_mapper,
+ NULL,
+ NULL,
+ is_header_count,
+ NULL
+ },
+ {
+ "CMML\0\0\0\0", 8, 0,
+ "text/x-cmml",
+ setup_cmml_mapper,
+ NULL,
+ NULL,
+ is_header_count,
+ NULL
+ },
+ {
+ "Annodex", 7, 0,
+ "application/x-annodex",
+ setup_fishead_mapper,
+ granulepos_to_granule_default,
+ NULL,
+ is_header_count,
+ NULL
+ },
+ {
+ "fishead", 7, 64,
+ "application/octet-stream",
+ setup_fishead_mapper,
+ NULL,
+ NULL,
+ is_header_true,
+ NULL
+ },
+ {
+ "fLaC", 4, 0,
+ "audio/x-flac",
+ setup_fLaC_mapper,
+ granulepos_to_granule_default,
+ NULL,
+ is_header_count,
+ NULL
+ },
+ {
+ "\177FLAC", 4, 36,
+ "audio/x-flac",
+ setup_flac_mapper,
+ granulepos_to_granule_default,
+ NULL,
+ is_header_count,
+ packet_duration_flac
+ },
+ {
+ "AnxData", 7, 0,
+ "application/octet-stream",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ },
+ {
+ "CELT ", 8, 0,
+ "audio/x-celt",
+ setup_celt_mapper,
+ granulepos_to_granule_default,
+ NULL,
+ is_header_count,
+ packet_duration_constant
+ },
+ {
+ "\200kate\0\0\0", 8, 0,
+ "text/x-kate",
+ setup_kate_mapper,
+ NULL,
+ NULL,
+ is_header_count,
+ NULL
+ },
+ {
+ "BBCD\0", 5, 13,
+ "video/x-dirac",
+ setup_dirac_mapper,
+ granulepos_to_granule_dirac,
+ is_keyframe_dirac,
+ is_header_count,
+ packet_duration_constant
+ },
+ {
+ "OGM audio", 100, 0,
+ "application/x-ogm-audio",
+ setup_ogmaudio_mapper,
+ granulepos_to_granule_default,
+ is_keyframe_true,
+ is_header_unknown,
+ NULL
+ },
+ {
+ "OGM video", 100, 0,
+ "application/x-ogm-video",
+ setup_ogmvideo_mapper,
+ granulepos_to_granule_default,
+ NULL,
+ is_header_unknown,
+ NULL
+ },
+ {
+ "OGM text", 100, 0,
+ "application/x-ogm-text",
+ setup_ogmtext_mapper,
+ granulepos_to_granule_default,
+ is_keyframe_true,
+ is_header_unknown,
+ NULL
+ }
+};
+/* *INDENT-ON* */
+
+gboolean
+gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet)
+{
+ int i;
+ gboolean ret;
+
+ for (i = 0; i < G_N_ELEMENTS (mappers); i++) {
+ if (packet->bytes >= mappers[i].min_packet_size &&
+ packet->bytes >= mappers[i].id_length &&
+ memcmp (packet->packet, mappers[i].id, mappers[i].id_length) == 0) {
+ ret = mappers[i].setup_func (pad, packet);
+ if (ret) {
+ GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps);
+ pad->map = i;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2009 David Schleef <ds@schleef.org>
+ *
+ * gstoggstream.h: header for GstOggStream
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_OGG_STREAM_H__
+#define __GST_OGG_STREAM_H__
+
+#include <ogg/ogg.h>
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GstOggStream GstOggStream;
+
+struct _GstOggStream
+{
+ ogg_stream_state stream;
+
+ guint32 serialno;
+ GList *headers;
+
+ /* for oggparse */
+ gboolean in_headers;
+ GList *unknown_pages;
+
+ gint map;
+ gboolean is_skeleton;
+ gboolean have_fisbone;
+ gint granulerate_n;
+ gint granulerate_d;
+ guint32 preroll;
+ guint granuleshift;
+ gint n_header_packets;
+ gint n_header_packets_seen;
+ gint64 accumulated_granule;
+ gint frame_size;
+
+ GstCaps *caps;
+
+ /* vorbis stuff */
+ int nln_increments[4];
+ int nsn_increment;
+ int short_size;
+ int long_size;
+ int vorbis_log2_num_modes;
+ int vorbis_mode_sizes[256];
+};
+
+
+gboolean gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet *packet);
+GstClockTime gst_ogg_stream_get_end_time_for_granulepos (GstOggStream *pad,
+ gint64 granulepos);
+GstClockTime gst_ogg_stream_get_start_time_for_granulepos (GstOggStream *pad,
+ gint64 granulepos);
+GstClockTime gst_ogg_stream_granule_to_time (GstOggStream *pad, gint64 granule);
+gint64 gst_ogg_stream_granulepos_to_granule (GstOggStream * pad, gint64 granulepos);
+GstClockTime gst_ogg_stream_get_packet_start_time (GstOggStream *pad,
+ ogg_packet *packet);
+gboolean gst_ogg_stream_granulepos_is_key_frame (GstOggStream *pad,
+ gint64 granulepos);
+gboolean gst_ogg_stream_packet_is_header (GstOggStream *pad, ogg_packet *packet);
+gint64 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet *packet);
+
+
+G_END_DECLS
+
+#endif /* __GST_OGG_STREAM_H__ */
--- /dev/null
+/*
+ This file borrowed from liboggz
+ */
+/*
+ Copyright (C) 2003 Commonwealth Scientific and Industrial Research
+ Organisation (CSIRO) Australia
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ - Neither the name of CSIRO Australia nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * oggz_auto.c
+ *
+ * Conrad Parker <conrad@annodex.net>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gstoggstream.h"
+
+/*
+ * Vorbis packets can be short or long, and each packet overlaps the previous
+ * and next packets. The granulepos of a packet is always the last sample
+ * that is completely decoded at the end of decoding that packet - i.e. the
+ * last packet before the first overlapping packet. If the sizes of packets
+ * are 's' and 'l', then the increment will depend on the previous and next
+ * packet types:
+ * v prev<<1 | next
+ * lll: l/2 3
+ * lls: 3l/4 - s/4 2
+ * lsl: s/2
+ * lss: s/2
+ * sll: l/4 + s/4 1
+ * sls: l/2 0
+ * ssl: s/2
+ * sss: s/2
+ *
+ * The previous and next packet types can be inferred from the current packet
+ * (additional information is not required)
+ *
+ * The two blocksizes can be determined from the first header packet, by reading
+ * byte 28. 1 << (packet[28] >> 4) == long_size.
+ * 1 << (packet[28] & 0xF) == short_size.
+ *
+ * (see http://xiph.org/vorbis/doc/Vorbis_I_spec.html for specification)
+ */
+
+
+void
+parse_vorbis_header_packet (GstOggStream * pad, ogg_packet * packet)
+{
+ /*
+ * on the first (b_o_s) packet, determine the long and short sizes,
+ * and then calculate l/2, l/4 - s/4, 3 * l/4 - s/4, l/2 - s/2 and s/2
+ */
+ int short_size;
+ int long_size;
+
+ long_size = 1 << (packet->packet[28] >> 4);
+ short_size = 1 << (packet->packet[28] & 0xF);
+
+ pad->nln_increments[3] = long_size >> 1;
+ pad->nln_increments[2] = 3 * (long_size >> 2) - (short_size >> 2);
+ pad->nln_increments[1] = (long_size >> 2) + (short_size >> 2);
+ pad->nln_increments[0] = pad->nln_increments[3];
+ pad->short_size = short_size;
+ pad->long_size = long_size;
+ pad->nsn_increment = short_size >> 1;
+
+ pad->accumulated_granule = -long_size / 2;
+}
+
+void
+parse_vorbis_setup_packet (GstOggStream * pad, ogg_packet * op)
+{
+ /*
+ * the code pages, a whole bunch of other fairly useless stuff, AND,
+ * RIGHT AT THE END (of a bunch of variable-length compressed rubbish that
+ * basically has only one actual set of values that everyone uses BUT YOU
+ * CAN'T BE SURE OF THAT, OH NO YOU CAN'T) is the only piece of data that's
+ * actually useful to us - the packet modes (because it's inconceivable to
+ * think people might want _just that_ and nothing else, you know, for
+ * seeking and stuff).
+ *
+ * Fortunately, because of the mandate that non-used bits must be zero
+ * at the end of the packet, we might be able to sneakily work backwards
+ * and find out the information we need (namely a mapping of modes to
+ * packet sizes)
+ */
+ unsigned char *current_pos = &op->packet[op->bytes - 1];
+ int offset;
+ int size;
+ int size_check;
+ int *mode_size_ptr;
+ int i;
+ int ii;
+
+ /*
+ * This is the format of the mode data at the end of the packet for all
+ * Vorbis Version 1 :
+ *
+ * [ 6:number_of_modes ]
+ * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ]
+ * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ]
+ * [ 1:size | 16:window_type(0) | 16:transform_type(0) | 8:mapping ]
+ * [ 1:framing(1) ]
+ *
+ * e.g.:
+ *
+ * <-
+ * 0 0 0 0 0 1 0 0
+ * 0 0 1 0 0 0 0 0
+ * 0 0 1 0 0 0 0 0
+ * 0 0 1|0 0 0 0 0
+ * 0 0 0 0|0|0 0 0
+ * 0 0 0 0 0 0 0 0
+ * 0 0 0 0|0 0 0 0
+ * 0 0 0 0 0 0 0 0
+ * 0 0 0 0|0 0 0 0
+ * 0 0 0|1|0 0 0 0 |
+ * 0 0 0 0 0 0 0 0 V
+ * 0 0 0|0 0 0 0 0
+ * 0 0 0 0 0 0 0 0
+ * 0 0 1|0 0 0 0 0
+ * 0 0|1|0 0 0 0 0
+ *
+ *
+ * i.e. each entry is an important bit, 32 bits of 0, 8 bits of blah, a
+ * bit of 1.
+ * Let's find our last 1 bit first.
+ *
+ */
+
+ size = 0;
+
+ offset = 8;
+ while (!((1 << --offset) & *current_pos)) {
+ if (offset == 0) {
+ offset = 8;
+ current_pos -= 1;
+ }
+ }
+
+ while (1) {
+
+ /*
+ * from current_pos-5:(offset+1) to current_pos-1:(offset+1) should
+ * be zero
+ */
+ offset = (offset + 7) % 8;
+ if (offset == 7)
+ current_pos -= 1;
+
+ if (((current_pos[-5] & ~((1 << (offset + 1)) - 1)) != 0)
+ ||
+ current_pos[-4] != 0
+ ||
+ current_pos[-3] != 0
+ ||
+ current_pos[-2] != 0
+ || ((current_pos[-1] & ((1 << (offset + 1)) - 1)) != 0)
+ ) {
+ break;
+ }
+
+ size += 1;
+
+ current_pos -= 5;
+
+ }
+
+ /* Give ourselves a chance to recover if we went back too far by using
+ * the size check. */
+ for (ii = 0; ii < 2; ii++) {
+ if (offset > 4) {
+ size_check = (current_pos[0] >> (offset - 5)) & 0x3F;
+ } else {
+ /* mask part of byte from current_pos */
+ size_check = (current_pos[0] & ((1 << (offset + 1)) - 1));
+ /* shift to appropriate position */
+ size_check <<= (5 - offset);
+ /* or in part of byte from current_pos - 1 */
+ size_check |= (current_pos[-1] & ~((1 << (offset + 3)) - 1)) >>
+ (offset + 3);
+ }
+
+ size_check += 1;
+ if (size_check == size) {
+ break;
+ }
+ offset = (offset + 1) % 8;
+ if (offset == 0)
+ current_pos += 1;
+ current_pos += 5;
+ size -= 1;
+ }
+
+ /* Store mode size information in our info struct */
+ i = -1;
+ while ((1 << (++i)) < size);
+ pad->vorbis_log2_num_modes = i;
+
+ mode_size_ptr = pad->vorbis_mode_sizes;
+
+ for (i = 0; i < size; i++) {
+ offset = (offset + 1) % 8;
+ if (offset == 0)
+ current_pos += 1;
+ *mode_size_ptr++ = (current_pos[0] >> offset) & 0x1;
+ current_pos += 5;
+ }
+
+}