From 72edd1467bcb5040d1e89e782fc46059743e8646 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Sat, 29 Aug 2009 10:51:48 -0700 Subject: [PATCH] ogg: Add ogg stream parsing Adds code that parses headers of various formats encapsulated in Ogg in order to calculate timestamps and durations of each buffer. Removes the creation of helper decoder elements to do this calculation via conversion queries. Fixes: #344013, #568014. --- ext/ogg/Makefile.am | 7 +- ext/ogg/dirac_parse.c | 501 +++++++++++++++++++ ext/ogg/dirac_parse.h | 178 +++++++ ext/ogg/gstoggdemux.c | 667 +++++-------------------- ext/ogg/gstoggdemux.h | 25 +- ext/ogg/gstoggparse.c | 105 ++-- ext/ogg/gstoggstream.c | 1062 ++++++++++++++++++++++++++++++++++++++++ ext/ogg/gstoggstream.h | 85 ++++ ext/ogg/vorbis_parse.c | 241 +++++++++ 9 files changed, 2254 insertions(+), 617 deletions(-) create mode 100644 ext/ogg/dirac_parse.c create mode 100644 ext/ogg/dirac_parse.h create mode 100644 ext/ogg/gstoggstream.c create mode 100644 ext/ogg/gstoggstream.h create mode 100644 ext/ogg/vorbis_parse.c diff --git a/ext/ogg/Makefile.am b/ext/ogg/Makefile.am index dbb050c317..2d152335df 100644 --- a/ext/ogg/Makefile.am +++ b/ext/ogg/Makefile.am @@ -6,7 +6,12 @@ libgstogg_la_SOURCES = \ 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 diff --git a/ext/ogg/dirac_parse.c b/ext/ogg/dirac_parse.c new file mode 100644 index 0000000000..f7b483dc17 --- /dev/null +++ b/ext/ogg/dirac_parse.c @@ -0,0 +1,501 @@ + +#include "dirac_parse.h" +#include + +#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; +} diff --git a/ext/ogg/dirac_parse.h b/ext/ogg/dirac_parse.h new file mode 100644 index 0000000000..9dc4ffec5f --- /dev/null +++ b/ext/ogg/dirac_parse.h @@ -0,0 +1,178 @@ + +#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 + diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index ffe2c25de6..dd7c99daab 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -41,7 +41,6 @@ #endif #include #include -#include #include "gstoggdemux.h" @@ -60,8 +59,8 @@ GST_ELEMENT_DETAILS ("Ogg demuxer", #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 * @@ -86,12 +85,6 @@ gst_ogg_page_free (ogg_page * 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, @@ -108,10 +101,6 @@ static void gst_ogg_pad_init (GstOggPad * pad); 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); @@ -119,12 +108,6 @@ static GstCaps *gst_ogg_pad_getcaps (GstPad * pad); 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); @@ -156,50 +139,35 @@ gst_ogg_pad_init (GstOggPad * pad) 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); } @@ -209,43 +177,11 @@ gst_ogg_pad_finalize (GObject * 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) { @@ -408,7 +344,7 @@ error: 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"); @@ -421,84 +357,6 @@ gst_ogg_pad_reset (GstOggPad * pad) 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 @@ -511,27 +369,18 @@ gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet) 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)); @@ -542,51 +391,34 @@ gst_ogg_pad_parse_skeleton_fishead (GstOggPad * pad, ogg_packet * packet) 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; + fisbone_pad->map.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.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, @@ -594,231 +426,6 @@ gst_ogg_pad_parse_skeleton_fisbone (GstOggPad * pad, ogg_packet * packet) } } -/* 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. */ @@ -831,13 +438,14 @@ gst_ogg_demux_queue_data (GstOggPad * pad, ogg_packet * packet) 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; @@ -850,12 +458,11 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet) 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), @@ -869,6 +476,10 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet) /* 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; @@ -886,7 +497,7 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet) 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 @@ -898,11 +509,8 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet) 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)) { @@ -929,15 +537,10 @@ no_buffer: { 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 @@ -952,15 +555,18 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) 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 && @@ -973,42 +579,36 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) 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 (!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; + } + + 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++; } - if (granule != -1 && memcmp (packet->packet, "KW-DIRAC", 8) == 0) { - return GST_FLOW_OK; - } - - /* 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); - } /* 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) { @@ -1065,7 +665,7 @@ gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets) 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"); @@ -1099,7 +699,7 @@ gst_ogg_pad_stream_out (GstOggPad * pad, gint npackets) 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; @@ -1144,7 +744,7 @@ gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page) } } - 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 @@ -1159,7 +759,7 @@ gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page) 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); @@ -1175,7 +775,7 @@ gst_ogg_pad_submit_page (GstOggPad * pad, ogg_page * page) /* 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: @@ -1193,7 +793,7 @@ choked: { 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; @@ -1279,8 +879,8 @@ gst_ogg_chain_new_stream (GstOggChain * chain, glong serialno) 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); @@ -1318,7 +918,7 @@ gst_ogg_chain_get_stream (GstOggChain * chain, glong 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; @@ -1360,8 +960,6 @@ static GstStaticPadTemplate ogg_demux_sink_template_factory = 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, @@ -1764,7 +1362,7 @@ gst_ogg_demux_deactivate_current_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 (); @@ -1810,7 +1408,7 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, 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); @@ -1861,7 +1459,7 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, 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) { @@ -1873,8 +1471,8 @@ gst_ogg_demux_activate_chain (GstOggDemux * ogg, GstOggChain * chain, 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; } @@ -1991,7 +1589,6 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, /* 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, @@ -2003,23 +1600,17 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, } 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 %" @@ -2499,17 +2090,18 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain) 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). @@ -2519,10 +2111,10 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain) } } /* 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 @@ -2544,24 +2136,10 @@ gst_ogg_demux_read_chain (GstOggDemux * ogg, GstOggChain ** res_chain) /* 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; } @@ -2581,7 +2159,6 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain) gint64 end = begin; gint64 last_granule = -1; GstOggPad *last_pad = NULL; - GstFormat target; GstFlowReturn ret; gboolean done = FALSE; ogg_page og; @@ -2608,13 +2185,13 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain) 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; } @@ -2627,14 +2204,16 @@ gst_ogg_demux_read_end_chain (GstOggDemux * ogg, GstOggChain * chain) } } - 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; } @@ -2699,7 +2278,7 @@ gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain) 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 */ @@ -2713,6 +2292,8 @@ gst_ogg_demux_collect_chain_info (GstOggDemux * ogg, GstOggChain * chain) && 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; @@ -3380,30 +2961,6 @@ gst_ogg_demux_change_state (GstElement * element, GstStateChange transition) 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) { @@ -3463,13 +3020,9 @@ gst_ogg_print (GstOggDemux * ogg) 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)); } } } diff --git a/ext/ogg/gstoggdemux.h b/ext/ogg/gstoggdemux.h index 6cc7fb9b78..089f3c1c63 100644 --- a/ext/ogg/gstoggdemux.h +++ b/ext/ogg/gstoggdemux.h @@ -26,6 +26,8 @@ #include +#include "gstoggstream.h" + G_BEGIN_DECLS #define GST_TYPE_OGG_PAD (gst_ogg_pad_get_type()) @@ -43,7 +45,7 @@ typedef struct _GstOggPadClass GstOggPadClass; #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; @@ -87,23 +89,32 @@ struct _GstOggPad 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; @@ -116,7 +127,6 @@ struct _GstOggPad 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; @@ -183,6 +193,7 @@ struct _GstOggDemuxClass GstElementClass parent_class; }; + G_END_DECLS #endif /* __GST_OGG_DEMUX_H__ */ diff --git a/ext/ogg/gstoggparse.c b/ext/ogg/gstoggparse.c index c3dddd3ce5..1ceae91ec9 100644 --- a/ext/ogg/gstoggparse.c +++ b/ext/ogg/gstoggparse.c @@ -36,6 +36,8 @@ #include #include +#include "gstoggstream.h" + static const GstElementDetails gst_ogg_parse_details = GST_ELEMENT_DETAILS ("Ogg parser", "Codec/Parser", @@ -56,22 +58,6 @@ static GType gst_ogg_parse_get_type (void); 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; @@ -130,8 +116,8 @@ gst_ogg_parse_get_type (void) 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); } @@ -145,28 +131,40 @@ gst_ogg_parse_delete_all_streams (GstOggParse * ogg) } 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 * @@ -400,24 +398,27 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) /* 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 %" @@ -444,12 +445,12 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) 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, @@ -488,7 +489,7 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) 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) { @@ -499,11 +500,11 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) /* 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) { @@ -527,7 +528,7 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) 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; @@ -542,9 +543,9 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) 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++; } } @@ -581,9 +582,9 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) 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); @@ -597,7 +598,7 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) /* 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; @@ -609,7 +610,7 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) 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; @@ -620,9 +621,9 @@ gst_ogg_parse_chain (GstPad * pad, GstBuffer * buffer) 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; } diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c new file mode 100644 index 0000000000..0511d849d9 --- /dev/null +++ b/ext/ogg/gstoggstream.c @@ -0,0 +1,1062 @@ +/* GStreamer Ogg Granulepos Mapping Utility Functions + * Copyright (C) 2006 Tim-Philipp Müller + * Copyright (C) 2009 David Schleef + * + * 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 + +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; +} diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h new file mode 100644 index 0000000000..6f3310c52c --- /dev/null +++ b/ext/ogg/gstoggstream.h @@ -0,0 +1,85 @@ +/* GStreamer + * Copyright (C) 2009 David Schleef + * + * 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 + +#include + +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__ */ diff --git a/ext/ogg/vorbis_parse.c b/ext/ogg/vorbis_parse.c new file mode 100644 index 0000000000..c1d4fe02df --- /dev/null +++ b/ext/ogg/vorbis_parse.c @@ -0,0 +1,241 @@ +/* + 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 + */ + +#include "config.h" + +#include +#include + +#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; + } + +}