summaryrefslogtreecommitdiff
path: root/src/demuxers/demux_real.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/demuxers/demux_real.c')
-rw-r--r--src/demuxers/demux_real.c500
1 files changed, 387 insertions, 113 deletions
diff --git a/src/demuxers/demux_real.c b/src/demuxers/demux_real.c
index 141a07467..c8df16963 100644
--- a/src/demuxers/demux_real.c
+++ b/src/demuxers/demux_real.c
@@ -21,7 +21,7 @@
* For more information regarding the Real file format, visit:
* http://www.pcisys.net/~melanson/codecs/
*
- * $Id: demux_real.c,v 1.8 2002/11/09 23:22:32 guenter Exp $
+ * $Id: demux_real.c,v 1.9 2002/11/18 03:03:09 guenter Exp $
*/
#ifdef HAVE_CONFIG_H
@@ -40,6 +40,8 @@
#include "demux.h"
#include "bswap.h"
+#define LOG
+
#define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
( (long)(unsigned char)(ch3) | \
( (long)(unsigned char)(ch2) << 8 ) | \
@@ -60,15 +62,23 @@
#define PN_KEYFRAME_FLAG 0x0002
+#define MAX_STREAMS 10
+
typedef struct {
- int stream;
- int64_t offset;
- unsigned int size;
- int64_t pts;
- int keyframe;
+ int stream;
+ int64_t offset;
+ unsigned int size;
+ int64_t pts;
+ int keyframe;
} real_packet;
typedef struct {
+ int num;
+ int32_t buf_type ;
+ fifo_buffer_t *fifo;
+} stream_info_t;
+
+typedef struct {
demux_plugin_t demux_plugin;
@@ -85,6 +95,8 @@ typedef struct {
off_t data_size;
int status;
unsigned int duration;
+ int packet_count;
+ int bitrate;
unsigned int video_type;
unsigned int audio_type;
@@ -95,9 +107,8 @@ typedef struct {
unsigned int audio_sample_rate;
unsigned int audio_bits;
- unsigned int packet_count;
- unsigned int current_packet;
- real_packet *packets;
+ int num_streams;
+ stream_info_t streams[MAX_STREAMS];
unsigned int current_data_chunk_packet_count;
unsigned int next_data_chunk_offset;
@@ -115,30 +126,178 @@ typedef struct {
config_values_t *config;
} demux_real_class_t;
-/* returns 1 if the real file was opened successfully, 0 otherwise */
-static int open_real_file(demux_real_t *this) {
+typedef struct {
+
+ u_int16_t object_version;
+
+ u_int16_t stream_number;
+ u_int32_t max_bit_rate;
+ u_int32_t avg_bit_rate;
+ u_int32_t max_packet_size;
+ u_int32_t avg_packet_size;
+ u_int32_t start_time;
+ u_int32_t preroll;
+ u_int32_t duration;
+ char stream_name_size;
+ char *stream_name;
+ char mime_type_size;
+ char *mime_type;
+ u_int32_t type_specific_len;
+ char *type_specific_data;
+
+} pnm_mdpr_t;
+
+void hexdump (char *buf, int length) {
+
+ int i;
+
+ printf ("pnm: ascii contents>");
+ for (i = 0; i < length; i++) {
+ unsigned char c = buf[i];
+
+ if ((c >= 32) && (c <= 128))
+ printf ("%c", c);
+ else
+ printf (".");
+ }
+ printf ("\n");
+
+ printf ("pnm: complete hexdump of package follows:\npnm: ");
+ for (i = 0; i < length; i++) {
+ unsigned char c = buf[i];
+
+ printf ("%02x", c);
+
+ if ((i % 16) == 15)
+ printf ("\npnm: ");
+
+ if ((i % 2) == 1)
+ printf (" ");
+
+ }
+ printf ("\n");
+}
+
+static pnm_mdpr_t *pnm_parse_mdpr(const char *data) {
+
+ pnm_mdpr_t *mdpr=malloc(sizeof(pnm_mdpr_t));
+
+ mdpr->object_version=BE_16(&data[0]);
+
+ if (mdpr->object_version != 0) {
+ printf("warning: unknown object version in MDPR: 0x%04x\n",
+ mdpr->object_version);
+ }
+
+ mdpr->stream_number=BE_16(&data[2]);
+ mdpr->max_bit_rate=BE_32(&data[4]);
+ mdpr->avg_bit_rate=BE_32(&data[8]);
+ mdpr->max_packet_size=BE_32(&data[12]);
+ mdpr->avg_packet_size=BE_32(&data[16]);
+ mdpr->start_time=BE_32(&data[20]);
+ mdpr->preroll=BE_32(&data[24]);
+ mdpr->duration=BE_32(&data[28]);
+
+ mdpr->stream_name_size=data[32];
+ mdpr->stream_name=malloc(sizeof(char)*(mdpr->stream_name_size+1));
+ memcpy(mdpr->stream_name, &data[33], mdpr->stream_name_size);
+ mdpr->stream_name[mdpr->stream_name_size]=0;
+
+ mdpr->mime_type_size=data[33+mdpr->stream_name_size];
+ mdpr->mime_type=malloc(sizeof(char)*(mdpr->mime_type_size+1));
+ memcpy(mdpr->mime_type, &data[34+mdpr->stream_name_size], mdpr->mime_type_size);
+ mdpr->mime_type[mdpr->mime_type_size]=0;
+
+ mdpr->type_specific_len=BE_32(&data[34+mdpr->stream_name_size+mdpr->mime_type_size]);
+ mdpr->type_specific_data=malloc(sizeof(char)*(mdpr->type_specific_len));
+ memcpy(mdpr->type_specific_data,
+ &data[38+mdpr->stream_name_size+mdpr->mime_type_size], mdpr->type_specific_len);
+
+ printf("pnm: MDPR: stream number: %i\n", mdpr->stream_number);
+ printf("pnm: MDPR: maximal bit rate: %i\n", mdpr->max_bit_rate);
+ printf("pnm: MDPR: average bit rate: %i\n", mdpr->avg_bit_rate);
+ printf("pnm: MDPR: largest packet size: %i bytes\n", mdpr->max_packet_size);
+ printf("pnm: MDPR: average packet size: %i bytes\n", mdpr->avg_packet_size);
+ printf("pnm: MDPR: start time: %i\n", mdpr->start_time);
+ printf("pnm: MDPR: pre-buffer: %i ms\n", mdpr->preroll);
+ printf("pnm: MDPR: duration of stream: %i ms\n", mdpr->duration);
+ printf("pnm: MDPR: stream name: %s\n", mdpr->stream_name);
+ printf("pnm: MDPR: mime type: %s\n", mdpr->mime_type);
+ printf("pnm: MDPR: type specific data:\n");
+ hexdump(mdpr->type_specific_data, mdpr->type_specific_len);
+ printf("\n");
+
+ return mdpr;
+}
+
+typedef struct dp_hdr_s {
+ uint32_t chunks; /* number of chunks */
+ uint32_t timestamp; /* timestamp from packet header */
+ uint32_t len; /* length of actual data */
+ uint32_t chunktab; /* offset to chunk offset array */
+} dp_hdr_t;
+
+static void send_real_buf (demux_real_t *this, uint32_t timestamp, int len,
+ fifo_buffer_t *fifo,
+ uint32_t buf_type, uint32_t decoder_flags) {
+
+ dp_hdr_t *hdr;
+ buf_element_t *buf;
+
+ if (!fifo) {
+ this->input->seek (this->input, len, SEEK_CUR);
+ return;
+ }
+
+ buf = fifo->buffer_pool_alloc (fifo);
+
+ buf->content = buf->mem;
+
+ hdr = buf->content;
+ hdr->chunks = 1;
+ hdr->timestamp = timestamp;
+ hdr->len = len;
+ hdr->chunktab = 0;
+
+ this->input->read (this->input, buf->content+16, len);
+
+ buf->size = len+16;
+
+ buf->input_pos = 0 ; /* FIXME */
+ buf->input_time = 0 ; /* FIXME */
+ buf->type = buf_type;
+ buf->decoder_flags = decoder_flags;
+
+ fifo->put (fifo, buf);
+
+}
+
+
+static void real_parse_headers (demux_real_t *this) {
- char preamble[PREAMBLE_SIZE];
- unsigned int chunk_type = 0;
- unsigned int chunk_size;
+ char preamble[PREAMBLE_SIZE];
+ unsigned int chunk_type = 0;
+ unsigned int chunk_size;
unsigned char *chunk_buffer;
- int field_size;
- int stream_ptr;
- unsigned char data_chunk_header[DATA_CHUNK_HEADER_SIZE];
- unsigned char signature[REAL_SIGNATURE_SIZE];
+ int field_size;
+ int stream_ptr;
+ unsigned char data_chunk_header[DATA_CHUNK_HEADER_SIZE];
+ unsigned char signature[REAL_SIGNATURE_SIZE];
this->data_start = 0;
this->data_size = 0;
- this->packets = NULL;
- this->current_packet = 0;
this->input->seek(this->input, 0, SEEK_SET);
if (this->input->read(this->input, signature, REAL_SIGNATURE_SIZE) !=
- REAL_SIGNATURE_SIZE)
- return 0;
+ REAL_SIGNATURE_SIZE) {
+ this->status = DEMUX_FINISHED;
+ return;
+ }
- if (BE_32(signature) != RMF_TAG)
- return 0;
+ if (BE_32(signature) != RMF_TAG) {
+ this->status = DEMUX_FINISHED;
+ return;
+ }
/* skip to the start of the first chunk (the first chunk is 0x12 bytes
* long) and start traversing */
@@ -149,8 +308,10 @@ static int open_real_file(demux_real_t *this) {
while (chunk_type != DATA_TAG) {
if (this->input->read(this->input, preamble, PREAMBLE_SIZE) !=
- PREAMBLE_SIZE)
- return 0;
+ PREAMBLE_SIZE) {
+ this->status = DEMUX_FINISHED;
+ return;
+ }
chunk_type = BE_32(&preamble[0]);
chunk_size = BE_32(&preamble[4]);
@@ -163,9 +324,9 @@ static int open_real_file(demux_real_t *this) {
chunk_size -= PREAMBLE_SIZE;
chunk_buffer = xine_xmalloc(chunk_size);
if (this->input->read(this->input, chunk_buffer, chunk_size) !=
- chunk_size) {
- free(chunk_buffer);
- return 0;
+ chunk_size) {
+ this->status = DEMUX_FINISHED;
+ return;
}
if (chunk_type == PROP_TAG) {
@@ -176,6 +337,91 @@ static int open_real_file(demux_real_t *this) {
} else if (chunk_type == MDPR_TAG) {
+ pnm_mdpr_t *mdpr;
+
+ mdpr = pnm_parse_mdpr (chunk_buffer);
+
+ this->bitrate = mdpr->avg_bit_rate;
+ this->stream->stream_info[XINE_STREAM_INFO_BITRATE] = mdpr->avg_bit_rate;
+
+ /* detect streamtype */
+
+ this->streams[this->num_streams].num = mdpr->stream_number;
+
+ if (!strncmp (mdpr->type_specific_data+4, "VIDORV20", 8)) {
+
+ buf_element_t *buf;
+
+ this->streams[this->num_streams].buf_type = BUF_VIDEO_RV20;
+ this->streams[this->num_streams].fifo = this->video_fifo;
+
+ printf ("demux_real: RV20 video detected\n");
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+
+ buf->content = buf->mem;
+
+ memcpy (buf->content, mdpr->type_specific_data,
+ mdpr->type_specific_len);
+
+ buf->size = mdpr->type_specific_len;
+
+ buf->input_pos = 0 ;
+ buf->input_time = 0 ;
+ buf->type = BUF_VIDEO_RV20;
+ buf->decoder_flags = BUF_FLAG_HEADER;
+
+ this->video_fifo->put (this->video_fifo, buf);
+
+ } else if (!strncmp (mdpr->type_specific_data+4, "VIDORV30", 8)) {
+
+ buf_element_t *buf;
+
+ this->streams[this->num_streams].buf_type = BUF_VIDEO_RV30;
+ this->streams[this->num_streams].fifo = this->video_fifo;
+
+ printf ("demux_real: RV30 video detected\n");
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+
+ buf->content = buf->mem;
+
+ memcpy (buf->content, mdpr->type_specific_data,
+ mdpr->type_specific_len);
+
+ buf->size = mdpr->type_specific_len;
+
+ buf->input_pos = 0 ;
+ buf->input_time = 0 ;
+ buf->type = BUF_VIDEO_RV30;
+ buf->decoder_flags = BUF_FLAG_HEADER;
+
+ this->video_fifo->put (this->video_fifo, buf);
+
+ } else if ((mdpr->type_specific_len>61)
+ && (!strncmp (mdpr->type_specific_data+57, "sipr", 4))) {
+
+ this->streams[this->num_streams].buf_type = BUF_AUDIO_SIPRO;
+ this->streams[this->num_streams].fifo = this->audio_fifo;
+
+ printf ("demux_real: sipro audio detected\n");
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+
+ } else {
+
+ printf ("demux_real: codec not recognized\n");
+
+ this->streams[this->num_streams].buf_type = 0;
+ this->streams[this->num_streams].fifo = NULL;
+ }
+
+ this->num_streams++;
+ free (mdpr);
} else if (chunk_type == CONT_TAG) {
@@ -227,8 +473,11 @@ static int open_real_file(demux_real_t *this) {
case DATA_TAG:
if (this->input->read(this->input, data_chunk_header,
- DATA_CHUNK_HEADER_SIZE) != DATA_CHUNK_HEADER_SIZE)
- return 0;
+ DATA_CHUNK_HEADER_SIZE) != DATA_CHUNK_HEADER_SIZE) {
+ this->status = DEMUX_FINISHED;
+ return ;
+ }
+
this->current_data_chunk_packet_count = BE_32(&data_chunk_header[2]);
this->next_data_chunk_offset = BE_32(&data_chunk_header[6]);
break;
@@ -240,54 +489,59 @@ static int open_real_file(demux_real_t *this) {
}
}
-
- /* allocate space for the packet list */
- this->packets = xine_xmalloc(this->packet_count * sizeof(real_packet));
-
- return 1;
}
static int demux_real_send_chunk(demux_plugin_t *this_gen) {
- demux_real_t *this = (demux_real_t *) this_gen;
- buf_element_t *buf = NULL;
- char preamble[PREAMBLE_SIZE];
- unsigned char data_chunk_header[DATA_CHUNK_HEADER_SIZE];
- char header[DATA_PACKET_HEADER_SIZE];
+ demux_real_t *this = (demux_real_t *) this_gen;
+ char preamble[PREAMBLE_SIZE];
+ unsigned char data_chunk_header[DATA_CHUNK_HEADER_SIZE];
+ char header[DATA_PACKET_HEADER_SIZE];
+ int stream_num,i;
+ int stream, size, keyframe;
+ uint32_t timestamp;
+ int64_t pts;
+ off_t offset;
/* load a header from wherever the stream happens to be pointing */
if (this->input->read(this->input, header, DATA_PACKET_HEADER_SIZE) !=
- DATA_PACKET_HEADER_SIZE) {
+ DATA_PACKET_HEADER_SIZE) {
this->status = DEMUX_FINISHED;
return this->status;
}
- /* log the packet information */
- this->packets[this->current_packet].stream = BE_16(&header[4]);
- this->packets[this->current_packet].offset =
- this->input->get_current_pos(this->input);
- this->packets[this->current_packet].size =
- BE_16(&header[2]) - DATA_PACKET_HEADER_SIZE;
- this->packets[this->current_packet].pts =
- BE_32(&header[6]);
- this->packets[this->current_packet].pts *= 90;
- this->packets[this->current_packet].keyframe =
- (header[11] & PN_KEYFRAME_FLAG);
+ /* read the packet information */
+ stream = BE_16(&header[4]);
+ offset = this->input->get_current_pos(this->input);
+ size = BE_16(&header[2]) - DATA_PACKET_HEADER_SIZE;
+ timestamp= BE_32(&header[6]);
+ pts = (int64_t) timestamp * 90;
+ keyframe = header[11] & PN_KEYFRAME_FLAG;
+
+#ifdef LOG
+ printf ("demux_real: packet of stream %d, 0x%X bytes @ %llX, pts = %lld%s\n",
+ stream, size, offset, pts, keyframe ? ", keyframe" : "");
+#endif
+
+ stream_num = -1;
-printf ("packet %d: stream %d, 0x%X bytes @ %llX, pts = %lld%s\n",
-this->current_packet,
-this->packets[this->current_packet].stream,
-this->packets[this->current_packet].size,
-this->packets[this->current_packet].offset,
-this->packets[this->current_packet].pts,
-(this->packets[this->current_packet].keyframe) ? ", keyframe" : "");
+ for (i=0; i<this->num_streams; i++) {
+
+ if (this->streams[i].num == stream)
+ stream_num = i;
+ }
+ if (stream_num >= 0) {
+ printf ("demux_real: buf type is %08x\n", this->streams[stream_num].buf_type);
-this->input->seek(this->input, this->packets[this->current_packet].size,
-SEEK_CUR);
+ send_real_buf (this, timestamp, size,
+ this->streams[stream_num].fifo,
+ this->streams[stream_num].buf_type,
+ 0 /* FIXME */);
+ } else
+ this->input->seek(this->input, size, SEEK_CUR);
- this->current_packet++;
this->current_data_chunk_packet_count--;
/* check if it's time to reload */
@@ -310,7 +564,9 @@ SEEK_CUR);
this->status = DEMUX_FINISHED;
return this->status;
}
-printf ("**** found next DATA tag\n");
+#ifdef LOG
+ printf ("demux_real: **** found next DATA tag\n");
+#endif
this->current_data_chunk_packet_count = BE_32(&data_chunk_header[2]);
this->next_data_chunk_offset = BE_32(&data_chunk_header[6]);
}
@@ -325,19 +581,31 @@ printf ("**** found next DATA tag\n");
static void demux_real_send_headers(demux_plugin_t *this_gen) {
demux_real_t *this = (demux_real_t *) this_gen;
- buf_element_t *buf;
this->video_fifo = this->stream->video_fifo;
this->audio_fifo = this->stream->audio_fifo;
this->status = DEMUX_OK;
+ this->num_streams = 0;
+
/* send start buffers */
-/* xine_demux_control_start(this->stream);
-*/
+ xine_demux_control_start(this->stream);
/* send init info to decoders */
+ this->input->seek (this->input, 0, SEEK_SET);
+
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 0;
+ real_parse_headers (this);
+
+
+ /* print vital stats */
+ xine_log (this->stream->xine, XINE_LOG_MSG,
+ _("demux_real: Real media file, running time: %d min, %d sec\n"),
+ this->duration / 1000 / 60,
+ this->duration / 1000 % 60);
xine_demux_control_headers_done (this->stream);
}
@@ -364,7 +632,6 @@ static void demux_real_dispose (demux_plugin_t *this_gen) {
demux_real_t *this = (demux_real_t *) this_gen;
- free(this->packets);
free(this);
}
@@ -388,36 +655,38 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
input_plugin_t *input = (input_plugin_t *) input_gen;
demux_real_t *this;
- if (! (input->get_capabilities(input) & INPUT_CAP_SEEKABLE)) {
- printf(_("demux_real.c: input not seekable, can not handle!\n"));
- return NULL;
- }
-
- this = xine_xmalloc (sizeof (demux_real_t));
- this->stream = stream;
- this->input = input;
-
- this->demux_plugin.send_headers = demux_real_send_headers;
- this->demux_plugin.send_chunk = demux_real_send_chunk;
- this->demux_plugin.seek = demux_real_seek;
- this->demux_plugin.dispose = demux_real_dispose;
- this->demux_plugin.get_status = demux_real_get_status;
- this->demux_plugin.get_stream_length = demux_real_get_stream_length;
- this->demux_plugin.get_video_frame = NULL;
- this->demux_plugin.got_video_frame_cb= NULL;
- this->demux_plugin.demux_class = class_gen;
-
- this->status = DEMUX_FINISHED;
+ printf ("demux_real: open_plugin\n");
switch (stream->content_detection_method) {
case METHOD_BY_CONTENT:
- case METHOD_EXPLICIT:
-
- if (!open_real_file(this)) {
- free (this);
- return NULL;
+ {
+ uint8_t buf[4096];
+
+ if ((input->get_capabilities(input) & INPUT_CAP_SEEKABLE) != 0) {
+
+ input->seek(input, 0, SEEK_SET);
+
+ if (input->read(input, buf, 4)) {
+
+ if ((buf[0] != 0x2e)
+ || (buf[1] != 'R')
+ || (buf[2] != 'M')
+ || (buf[3] != 'F'))
+ return NULL;
+ }
+ } else if (input->get_optional_data (input, buf, INPUT_OPTIONAL_DATA_PREVIEW)) {
+ if ((buf[0] != 0x2e)
+ || (buf[1] != 'R')
+ || (buf[2] != 'M')
+ || (buf[3] != 'F'))
+ return NULL;
+ } else
+ return NULL;
}
+
+
+ printf ("demux_real: by content accepted.\n");
break;
@@ -428,37 +697,41 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
ending = strrchr(mrl, '.');
- if (!ending) {
- free (this);
- return NULL;
- }
-
- if (strncasecmp (ending, ".rm", 3)) {
- free (this);
+ if (!ending)
return NULL;
- }
- if (!open_real_file(this)) {
- free (this);
+ if (strncasecmp (ending, ".rm", 3)
+ && strncasecmp (ending, ".ra", 3)
+ && strncasecmp (ending, ".ram", 4))
return NULL;
- }
}
break;
+ case METHOD_EXPLICIT:
+ break;
+
default:
- free (this);
return NULL;
}
- strncpy (this->last_mrl, input->get_mrl (input), 1024);
- /* print vital stats */
- xine_log (this->stream->xine, XINE_LOG_MSG,
- _("demux_real: Real media file, running time: %d min, %d sec\n"),
- this->duration / 1000 / 60,
- this->duration / 1000 % 60);
+ this = xine_xmalloc (sizeof (demux_real_t));
+ this->stream = stream;
+ this->input = input;
+
+ this->demux_plugin.send_headers = demux_real_send_headers;
+ this->demux_plugin.send_chunk = demux_real_send_chunk;
+ this->demux_plugin.seek = demux_real_seek;
+ this->demux_plugin.dispose = demux_real_dispose;
+ this->demux_plugin.get_status = demux_real_get_status;
+ this->demux_plugin.get_stream_length = demux_real_get_stream_length;
+ this->demux_plugin.get_video_frame = NULL;
+ this->demux_plugin.got_video_frame_cb= NULL;
+ this->demux_plugin.demux_class = class_gen;
+
+ strncpy (this->last_mrl, input->get_mrl (input), 1024);
return &this->demux_plugin;
}
@@ -472,11 +745,12 @@ static char *get_identifier (demux_class_t *this_gen) {
}
static char *get_extensions (demux_class_t *this_gen) {
- return "rm";
+ return "rm ra ram";
}
static char *get_mimetypes (demux_class_t *this_gen) {
- return NULL;
+ return "audio/x-pn-realaudio: ra, rm, ram: Real Media File;"
+ "audio/x-realaudio: ra: Real Media File;";
}
static void class_dispose (demux_class_t *this_gen) {