#include "config.h"
#ifdef HAVE_OGGVORBIS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include "../mp_msg.h"
#include "../help_mp.h"
#include "stream.h"
#include "demuxer.h"
#include "stheader.h"
#ifdef TREMOR
#include <tremor/ogg.h>
#include <tremor/ivorbiscodec.h>
#else
#include <ogg/ogg.h>
#include <vorbis/codec.h>
#endif
#ifdef HAVE_OGGTHEORA
#include <theora/theora.h>
#endif
#define BLOCK_SIZE 4096
/// Vorbis decoder context : we need the vorbis_info for vorbis timestamping
/// Shall we put this struct def in a common header ?
typedef struct ov_struct_st {
vorbis_info vi; /* struct that stores all the static vorbis bitstream
settings */
vorbis_comment vc; /* struct that stores all the bitstream user comments */
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
vorbis_block vb; /* local working space for packet->PCM decode */
float rg_scale; /* replaygain scale */
#ifdef TREMOR
int rg_scale_int;
#endif
} ov_struct_t;
/* Theora decoder context : we won't be able to interpret granule positions
* without using theora_granule_time with the theora_state of the stream.
* This is duplicated in `vd_theora.c'; put this in a common header?
*/
#ifdef HAVE_OGGTHEORA
typedef struct theora_struct_st {
theora_state st;
theora_comment cc;
theora_info inf;
} theora_struct_t;
#endif
//// OggDS headers
// Header for the new header format
typedef struct stream_header_video
{
ogg_int32_t width;
ogg_int32_t height;
} stream_header_video;
typedef struct stream_header_audio
{
ogg_int16_t channels;
ogg_int16_t blockalign;
ogg_int32_t avgbytespersec;
} stream_header_audio;
typedef struct __attribute__((__packed__)) stream_header
{
char streamtype[8];
char subtype[4];
ogg_int32_t size; // size of the structure
ogg_int64_t time_unit; // in reference time
ogg_int64_t samples_per_unit;
ogg_int32_t default_len; // in media time
ogg_int32_t buffersize;
ogg_int16_t bits_per_sample;
ogg_int16_t padding;
union
{
// Video specific
stream_header_video video;
// Audio specific
stream_header_audio audio;
} sh;
} stream_header;
/// Our private datas
typedef struct ogg_syncpoint {
int64_t granulepos;
off_t page_pos;
} ogg_syncpoint_t;
/// A logical stream
typedef struct ogg_stream {
/// Timestamping stuff
float samplerate; /// granulpos 2 time
int64_t lastpos;
int32_t lastsize;
// Logical stream state
ogg_stream_state stream;
int hdr_packets;
int vorbis;
int theora;
int flac;
int text;
int id;
} ogg_stream_t;
typedef struct ogg_demuxer {
/// Physical stream state
ogg_sync_state sync;
/// Current page
ogg_page page;
/// Logical streams
ogg_stream_t *subs;
int num_sub;
ogg_syncpoint_t* syncpoints;
int num_syncpoint;
off_t pos, last_size;
int64_t final_granulepos;
/* Used for subtitle switching. */
int n_text;
int *text_ids;
char **text_langs;
} ogg_demuxer_t;
#define NUM_VORBIS_HDR_PACKETS 3
/// Some defines from OggDS
#define PACKET_TYPE_HEADER 0x01
#define PACKET_TYPE_BITS 0x07
#define PACKET_LEN_BITS01 0xc0
#define PACKET_LEN_BITS2 0x02
#define PACKET_IS_SYNCPOINT 0x08
extern int index_mode;
extern char *dvdsub_lang, *audio_lang;
extern int dvdsub_id;
extern int demux_aid_vid_mismatch;
//-------- subtitle support - should be moved to decoder layer, and queue
// - subtitles up in demuxer buffer...
#include "../subreader.h"
#include "../libvo/sub.h"
#define OGG_SUB_MAX_LINE 128
static subtitle ogg_sub;
extern subtitle* vo_sub;
static float clear_sub;
//FILE* subout;
static
uint16_t get_uint16 (const void *buf)
{
uint16_t ret;
unsigned char *tmp;
tmp = (unsigned char *) buf;
ret = tmp[1] & 0xff;
ret = (ret << 8) + (tmp[0] & 0xff);
return (ret);
}
static
uint32_t get_uint32 (const void *buf)
{
uint32_t ret;
unsigned char *tmp;
tmp = (unsigned char *) buf;
ret = tmp[3] & 0xff;
ret = (ret << 8) + (tmp[2] & 0xff);
ret = (ret << 8) + (tmp[1] & 0xff);
ret = (ret << 8) + (tmp[0] & 0xff);
return (ret);
}
static
uint64_t get_uint64 (const void *buf)
{
uint64_t ret;
unsigned char *tmp;
tmp = (unsigned char *) buf;
ret = tmp[7] & 0xff;
ret = (ret << 8) + (tmp[6] & 0xff);
ret = (ret << 8) + (tmp[5] & 0xff);
ret = (ret << 8) + (tmp[4] & 0xff);
ret = (ret << 8) + (tmp[3] & 0xff);
ret = (ret << 8) + (tmp[2] & 0xff);
ret = (ret << 8) + (tmp[1] & 0xff);
ret = (ret << 8) + (tmp[0] & 0xff);
return (ret);
}
void demux_ogg_init_sub () {
int lcv;
if(!ogg_sub.text[0]) // not yet allocated
for (lcv = 0; lcv < SUB_MAX_TEXT; lcv++) {
ogg_sub.text[lcv] = (char*)malloc(OGG_SUB_MAX_LINE);
}
}
void demux_ogg_add_sub (ogg_stream_t* os,ogg_packet* pack) {
int lcv;
int line_pos = 0;
int ignoring = 0;
char *packet = pack->packet;
mp_msg(MSGT_DEMUX,MSGL_DBG2,"\ndemux_ogg_add_sub %02X %02X %02X '%s'\n",
(unsigned char)packet[0],
(unsigned char)packet[1],
(unsigned char)packet[2],
&packet[3]);
ogg_sub.lines = 0;
if (((unsigned char)packet[0]) == 0x88) { // some subtitle text
// Find data start
int32_t duration = 0;
int16_t hdrlen = (*packet & PACKET_LEN_BITS01)>>6, i;
hdrlen |= (*packet & PACKET_LEN_BITS2) <<1;
lcv = 1 + hdrlen;
for (i = hdrlen; i > 0; i--) {
duration <<= 8;
duration |= (unsigned char)packet[i];
}
if ((hdrlen > 0) && (duration > 0)) {
float pts;
if(pack->granulepos == -1)
pack->granulepos = os->lastpos + os->lastsize;
pts = (float)pack->granulepos/(float)os->samplerate;
clear_sub = 1.0 + pts + (float)duration/1000.0;
}
while (1) {
int c = packet[lcv++];
if(c=='\n' || c==0 || line_pos >= OGG_SUB_MAX_LINE-1){
ogg_sub.text[ogg_sub.lines][line_pos] = 0; // close sub
if(line_pos) ogg_sub.lines++;
if(!c || ogg_sub.lines>=SUB_MAX_TEXT) break; // EOL or TooMany
line_pos = 0;
}
switch (c) {
case '\r':
case '\n': // just ignore linefeeds for now
// their placement seems rather haphazard
break;
case '<': // some html markup, ignore for now
ignoring = 1;
break;
case '>':
ignoring = 0;
break;
default:
if(!ignoring)
ogg_sub.text[ogg_sub.lines][line_pos++] = c;
break;
}
}
}
mp_msg(MSGT_DEMUX,MSGL_DBG2,"Ogg sub lines: %d first: '%s'\n",
ogg_sub.lines, ogg_sub.text[0]);
#ifdef USE_ICONV
subcp_recode1(&ogg_sub);
#endif
vo_sub = &ogg_sub;
vo_osd_changed(OSDTYPE_SUBTITLE);
}
// get the logical stream of the current page
// fill os if non NULL and return the stream id
static int demux_ogg_get_page_stream(ogg_demuxer_t* ogg_d,ogg_stream_state** os) {
int id,s_no;
ogg_page* page = &ogg_d->page;
s_no = ogg_page_serialno(page
|