2009-08-29 17:51:48 +00:00
|
|
|
/* GStreamer Ogg Granulepos Mapping Utility Functions
|
|
|
|
* Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
|
|
|
|
* Copyright (C) 2009 David Schleef <ds@schleef.org>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "gstoggstream.h"
|
|
|
|
#include "dirac_parse.h"
|
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
#include <gst/riff/riff-media.h>
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_debug);
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_ogg_demux_setup_debug);
|
|
|
|
#define GST_CAT_DEFAULT gst_ogg_demux_debug
|
|
|
|
|
|
|
|
typedef struct _GstOggMap GstOggMap;
|
|
|
|
|
|
|
|
typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad,
|
|
|
|
ogg_packet * packet);
|
|
|
|
typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad,
|
|
|
|
gint64 granulepos);
|
|
|
|
typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad,
|
|
|
|
gint64 granulepos);
|
2009-11-25 05:22:03 +00:00
|
|
|
typedef gint64 (*GstOggMapToGranuleposFunc) (GstOggStream * pad,
|
|
|
|
gint64 granule, gint64 keyframe_granule);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
/* 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;
|
2009-11-25 05:22:03 +00:00
|
|
|
GstOggMapToGranuleposFunc granule_to_granulepos_func;
|
2009-08-29 17:51:48 +00:00
|
|
|
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) {
|
2009-11-25 05:22:03 +00:00
|
|
|
GST_WARNING ("Failed to convert granulepos to granule");
|
|
|
|
return -1;
|
2009-08-29 17:51:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return mappers[pad->map].granulepos_to_granule_func (pad, granulepos);
|
|
|
|
}
|
|
|
|
|
2009-11-25 06:08:09 +00:00
|
|
|
gint64
|
|
|
|
gst_ogg_stream_granulepos_to_key_granule (GstOggStream * pad, gint64 granulepos)
|
|
|
|
{
|
|
|
|
if (granulepos == -1 || granulepos == 0) {
|
|
|
|
return granulepos;
|
|
|
|
}
|
|
|
|
|
|
|
|
return granulepos >> pad->granuleshift;
|
|
|
|
}
|
|
|
|
|
2009-11-25 05:22:03 +00:00
|
|
|
gint64
|
|
|
|
gst_ogg_stream_granule_to_granulepos (GstOggStream * pad, gint64 granule,
|
|
|
|
gint64 keyframe_granule)
|
|
|
|
{
|
|
|
|
if (granule == -1 || granule == 0) {
|
|
|
|
return granule;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mappers[pad->map].granule_to_granulepos_func == NULL) {
|
|
|
|
GST_WARNING ("Failed to convert granule to granulepos");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mappers[pad->map].granule_to_granulepos_func (pad, granule,
|
|
|
|
keyframe_granule);
|
|
|
|
}
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-25 06:08:09 +00:00
|
|
|
|
2009-11-25 05:22:03 +00:00
|
|
|
static gint64
|
|
|
|
granule_to_granulepos_default (GstOggStream * pad, gint64 granule,
|
|
|
|
gint64 keyframe_granule)
|
|
|
|
{
|
|
|
|
gint64 keyoffset;
|
|
|
|
|
|
|
|
if (pad->granuleshift != 0) {
|
|
|
|
keyoffset = granule - keyframe_granule;
|
|
|
|
return (keyframe_granule << pad->granuleshift) | keyoffset;
|
|
|
|
} else {
|
|
|
|
return granule;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
#ifdef unused
|
2009-08-29 17:51:48 +00:00
|
|
|
static gboolean
|
|
|
|
is_header_unknown (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
GST_WARNING ("don't know how to detect header");
|
|
|
|
return FALSE;
|
|
|
|
}
|
2009-12-04 04:05:29 +00:00
|
|
|
#endif
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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;
|
2009-12-02 02:08:46 +00:00
|
|
|
guint w, h, par_d, par_n;
|
|
|
|
|
|
|
|
w = GST_READ_UINT24_BE (data + 14) & 0xFFFFF0;
|
|
|
|
h = GST_READ_UINT24_BE (data + 17) & 0xFFFFF0;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
pad->granulerate_n = GST_READ_UINT32_BE (data + 22);
|
|
|
|
pad->granulerate_d = GST_READ_UINT32_BE (data + 26);
|
2009-12-02 02:08:46 +00:00
|
|
|
|
|
|
|
par_n = GST_READ_UINT24_BE (data + 30);
|
|
|
|
par_d = GST_READ_UINT24_BE (data + 33);
|
|
|
|
|
|
|
|
GST_LOG ("fps = %d/%d, PAR = %u/%u, width = %u, height = %u",
|
|
|
|
pad->granulerate_n, pad->granulerate_d, par_n, par_d, w, h);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2009-12-04 01:02:11 +00:00
|
|
|
if (pad->granulerate_n == 0 || pad->granulerate_d == 0) {
|
|
|
|
GST_WARNING ("frame rate %d/%d", pad->granulerate_n, pad->granulerate_d);
|
2009-08-29 17:51:48 +00:00
|
|
|
return FALSE;
|
2009-12-04 01:02:11 +00:00
|
|
|
}
|
2009-08-29 17:51:48 +00:00
|
|
|
|
2009-12-02 02:08:46 +00:00
|
|
|
pad->caps = gst_caps_new_simple ("video/x-theora", NULL);
|
|
|
|
|
|
|
|
if (w > 0 && h > 0) {
|
|
|
|
gst_caps_set_simple (pad->caps, "width", G_TYPE_INT, w, "height",
|
|
|
|
G_TYPE_INT, h, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* only add framerate now so caps look prettier, with width/height first */
|
|
|
|
gst_caps_set_simple (pad->caps, "framerate", GST_TYPE_FRACTION,
|
|
|
|
pad->granulerate_n, pad->granulerate_d, NULL);
|
|
|
|
|
|
|
|
if (par_n > 0 && par_d > 0) {
|
|
|
|
gst_caps_set_simple (pad->caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
|
|
|
|
par_n, par_d, NULL);
|
|
|
|
}
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-11-26 01:04:41 +00:00
|
|
|
static gint64
|
|
|
|
granulepos_to_granule_theora (GstOggStream * pad, gint64 granulepos)
|
|
|
|
{
|
|
|
|
gint64 keyindex, keyoffset;
|
|
|
|
|
|
|
|
if (pad->granuleshift != 0) {
|
|
|
|
keyindex = granulepos >> pad->granuleshift;
|
|
|
|
keyoffset = granulepos - (keyindex << pad->granuleshift);
|
|
|
|
if (keyoffset == 0) {
|
|
|
|
pad->theora_has_zero_keyoffset = TRUE;
|
|
|
|
}
|
|
|
|
if (pad->theora_has_zero_keyoffset) {
|
|
|
|
keyoffset++;
|
|
|
|
}
|
|
|
|
return keyindex + keyoffset;
|
|
|
|
} else {
|
|
|
|
return granulepos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
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;
|
|
|
|
|
2009-12-03 21:07:49 +00:00
|
|
|
GST_DEBUG ("pt %" G_GINT64_FORMAT " delay %d", pt, delay);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
return dt + 4;
|
|
|
|
}
|
|
|
|
|
2009-11-25 05:22:03 +00:00
|
|
|
static gint64
|
|
|
|
granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule,
|
|
|
|
gint64 keyframe_granule)
|
|
|
|
{
|
|
|
|
/* This conversion requires knowing more details about the Dirac
|
|
|
|
* stream. */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
/* 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;
|
2009-12-03 23:43:08 +00:00
|
|
|
guint chans;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
2009-12-03 23:43:08 +00:00
|
|
|
data += 1 + 6 + 4;
|
|
|
|
chans = GST_READ_UINT8 (data);
|
|
|
|
data += 1;
|
2009-08-29 17:51:48 +00:00
|
|
|
pad->granulerate_n = GST_READ_UINT32_LE (data);
|
|
|
|
pad->granulerate_d = 1;
|
|
|
|
pad->granuleshift = 0;
|
2009-11-25 05:22:03 +00:00
|
|
|
pad->last_size = 0;
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("sample rate: %d", pad->granulerate_n);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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",
|
2009-12-03 23:43:08 +00:00
|
|
|
"rate", G_TYPE_INT, pad->granulerate_n, "channels", G_TYPE_INT, chans,
|
|
|
|
NULL);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2009-11-25 05:22:03 +00:00
|
|
|
if (packet->packet[0] & 1)
|
|
|
|
return 0;
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
mode = (packet->packet[0] >> 1) & ((1 << pad->vorbis_log2_num_modes) - 1);
|
2009-11-25 05:22:03 +00:00
|
|
|
size = pad->vorbis_mode_sizes[mode] ? pad->long_size : pad->short_size;
|
|
|
|
|
|
|
|
if (pad->last_size == 0) {
|
|
|
|
duration = 0;
|
2009-08-29 17:51:48 +00:00
|
|
|
} else {
|
2009-11-25 05:22:03 +00:00
|
|
|
duration = pad->last_size / 4 + size / 4;
|
2009-08-29 17:51:48 +00:00
|
|
|
}
|
2009-11-25 05:22:03 +00:00
|
|
|
pad->last_size = size;
|
|
|
|
|
|
|
|
GST_DEBUG ("duration %d", (int) duration);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
return duration;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* speex */
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
setup_speex_mapper (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
guint8 *data = packet->packet;
|
2009-12-03 23:43:08 +00:00
|
|
|
guint chans;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
data += 8 + 20 + 4 + 4;
|
|
|
|
pad->granulerate_n = GST_READ_UINT32_LE (data);
|
|
|
|
pad->granulerate_d = 1;
|
|
|
|
pad->granuleshift = 0;
|
2009-12-03 23:43:08 +00:00
|
|
|
|
|
|
|
data += 4 + 4 + 4;
|
|
|
|
chans = GST_READ_UINT32_LE (data);
|
|
|
|
|
|
|
|
GST_LOG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2009-12-03 23:43:08 +00:00
|
|
|
pad->caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT,
|
|
|
|
pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* flac */
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
setup_fLaC_mapper (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
2009-12-11 06:47:53 +00:00
|
|
|
pad->granulerate_n = 0;
|
|
|
|
pad->granulerate_d = 1;
|
|
|
|
pad->granuleshift = 0;
|
|
|
|
|
|
|
|
pad->n_header_packets = 3;
|
|
|
|
|
|
|
|
pad->caps = gst_caps_new_simple ("audio/x-flac", NULL);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
is_header_fLaC (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
if (pad->n_header_packets_seen == 1) {
|
|
|
|
pad->granulerate_n = (packet->packet[14] << 12) |
|
|
|
|
(packet->packet[15] << 4) | ((packet->packet[16] >> 4) & 0xf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pad->n_header_packets_seen < pad->n_header_packets) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
setup_flac_mapper (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
guint8 *data = packet->packet;
|
2009-12-03 23:43:08 +00:00
|
|
|
guint chans;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
/* 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;
|
2009-12-03 23:43:08 +00:00
|
|
|
chans = ((GST_READ_UINT32_BE (data + 27) & 0x00000E00) >> 9) + 1;
|
|
|
|
|
|
|
|
GST_DEBUG ("sample rate: %d, channels: %u", pad->granulerate_n, chans);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
pad->n_header_packets = GST_READ_UINT16_BE (packet->packet + 7);
|
|
|
|
|
|
|
|
if (pad->granulerate_n == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2009-12-03 23:43:08 +00:00
|
|
|
pad->caps = gst_caps_new_simple ("audio/x-flac", "rate", G_TYPE_INT,
|
|
|
|
pad->granulerate_n, "channels", G_TYPE_INT, chans, NULL);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2010-01-06 12:57:51 +00:00
|
|
|
static gboolean
|
|
|
|
is_header_flac (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
return (packet->bytes > 0 && (packet->packet[0] != 0xff));
|
|
|
|
}
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
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);
|
|
|
|
}
|
2010-01-05 13:11:06 +00:00
|
|
|
if (block_size_index == 6 || block_size_index == 7) {
|
|
|
|
guint len, bytes = (block_size_index - 6) + 1;
|
|
|
|
guint8 tmp;
|
|
|
|
|
|
|
|
if (packet->bytes < 4 + 1 + bytes)
|
|
|
|
return -1;
|
|
|
|
tmp = packet->packet[4];
|
|
|
|
/* utf-8 prefix */
|
|
|
|
len = 0;
|
|
|
|
while (tmp & 0x80) {
|
|
|
|
len++;
|
|
|
|
tmp <<= 1;
|
|
|
|
}
|
|
|
|
if (len == 2)
|
|
|
|
return -1;
|
|
|
|
if (len == 0)
|
|
|
|
len++;
|
|
|
|
if (packet->bytes < 4 + len + bytes)
|
|
|
|
return -1;
|
|
|
|
if (bytes == 1) {
|
|
|
|
return packet->packet[4 + len] + 1;
|
|
|
|
} else {
|
|
|
|
return GST_READ_UINT16_BE (packet->packet + 4 + len) + 1;
|
|
|
|
}
|
|
|
|
}
|
2009-08-29 17:51:48 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|
2009-12-04 04:05:29 +00:00
|
|
|
is_header_ogm (GstOggStream * pad, ogg_packet * packet)
|
2009-08-29 17:51:48 +00:00
|
|
|
{
|
2009-12-11 11:02:15 +00:00
|
|
|
if (packet->bytes >= 1 && (packet->packet[0] & 0x01))
|
2009-12-04 04:05:29 +00:00
|
|
|
return TRUE;
|
2009-12-11 11:02:15 +00:00
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2009-08-29 17:51:48 +00:00
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
static gint64
|
|
|
|
packet_duration_ogm (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
const guint8 *data;
|
|
|
|
int samples;
|
|
|
|
int offset;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
data = packet->packet;
|
|
|
|
offset = 1 + (((data[0] & 0xc0) >> 6) | ((data[0] & 0x02) << 1));
|
|
|
|
|
|
|
|
if (offset > packet->bytes) {
|
|
|
|
GST_ERROR ("buffer too small");
|
|
|
|
return -1;
|
2009-08-29 17:51:48 +00:00
|
|
|
}
|
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
samples = 0;
|
|
|
|
for (n = offset - 1; n > 0; n--) {
|
|
|
|
samples = (samples << 8) | data[n];
|
|
|
|
}
|
|
|
|
|
|
|
|
return samples;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
setup_ogmaudio_mapper (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
guint8 *data = packet->packet;
|
|
|
|
guint32 fourcc;
|
|
|
|
|
2009-08-29 17:51:48 +00:00
|
|
|
pad->granulerate_n = GST_READ_UINT64_LE (data + 25);
|
|
|
|
pad->granulerate_d = 1;
|
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
fourcc = GST_READ_UINT32_LE (data + 9);
|
|
|
|
GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
|
|
|
|
|
|
|
|
pad->caps = gst_riff_create_audio_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("sample rate: %d", pad->granulerate_n);
|
2009-08-29 17:51:48 +00:00
|
|
|
if (pad->granulerate_n == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
if (pad->caps) {
|
|
|
|
gst_caps_set_simple (pad->caps,
|
|
|
|
"rate", G_TYPE_INT, pad->granulerate_n, NULL);
|
|
|
|
} else {
|
|
|
|
pad->caps = gst_caps_new_simple ("audio/x-ogm-unknown",
|
|
|
|
"fourcc", GST_TYPE_FOURCC, fourcc,
|
|
|
|
"rate", G_TYPE_INT, pad->granulerate_n, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
pad->n_header_packets = 1;
|
|
|
|
pad->is_ogm = TRUE;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
setup_ogmvideo_mapper (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
guint8 *data = packet->packet;
|
2009-12-04 04:05:29 +00:00
|
|
|
guint32 fourcc;
|
|
|
|
int width, height;
|
|
|
|
gint64 time_unit;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
GST_DEBUG ("time unit %d", GST_READ_UINT32_LE (data + 16));
|
|
|
|
GST_DEBUG ("samples per unit %d", GST_READ_UINT32_LE (data + 24));
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
pad->granulerate_n = 10000000;
|
2009-12-04 04:05:29 +00:00
|
|
|
time_unit = GST_READ_UINT64_LE (data + 17);
|
|
|
|
if (time_unit > G_MAXINT || time_unit < G_MININT) {
|
|
|
|
GST_WARNING ("timeunit is out of range");
|
|
|
|
}
|
|
|
|
pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("fps = %d/%d = %.3f",
|
2009-08-29 17:51:48 +00:00
|
|
|
pad->granulerate_n, pad->granulerate_d,
|
|
|
|
(double) pad->granulerate_n / pad->granulerate_d);
|
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
fourcc = GST_READ_UINT32_LE (data + 9);
|
|
|
|
width = GST_READ_UINT32_LE (data + 45);
|
|
|
|
height = GST_READ_UINT32_LE (data + 49);
|
|
|
|
GST_DEBUG ("fourcc: %" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (fourcc));
|
|
|
|
|
|
|
|
pad->caps = gst_riff_create_video_caps (fourcc, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
if (pad->caps == NULL) {
|
|
|
|
pad->caps = gst_caps_new_simple ("video/x-ogm-unknown",
|
|
|
|
"fourcc", GST_TYPE_FOURCC, fourcc,
|
|
|
|
"framerate", GST_TYPE_FRACTION, pad->granulerate_n,
|
|
|
|
pad->granulerate_d, NULL);
|
|
|
|
} else {
|
|
|
|
gst_caps_set_simple (pad->caps,
|
|
|
|
"framerate", GST_TYPE_FRACTION, pad->granulerate_n,
|
|
|
|
pad->granulerate_d,
|
|
|
|
"width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
|
|
|
|
}
|
|
|
|
GST_DEBUG ("caps: %" GST_PTR_FORMAT, pad->caps);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
pad->n_header_packets = 1;
|
|
|
|
pad->frame_size = 1;
|
|
|
|
pad->is_ogm = TRUE;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
setup_ogmtext_mapper (GstOggStream * pad, ogg_packet * packet)
|
|
|
|
{
|
|
|
|
guint8 *data = packet->packet;
|
2009-12-04 04:05:29 +00:00
|
|
|
gint64 time_unit;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
pad->granulerate_n = 10000000;
|
2009-12-04 04:05:29 +00:00
|
|
|
time_unit = GST_READ_UINT64_LE (data + 17);
|
|
|
|
if (time_unit > G_MAXINT || time_unit < G_MININT) {
|
|
|
|
GST_WARNING ("timeunit is out of range");
|
|
|
|
}
|
|
|
|
pad->granulerate_d = (gint) CLAMP (time_unit, G_MININT, G_MAXINT);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("fps = %d/%d = %.3f",
|
2009-08-29 17:51:48 +00:00
|
|
|
pad->granulerate_n, pad->granulerate_d,
|
|
|
|
(double) pad->granulerate_n / pad->granulerate_d);
|
|
|
|
|
|
|
|
if (pad->granulerate_d <= 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2009-12-04 04:05:29 +00:00
|
|
|
pad->caps = gst_caps_new_simple ("text/plain", NULL);
|
|
|
|
|
|
|
|
pad->n_header_packets = 1;
|
|
|
|
pad->is_ogm = TRUE;
|
|
|
|
pad->is_ogm_text = TRUE;
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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;
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("sample rate: %d", pad->granulerate_n);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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];
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("sample rate: %d", pad->granulerate_n);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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;
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("sample rate: %d", pad->granulerate_n);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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);
|
2009-12-03 19:57:48 +00:00
|
|
|
GST_LOG ("sample rate: %d", pad->granulerate_n);
|
2009-08-29 17:51:48 +00:00
|
|
|
|
|
|
|
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,
|
2009-11-26 01:04:41 +00:00
|
|
|
granulepos_to_granule_theora,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_keyframe_theora,
|
|
|
|
is_header_theora,
|
|
|
|
packet_duration_constant
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"\001vorbis", 7, 22,
|
|
|
|
"audio/x-vorbis",
|
|
|
|
setup_vorbis_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_keyframe_true,
|
|
|
|
is_header_vorbis,
|
|
|
|
packet_duration_vorbis
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Speex", 5, 80,
|
|
|
|
"audio/x-speex",
|
|
|
|
setup_speex_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_keyframe_true,
|
|
|
|
is_header_count,
|
|
|
|
packet_duration_constant
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"PCM ", 8, 0,
|
|
|
|
"audio/x-raw-int",
|
|
|
|
setup_pcm_mapper,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2009-11-25 05:22:03 +00:00
|
|
|
NULL,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_header_count,
|
|
|
|
NULL
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"CMML\0\0\0\0", 8, 0,
|
|
|
|
"text/x-cmml",
|
|
|
|
setup_cmml_mapper,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2009-11-25 05:22:03 +00:00
|
|
|
NULL,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_header_count,
|
|
|
|
NULL
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Annodex", 7, 0,
|
|
|
|
"application/x-annodex",
|
|
|
|
setup_fishead_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
NULL,
|
|
|
|
is_header_count,
|
|
|
|
NULL
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"fishead", 7, 64,
|
|
|
|
"application/octet-stream",
|
|
|
|
setup_fishead_mapper,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2009-11-25 05:22:03 +00:00
|
|
|
NULL,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_header_true,
|
|
|
|
NULL
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"fLaC", 4, 0,
|
|
|
|
"audio/x-flac",
|
|
|
|
setup_fLaC_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-12-11 06:47:53 +00:00
|
|
|
is_keyframe_true,
|
|
|
|
is_header_fLaC,
|
2009-08-29 17:51:48 +00:00
|
|
|
NULL
|
|
|
|
},
|
|
|
|
{
|
2010-01-05 13:11:06 +00:00
|
|
|
"\177FLAC", 5, 36,
|
2009-08-29 17:51:48 +00:00
|
|
|
"audio/x-flac",
|
|
|
|
setup_flac_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-12-11 06:47:53 +00:00
|
|
|
is_keyframe_true,
|
2010-01-06 12:57:51 +00:00
|
|
|
is_header_flac,
|
2009-08-29 17:51:48 +00:00
|
|
|
packet_duration_flac
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"AnxData", 7, 0,
|
|
|
|
"application/octet-stream",
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2009-11-25 05:22:03 +00:00
|
|
|
NULL,
|
2009-08-29 17:51:48 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"CELT ", 8, 0,
|
|
|
|
"audio/x-celt",
|
|
|
|
setup_celt_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
NULL,
|
|
|
|
is_header_count,
|
|
|
|
packet_duration_constant
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"\200kate\0\0\0", 8, 0,
|
|
|
|
"text/x-kate",
|
|
|
|
setup_kate_mapper,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2009-11-25 05:22:03 +00:00
|
|
|
NULL,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_header_count,
|
|
|
|
NULL
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"BBCD\0", 5, 13,
|
|
|
|
"video/x-dirac",
|
|
|
|
setup_dirac_mapper,
|
|
|
|
granulepos_to_granule_dirac,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_dirac,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_keyframe_dirac,
|
|
|
|
is_header_count,
|
|
|
|
packet_duration_constant
|
|
|
|
},
|
|
|
|
{
|
2009-12-04 04:05:29 +00:00
|
|
|
"\001audio\0\0\0", 9, 53,
|
2009-08-29 17:51:48 +00:00
|
|
|
"application/x-ogm-audio",
|
|
|
|
setup_ogmaudio_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_keyframe_true,
|
2009-12-04 04:05:29 +00:00
|
|
|
is_header_ogm,
|
|
|
|
packet_duration_ogm
|
2009-08-29 17:51:48 +00:00
|
|
|
},
|
|
|
|
{
|
2009-12-04 04:05:29 +00:00
|
|
|
"\001video\0\0\0", 9, 53,
|
2009-08-29 17:51:48 +00:00
|
|
|
"application/x-ogm-video",
|
|
|
|
setup_ogmvideo_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
NULL,
|
2009-12-04 04:05:29 +00:00
|
|
|
is_header_ogm,
|
2009-12-11 11:02:15 +00:00
|
|
|
packet_duration_constant
|
2009-08-29 17:51:48 +00:00
|
|
|
},
|
|
|
|
{
|
2009-12-04 04:05:29 +00:00
|
|
|
"\001text\0\0\0", 9, 9,
|
2009-08-29 17:51:48 +00:00
|
|
|
"application/x-ogm-text",
|
|
|
|
setup_ogmtext_mapper,
|
|
|
|
granulepos_to_granule_default,
|
2009-11-25 05:22:03 +00:00
|
|
|
granule_to_granulepos_default,
|
2009-08-29 17:51:48 +00:00
|
|
|
is_keyframe_true,
|
2009-12-04 04:05:29 +00:00
|
|
|
is_header_ogm,
|
|
|
|
packet_duration_ogm
|
2009-08-29 17:51:48 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
/* *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;
|
2009-12-03 01:16:17 +00:00
|
|
|
} else {
|
2009-12-04 04:05:29 +00:00
|
|
|
GST_WARNING ("mapper '%s' did not accept setup header",
|
|
|
|
mappers[i].media_type);
|
2009-08-29 17:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|