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; + } + +}