summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS3
-rw-r--r--ChangeLog1
-rw-r--r--src/demuxers/demux_str.c497
-rw-r--r--src/libxineadec/adpcm.c244
4 files changed, 638 insertions, 107 deletions
diff --git a/AUTHORS b/AUTHORS
index f9b2c9aed..ca0de705b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -354,4 +354,7 @@ Marco Zühlke <MZuehlke@myrealbox.com>
Julio Sánchez <j.sanchezf@terra.es>
patch for drawing OSD bitmaps
+Stuart Caie <kyzer@4u.net>
+ Playstation STR file demuxer, CD-ROM/XA ADPCM decoder
+
(let us know if we've forgotten anyone)
diff --git a/ChangeLog b/ChangeLog
index ec80b2287..a58cb16ef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,6 +8,7 @@ xine-lib (1-beta5)
* OSS driver fixes (for cards using GETOPTR sync method)
* fixed gnome-vfs plugin to be used for remote locations (other than http)
* at least for DVD input, the language reporting is now channel-aware
+ * CD-ROM/XA ADPCM decoder
xine-lib (1-beta4)
* http input fixes
diff --git a/src/demuxers/demux_str.c b/src/demuxers/demux_str.c
index 8a9c2ac15..76a96d3c7 100644
--- a/src/demuxers/demux_str.c
+++ b/src/demuxers/demux_str.c
@@ -18,10 +18,92 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
* STR File Demuxer by Mike Melanson (melanson@pcisys.net)
+ * and Stuart Caie (kyzer@4u.net)
* This demuxer handles either raw STR files (which are just a concatenation
* of raw compact disc sectors) or STR files with RIFF headers.
*
- * $Id: demux_str.c,v 1.4 2003/01/26 15:56:21 tmmm Exp $
+ * $Id: demux_str.c,v 1.5 2003/02/14 04:32:28 tmmm Exp $
+ */
+
+/* CD-XA format:
+ *
+ * - the format is a series of 2352 byte CD sectors
+ * - 0x000: 12 bytes: sync header (00 FF FF FF FF FF FF FF FF FF FF 00)
+ * - 0x00C: 4 bytes: timecode (mm ss ff 02; BCD, not decimal!)
+ * - 0x010: 4 bytes: sector parameters
+ * - 0x10 file_num
+ * - 0x11 channel_num
+ * - 0x12 subcode
+ * - 0x13 coding_info
+ * - 0x014: 4 bytes: copy of parameters (should be identical)
+ * - 0x018: 2324 bytes: sector data
+ * - 0x92C: 4 bytes: EDC error correction code
+ * - 0x930: SIZEOF
+ *
+ * - file_num is purely to distinguish where a 'file' ends and a new
+ * 'file' begins among the sectors. It's usually 1.
+ * - channel_num is a sub-channel in this 'file'. Video, audio and data
+ * sectors can be mixed into the same channel or can be on seperate
+ * channels. Usually used for multiple audio tracks (e.g. 5 different
+ * songs in the same 'file', on channels 0, 1, 2, 3 and 4)
+ * - subcode is a set of bits
+ * - bit 7: eof_marker -- 0, or 1 if this sector is the end of the 'file'
+ * - bit 6: real_time -- unimportant (always set in PSX STR streams)
+ * - bit 5: form -- unimportant
+ * - bit 4: trigger -- for use by reader application (unimportant)
+ * - bit 3: DATA -- set to 1 to indicate DATA sector, otherwise 0
+ * - bit 2: AUDIO -- set to 1 to indicate AUDIO sector, otherwise 0
+ * - bit 1: VIDEO -- set to 1 to indicate VIDEO sector, otherwise 0
+ * - bit 0: end_audio -- end of audio frame (never set in PSX STR streams)
+ * - bits 1, 2 and 3 are mutually exclusive
+ * - coding_info is a set of bits, interpretation is dependant on the
+ * DATA/AUDIO/VIDEO bits setting of subcode.
+ * - For AUDIO:
+ * - bit 7: reserved -- should always be 0
+ * - bit 6: emphasis -- boost audio volume (ignored by us)
+ * - bit 5: bitssamp -- must always be 0
+ * - bit 4: bitssamp -- 0 for mode B/C (4 bits/sample, 8 sound sectors)
+ * 1 for mode A (8 bits/sample, 4 sound sectors)
+ * - bit 3: samprate -- must always be 0
+ * - bit 2: samprate -- 0 for 37.8kHz playback, 1 for 18.9kHz playback
+ * - bit 1: stereo -- must always be 0
+ * - bit 0: stereo -- 0 for mono sound, 1 for stereo sound
+ * - For DATA or VIDEO:
+ * - always seems to be 0 in PSX STR files
+ *
+ * Format of sector data in AUDIO sectors:
+ * - 18 "sound groups" of 128 byte structures
+ * - 20 unused bytes
+ * - we pass these 18*128 bytes to the XA_ADPCM audio decoder
+ *
+ * Format of sector data in DATA or VIDEO sectors:
+ * - all values are little-endian
+ * - 0x00: 32 bits; unknown -- usually 0x80010160 for a video frame.
+ * according to PSX hardware guide, this value is written
+ * to mdec0 register:
+ * - bit 27: 1 for 16-bit colour, 0 for 24-bit colour depth
+ * - bit 24: if 16-bit colour, 1/0=set/clear transparency bit
+ * - all other bits unknown
+ * - if not set to this value, it's not a video sector.
+ * - 0x04: 16 bits; 'chunk number' of this video frame (0 to numchunks-1)
+ * - 0x06: 16 bits; number of chunks in this frame
+ * - 0x08: 32 bits; frame number (1 to ...)
+ * - 0x0C: 32 bits; seemingly random number. frame duration?
+ * - 0x10: 16 bits; width of frame in pixels
+ * - 0x12: 16 bits; height of frame in pixels
+ * - remainder of data (up to 2304 bytes): compressed MDEC stream
+ * - 32 bits: (0x3800 << 16) | size of data (in bytes) following this header
+ * - any number of macroblocks (which each represent a 16x16 pixel area)
+ * - a macroblock is 6 blocks (Cb, Cr, Y0, Y1, Y2 and Y3)
+ * - a block is a DCT setting then an RLE data stream
+ * - 16 bits: DCT (bits 15-10: quantisation factor (unsigned)
+ * bits 9-0: Direct Current reference (signed))
+ * - then follows 16-bit RLE data until the EOD
+ * - RLE format: bits 15-10: # of 0s preceding this value (unsigned)
+ * bits 9-0: this value (signed)
+ * - e.g. 3 bytes (2,10)(0,20)(3,30) -> 0 0 10 20 0 0 0 30
+ * - 16 bits: EOD (0xFE00)
+ * - 16 bits: 0xFE00 end-of-data footer
*/
#ifdef HAVE_CONFIG_H
@@ -40,11 +122,25 @@
#include "demux.h"
#include "bswap.h"
-/* 68 bytes is adequate for empirically determining if a file conforms */
-#define STR_CHECK_BYTES 0x68
+/* There may be a RIFF/CDXA header at the beginning of the file, which
+ * accounts for 0x2C bytes. We need at most 0x30 bytes of the sector to
+ * verify whether it's a CDXA/MDEC file
+ */
+
+#define STR_CHECK_BYTES (0x2C + 0x30)
#define CD_RAW_SECTOR_SIZE 2352
+#define STR_MAX_CHANNELS 32
+
+#define STR_MAGIC (0x80010160)
+
+#define CDXA_TYPE_MASK 0x0E
+#define CDXA_TYPE_DATA 0x08
+#define CDXA_TYPE_AUDIO 0x04
+#define CDXA_TYPE_VIDEO 0x02
+#define CDXA_SUBMODE_EOF 0x80 /* set if EOF */
+
#define FOURCC_TAG( ch0, ch1, ch2, ch3 ) \
( (long)(unsigned char)(ch3) | \
( (long)(unsigned char)(ch2) << 8 ) | \
@@ -54,8 +150,7 @@
#define RIFF_TAG FOURCC_TAG('R', 'I', 'F', 'F')
#define CDXA_TAG FOURCC_TAG('C', 'D', 'X', 'A')
-/* this is a temporary measure; hopefully there is a more accurate method
- * for finding frame duration */
+/* FIXME */
#define FRAME_DURATION 45000
typedef struct {
@@ -78,13 +173,18 @@ typedef struct {
off_t data_start;
off_t data_size;
+ off_t current_pos;
int status;
- xine_bmiheader bih;
- int audio_samplerate;
+ xine_bmiheader bih[STR_MAX_CHANNELS];
+ unsigned char audio_info[STR_MAX_CHANNELS];
+ unsigned char channel_type[STR_MAX_CHANNELS];
+ int64_t audio_pts[STR_MAX_CHANNELS];
- char last_mrl[1024];
+ int seek_flag;
+ int default_video_channel;
+ char last_mrl[1024];
} demux_str_t;
typedef struct {
@@ -100,148 +200,324 @@ typedef struct {
/* returns 1 if the STR file was opened successfully, 0 otherwise */
static int open_str_file(demux_str_t *this) {
-
unsigned char check_bytes[STR_CHECK_BYTES];
+ int local_offset, sector, channel;
- this->bih.biWidth = this->bih.biHeight = 0;
+ for (channel = 0; channel < STR_MAX_CHANNELS; channel++) {
+ this->channel_type[channel] = 0;
+ }
this->input->seek(this->input, 0, SEEK_SET);
if (this->input->read(this->input, check_bytes, STR_CHECK_BYTES) !=
- STR_CHECK_BYTES)
+ STR_CHECK_BYTES) {
+#ifdef LOG
+ printf("PSX STR: read error\n");
+#endif
return 0;
+ }
/* check for STR with a RIFF header */
if ((BE_32(&check_bytes[0]) == RIFF_TAG) &&
(BE_32(&check_bytes[8]) == CDXA_TAG))
- this->data_start = 0x2C;
+ local_offset = 0x2C;
else
- this->data_start = 0;
-
- /* now that we have the theoretical start of the first sector, check
- * if it really is a raw CD sector; first step: check for 12-byte
- * sync marker */
- if ((BE_32(&check_bytes[this->data_start + 0]) != 0x00FFFFFF) ||
- (BE_32(&check_bytes[this->data_start + 4]) != 0xFFFFFFFF) ||
- (BE_32(&check_bytes[this->data_start + 8]) != 0xFFFFFF00))
- return 0;
+ local_offset = 0;
- /* the 32 bits starting at 0x10 and at 0x14 should be the same */
- if (BE_32(&check_bytes[this->data_start + 0x10]) !=
- BE_32(&check_bytes[this->data_start + 0x14]))
- return 0;
+ this->data_start = (off_t) local_offset;
- /* check if this an audio or video sector (bit 1 = video, bit 2 =
- * audio) */
- if ((check_bytes[this->data_start + 0x12] & 0x06) == 0x2) {
+ /* we need to check up to 32 sectors for up to 32 audio/video channels */
+ for (sector = 0; sector < STR_MAX_CHANNELS; sector++) {
- /* video is suspected */
+#ifdef LOG
+ printf("PSX STR: file=%d channel=%-2d submode=%02x coding_info=%02x\n",
+ check_bytes[local_offset + 0x10],
+ check_bytes[local_offset + 0x11],
+ check_bytes[local_offset + 0x12],
+ check_bytes[local_offset + 0x13]);
+#endif
- this->bih.biWidth = LE_16(&check_bytes[this->data_start + 0x28]);
- this->bih.biHeight = LE_16(&check_bytes[this->data_start + 0x2A]);
+ /* check for 12-byte sync marker */
+ if ((BE_32(&check_bytes[local_offset + 0]) != 0x00FFFFFF) ||
+ (BE_32(&check_bytes[local_offset + 4]) != 0xFFFFFFFF) ||
+ (BE_32(&check_bytes[local_offset + 8]) != 0xFFFFFF00)) {
+#ifdef LOG
+ printf("PSX STR: sector %d sync error\n", sector);
+#endif
+ return 0;
+ }
- /* sanity check for the width and height */
- if ((this->bih.biWidth <= 0) ||
- (this->bih.biWidth > 320) ||
- (this->bih.biHeight <= 0) ||
- (this->bih.biHeight > 240))
+ /* the 32 bits starting at 0x10 and at 0x14 should be the same */
+ if (BE_32(&check_bytes[local_offset + 0x10]) !=
+ BE_32(&check_bytes[local_offset + 0x14])) {
+#ifdef LOG
+ printf("PSX STR: sector %d control bits copy error\n", sector);
+#endif
return 0;
+ }
- } else if ((check_bytes[this->data_start + 0x12] & 0x06) == 0x4) {
+ /* channel should be from 0 to 31 */
+ channel = check_bytes[local_offset + 0x11];
+ if (channel >= STR_MAX_CHANNELS) {
+#ifdef LOG
+ printf("PSX STR: sector %d channel %d error\n", sector, channel);
+#endif
+ return 0;
+ }
- /* audio is suspected */
+ /* switch on the sector type */
+ switch(check_bytes[local_offset + 0x12] & CDXA_TYPE_MASK) {
- } else
- return 0;
+ case CDXA_TYPE_DATA:
+ case CDXA_TYPE_VIDEO:
+ /* first time we have seen video/data in this channel? */
+ if ((!(this->channel_type[channel] & CDXA_TYPE_DATA)) &&
+ (LE_32(&check_bytes[local_offset + 0x18]) == STR_MAGIC)) {
+
+ /* mark this channel as having video data */
+ this->channel_type[channel] |= CDXA_TYPE_VIDEO;
+
+ this->bih[channel].biWidth =
+ LE_16(&check_bytes[local_offset + 0x18 + 0x10]);
+ this->bih[channel].biHeight =
+ LE_16(&check_bytes[local_offset + 0x18 + 0x12]);
+ }
+ break;
+
+ case CDXA_TYPE_AUDIO:
+ /* first time we have seen audio in this channel? */
+ if (!(this->channel_type[channel] & CDXA_TYPE_AUDIO)) {
+
+ /* mark this channel as having audio data */
+ this->channel_type[channel] |= CDXA_TYPE_AUDIO;
+
+ this->audio_info[channel] = check_bytes[local_offset + 0x13];
+ }
+ break;
+ default:
+#ifdef LOG
+ printf("PSX STR: sector %d channel %d unknown type error\n",
+ sector, channel);
+#endif
+ /* several films (e.g. 37xa16.xap in Strider 1) have empty
+ * sectors with 0 as the type, despite having plenty of
+ * video/audio sectors
+ */
+ /*return 0;*/
+ }
+
+ /* seek to the next sector and read in the header */
+ local_offset = 0;
+ this->input->seek(this->input, this->data_start +
+ ((sector+1) * CD_RAW_SECTOR_SIZE), SEEK_SET);
+ if (this->input->read(this->input, check_bytes, 0x30) != 0x30) {
+#ifdef LOG
+ printf("PSX STR: sector %d read error\n", sector);
+#endif
+ return 0;
+ }
+ }
+
+ /* acceptable STR file */
this->data_size = this->input->get_length(this->input) - this->data_start;
+#ifdef LOG
+ for (channel = 0; channel < STR_MAX_CHANNELS; channel++) {
+ char vidinfo[22]; /* "Video (XXXXX x XXXXX)" */
+ char audinfo[33]; /* "Audio (XX.XkHz XXXXXX, mode XXX)" */
+ if (this->channel_type[channel]) {
+ if (this->channel_type[channel] & CDXA_TYPE_VIDEO) {
+ snprintf(vidinfo, 22, "Video (%d x %d)",
+ this->bih[channel].biWidth,
+ this->bih[channel].biHeight);
+ }
+ else {
+ strcpy(vidinfo, "No video");
+ }
+ if (this->channel_type[channel] & CDXA_TYPE_AUDIO) {
+ snprintf(audinfo, 33, "Audio (%skHz %s, mode %s)",
+ this->audio_info[channel] & 0x04 ? "18.9" : "37.8",
+ this->audio_info[channel] & 0x01 ? "stereo" : "mono",
+ this->audio_info[channel] & 0x10 ? "A" : "B/C");
+ }
+ else {
+ strcpy(audinfo, "No audio");
+ }
+ printf("PSX STR: channel %-2d %-22s%s\n", channel, vidinfo, audinfo);
+ }
+ }
+#endif
+
return 1;
}
static int demux_str_send_chunk(demux_plugin_t *this_gen) {
-
demux_str_t *this = (demux_str_t *) this_gen;
- unsigned char sector[CD_RAW_SECTOR_SIZE];
- unsigned int frame_number;
+ unsigned char sector[CD_RAW_SECTOR_SIZE], channel;
+ uint32_t frame_number;
buf_element_t *buf;
+ off_t current_pos;
+ current_pos = this->current_pos;
+ this->current_pos += CD_RAW_SECTOR_SIZE;
if (this->input->read(this->input, sector, CD_RAW_SECTOR_SIZE) !=
- CD_RAW_SECTOR_SIZE) {
+ CD_RAW_SECTOR_SIZE) {
this->status = DEMUX_FINISHED;
return this->status;
}
- if ((sector[0x12] & 0x06) == 0x2) {
+ channel = sector[0x11];
+ if (channel >= STR_MAX_CHANNELS) return 0;
+ switch (sector[0x12] & CDXA_TYPE_MASK) {
+ case CDXA_TYPE_VIDEO:
+ case CDXA_TYPE_DATA:
/* video chunk */
- frame_number = LE_32(&sector[0x20]);
+
+ if (LE_32(&sector[0x18]) != STR_MAGIC ||
+ channel != this->default_video_channel) {
+ return 0;
+ }
+
+ frame_number = LE_32(&sector[0x18 + 0x08]);
buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
- buf->type = BUF_VIDEO_PSX_MDEC;
- buf->extra_info->input_pos =
- this->data_start + frame_number * CD_RAW_SECTOR_SIZE;
- buf->extra_info->input_length = this->data_size;
buf->pts = frame_number * FRAME_DURATION;
- buf->extra_info->input_time = buf->pts / 90;
+
+ if (this->seek_flag) {
+ xine_demux_control_newpts(this->stream, buf->pts, 0);
+ this->seek_flag = 0;
+ }
+
+ /* first chunk of frame? sync forthcoming audio packets */
+ /* FIXME */
+ /*if (LE_16(&sector[0x18+0x04]) == 0) {
+ * int i;
+ * for (i = 0; i < STR_MAX_CHANNELS; i++) this->audio_pts[i] = buf->pts;
+ *}
+ */
+
+ buf->extra_info->input_pos = current_pos;
+ buf->extra_info->input_length = this->data_size;
+ buf->extra_info->input_time = (current_pos*1000)/(CD_RAW_SECTOR_SIZE*75);
/* constant size chunk */
- buf->size = 2048;
- memcpy(buf->content, &sector[0x38], 2048);
+ buf->size = 2304;
+ xine_fast_memcpy(buf->content, &sector[0x18+0x14], 2304);
/* entirely intracoded */
buf->decoder_flags |= BUF_FLAG_KEYFRAME;
/* if the current chunk is 1 less than the chunk count, this is the
* last chunk of the frame */
- if ((LE_16(&sector[0x1C]) + 1) == LE_16(&sector[0x1E]))
+ if ((LE_16(&sector[0x18+0x04]) + 1) == LE_16(&sector[0x18+0x06]))
buf->decoder_flags |= BUF_FLAG_FRAME_END;
+ buf->type = BUF_VIDEO_PSX_MDEC | channel;
this->video_fifo->put(this->video_fifo, buf);
+ break;
- } else if ((sector[0x12] & 0x06) == 0x4) {
+ case CDXA_TYPE_AUDIO:
+ /* audio frame */
+
+ if (this->audio_fifo) {
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+
+ buf->pts = this->audio_pts[channel];
+ this->audio_pts[channel] +=
+ (((sector[0x13] & 0x10) ? 2016 : 4032) *
+ ((sector[0x13] & 0x01) ? 45000 : 90000)) /
+ ((sector[0x13] & 0x04) ? 18900 : 37800);
+
+ if (this->seek_flag) {
+ xine_demux_control_newpts(this->stream, buf->pts, 0);
+ this->seek_flag = 0;
+ }
+
+ buf->extra_info->input_pos = current_pos;
+ buf->extra_info->input_length = this->data_size;
+ buf->extra_info->input_time = (current_pos*1000)/(CD_RAW_SECTOR_SIZE*75);
+
+ buf->size = 2304;
+ xine_fast_memcpy(buf->content, &sector[0x18], 2304);
+
+ buf->type = BUF_AUDIO_XA_ADPCM | channel;
+ buf->decoder_flags |= BUF_FLAG_FRAME_END;
+ this->audio_fifo->put (this->audio_fifo, buf);
+ }
+ break;
}
return this->status;
}
static void demux_str_send_headers(demux_plugin_t *this_gen) {
-
demux_str_t *this = (demux_str_t *) this_gen;
buf_element_t *buf;
+ char audio_info;
+ int channel, video_channel = -1;
this->video_fifo = this->stream->video_fifo;
this->audio_fifo = this->stream->audio_fifo;
this->status = DEMUX_OK;
- /* load stream information */
- this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
- this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
- this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] = this->bih.biWidth;
- this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] = this->bih.biHeight;
-
/* send start buffers */
xine_demux_control_start(this->stream);
- /* send init info to video decoder */
- buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
- buf->decoder_flags = BUF_FLAG_HEADER;
- buf->decoder_info[0] = 0;
- buf->decoder_info[1] = FRAME_DURATION; /* initial video_step */
- memcpy(buf->content, &this->bih, sizeof(this->bih));
- buf->size = sizeof(this->bih);
- buf->type = BUF_VIDEO_PSX_MDEC;
- this->video_fifo->put (this->video_fifo, buf);
-
- /* send init info to the audio decoder */
- if (this->audio_fifo) {
- buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
- buf->type = BUF_AUDIO_XA_ADPCM;
- buf->decoder_flags = BUF_FLAG_HEADER;
- buf->decoder_info[0] = 0;
- buf->decoder_info[1] = 37800;
- buf->decoder_info[2] = 16;
- buf->decoder_info[3] = 1;
- this->audio_fifo->put (this->audio_fifo, buf);
+ /* load stream information */
+ this->stream->stream_info[XINE_STREAM_INFO_SEEKABLE] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 0;
+
+ for (channel = 0; channel < STR_MAX_CHANNELS; channel++) {
+ if (this->channel_type[channel] & CDXA_TYPE_VIDEO) {
+ if (video_channel == -1) {
+ /* FIXME: until I figure out how to comfortably let the user
+ * pick a video channel, just pick a single video channel */
+ video_channel = this->default_video_channel = channel;
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] =
+ this->bih[channel].biWidth;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] =
+ this->bih[channel].biHeight;
+
+ /* send init info to video decoder */
+ buf = this->video_fifo->buffer_pool_alloc (this->video_fifo);
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = FRAME_DURATION; /* initial video_step */
+ buf->size = sizeof(xine_bmiheader);
+ memcpy(buf->content, &this->bih[channel], buf->size);
+ buf->type = BUF_VIDEO_PSX_MDEC;
+ this->video_fifo->put (this->video_fifo, buf);
+ }
+ }
+
+ if (this->channel_type[channel] & CDXA_TYPE_AUDIO) {
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+
+ audio_info = this->audio_info[channel];
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] =
+ (audio_info & 0x01) ? 2 : 1;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] =
+ (audio_info & 0x04) ? 18900 : 37800;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = 16;
+
+ /* send init info to the audio decoder */
+ if (this->audio_fifo) {
+ buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo);
+ buf->type = BUF_AUDIO_XA_ADPCM | channel;
+ buf->decoder_flags = BUF_FLAG_HEADER;
+ buf->decoder_info[0] = 0;
+ buf->decoder_info[1] = (audio_info & 0x04) ? 18900 : 37800;
+ buf->decoder_info[2] = (audio_info & 0x10) ? 1 : 0;
+ buf->decoder_info[3] = (audio_info & 0x01) ? 2 : 1;
+ this->audio_fifo->put (this->audio_fifo, buf);
+
+ this->audio_pts[channel] = 0;
+ }
+ }
}
}
@@ -250,18 +526,21 @@ static int demux_str_seek (demux_plugin_t *this_gen,
demux_str_t *this = (demux_str_t *) this_gen;
- /* if thread is not running, initialize demuxer */
- if( !this->stream->demux_thread_running ) {
-
- /* send new pts */
- xine_demux_control_newpts(this->stream, 0, 0);
-
- this->status = DEMUX_OK;
+ xine_demux_flush_engine (this->stream);
+ /* round to ensure we start on a sector */
+ start_pos /= CD_RAW_SECTOR_SIZE;
+#ifdef LOG
+ printf("PSX STR: seeking to sector %d (%02d:%02d)\n",
+ (int)start_pos, (int)start_pos / (60*75),
+ ((int)start_pos / 75)%60);
+#endif
- /* reposition at the start of the sectors */
- this->input->seek(this->input, this->data_start, SEEK_SET);
- }
+ /* reposition at the chosen sector */
+ this->current_pos = start_pos * CD_RAW_SECTOR_SIZE;
+ this->input->seek(this->input, this->data_start+this->current_pos, SEEK_SET);
+ this->seek_flag = 1;
+ this->status = DEMUX_OK;
return this->status;
}
@@ -278,6 +557,10 @@ static int demux_str_get_status (demux_plugin_t *this_gen) {
}
static int demux_str_get_stream_length (demux_plugin_t *this_gen) {
+ demux_str_t *this = (demux_str_t *) this_gen;
+
+ return (int)((int64_t) this->input->get_length(this->input)
+ * 1000 / (CD_RAW_SECTOR_SIZE * 75));
return 0;
}
@@ -291,6 +574,11 @@ static int demux_str_get_optional_data(demux_plugin_t *this_gen,
return DEMUX_OPTIONAL_UNSUPPORTED;
}
+static char *get_extensions (demux_class_t *this_gen) {
+ /* also .mov, but we don't want to hijack that extension */
+ return "str iki ik2 dps dat xa xa1 xa2 xas xap";
+}
+
static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *stream,
input_plugin_t *input_gen) {
@@ -298,7 +586,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
demux_str_t *this;
if (! (input->get_capabilities(input) & INPUT_CAP_SEEKABLE)) {
- printf(_("demux_str.c: input not seekable, can not handle!\n"));
+ printf(_("PSX STR: input not seekable, can not handle!\n"));
return NULL;
}
@@ -332,18 +620,28 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
break;
case METHOD_BY_EXTENSION: {
- char *ending, *mrl;
+ char *ending, *mrl, i, *extn;
mrl = input->get_mrl (input);
ending = strrchr(mrl, '.');
if (!ending) {
- free (this);
+ free(this);
return NULL;
}
- if (strncasecmp (ending, ".str", 4)) {
+ /* find if any of the extensions match */
+ extn = get_extensions((demux_class_t *) this);
+ for (i = 0; *extn; extn++) {
+ if (*extn == ' ') {
+ if (ending[i+1] == '\0') break;
+ }
+ else {
+ if (*extn == ending[i+1]) i++; else i = 0;
+ }
+ }
+ if (ending[i+1] != '\0') {
free (this);
return NULL;
}
@@ -354,7 +652,6 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
}
}
-
break;
default:
@@ -375,18 +672,12 @@ static char *get_identifier (demux_class_t *this_gen) {
return "PSX STR";
}
-static char *get_extensions (demux_class_t *this_gen) {
- return "str";
-}
-
static char *get_mimetypes (demux_class_t *this_gen) {
return NULL;
}
static void class_dispose (demux_class_t *this_gen) {
-
demux_str_class_t *this = (demux_str_class_t *) this_gen;
-
free (this);
}
diff --git a/src/libxineadec/adpcm.c b/src/libxineadec/adpcm.c
index 12ac4c6da..87b03b4dd 100644
--- a/src/libxineadec/adpcm.c
+++ b/src/libxineadec/adpcm.c
@@ -23,8 +23,15 @@
* formats that various entities have created. Details about the data
* formats can be found here:
* http://www.pcisys.net/~melanson/codecs/
+ * CD-ROM/XA ADPCM decoder by Stuart Caie (kyzer@4u.net)
+ * - based on information in the USENET post by Jac Goudsmit (jac@codim.nl)
+ * <01bbc34c$dbf64020$f9c8a8c0@cray.codim.nl>
+ * - tested for correctness using Jon Atkins's CDXA software:
+ * http://jonatkins.org/cdxa/
+ * this is also useful for extracting streams from Playstation discs
*
- * $Id: adpcm.c,v 1.28 2003/02/14 00:55:52 miguelfreitas Exp $
+ *
+ * $Id: adpcm.c,v 1.29 2003/02/14 04:32:28 tmmm Exp $
*/
#include <stdio.h>
@@ -83,6 +90,10 @@ static int ea_adpcm_table[] = {
3, 4, 7, 8, 10, 11, 0, -1, -3, -4
};
+static int xa_adpcm_table[] = {
+ 0, 240, 460, 392, 0, 0, -208, -220
+};
+
#define QT_IMA_ADPCM_PREAMBLE_SIZE 2
#define QT_IMA_ADPCM_BLOCK_SIZE 0x22
#define QT_IMA_ADPCM_SAMPLES_PER_BLOCK \
@@ -134,6 +145,12 @@ typedef struct adpcm_decoder_s {
unsigned int in_block_size;
unsigned int out_block_size; /* size in samples (2 bytes/sample) */
+ int xa_mode; /* 1 for mode A, 0 for mode B or mode C */
+ int xa_p_l; /* previous sample, left/mono channel */
+ int xa_p_r; /* previous sample, right channel */
+ int xa_pp_l; /* 2nd-previous sample, left/mono channel */
+ int xa_pp_r; /* 2nd-previous sample, right channel */
+
} adpcm_decoder_t;
/*
@@ -1147,6 +1164,202 @@ static void dialogic_ima_decode_block(adpcm_decoder_t *this, buf_element_t *buf)
this->size = 0;
}
+
+static void xa_adpcm_decode_block(adpcm_decoder_t *this, buf_element_t *buf) {
+ int32_t p_l, pp_l, coeff_p_l, coeff_pp_l, range_l;
+ int32_t p_r, pp_r, coeff_p_r, coeff_pp_r, range_r;
+ int32_t snd_group, snd_unit, snd_data, samp, i, j;
+ uint8_t *inp;
+
+ /* restore decoding history */
+ p_l = this->xa_p_l; pp_l = this->xa_pp_l;
+ p_r = this->xa_p_r; pp_r = this->xa_pp_r;
+
+ inp = &this->buf[0];
+ j = 0;
+
+ if (this->xa_mode) {
+ if (this->channels == 2) {
+ /* mode A (8 bits per sample / 4 sound units) stereo
+ * - sound units 0,2 are left channel, 1,3 are right channel
+ * - sound data (8 bits) is shifted left to 16-bit border, then
+ * shifted right by the range parameter, therefore it's shifted
+ * (8-range) bits left.
+ * - two coefficients tables (4 entries each) are merged into one
+ * - coefficients are multiples of 1/256, so '>> 8' is applied
+ * after multiplication to get correct answer.
+ */
+ for (snd_group = 0; snd_group < 18; snd_group++, inp += 128) {
+ for (snd_unit = 0; snd_unit < 4; snd_unit += 2) {
+ /* get left channel coeffs and range */
+ coeff_p_l = xa_adpcm_table[((inp[snd_unit] >> 4) & 0x3)];
+ coeff_pp_l = xa_adpcm_table[((inp[snd_unit] >> 4) & 0x3) + 4];
+ range_l = 8 - (inp[snd_unit] & 0xF);
+
+ /* get right channel coeffs and range */
+ coeff_p_r = xa_adpcm_table[((inp[snd_unit+1] >> 4) & 0x3)];
+ coeff_pp_r = xa_adpcm_table[((inp[snd_unit+1] >> 4) & 0x3) + 4];
+ range_r = 8 - (inp[snd_unit+1] & 0xF);
+
+ for (snd_data = 0; snd_data < 28; snd_data++) {
+ /* left channel */
+ samp = ((signed char *)inp)[16 + (snd_data << 2) + snd_unit];
+ samp <<= range_l;
+ samp += (coeff_p_l * p_l + coeff_pp_l * pp_l) >> 8;
+ CLAMP_S16(samp);
+ pp_l = p_l;
+ p_l = samp;
+ this->decode_buffer[j++] = (unsigned short) samp;
+
+ /* right channel */
+ samp = ((signed char *)inp)[16 + (snd_data << 2) + snd_unit+1];
+ samp <<= range_r;
+ samp += (coeff_p_r * p_r + coeff_pp_r * pp_r) >> 8;
+ CLAMP_S16(samp);
+ pp_r = p_r;
+ p_r = samp;
+ this->decode_buffer[j++] = (unsigned short) samp;
+ }
+ }
+ }
+ }
+ else {
+ /* mode A (8 bits per sample / 4 sound units) mono
+ * - other details as before
+ */
+ for (snd_group = 0; snd_group < 18; snd_group++, inp += 128) {
+ for (snd_unit = 0; snd_unit < 4; snd_unit++) {
+ /* get coeffs and range */
+ coeff_p_l = xa_adpcm_table[((inp[snd_unit] >> 4) & 0x3)];
+ coeff_pp_l = xa_adpcm_table[((inp[snd_unit] >> 4) & 0x3) + 4];
+ range_l = 8 - (inp[snd_unit] & 0xF);
+
+ for (snd_data = 0; snd_data < 28; snd_data++) {
+ samp = ((signed char *)inp)[16 + (snd_data << 2) + snd_unit];
+ samp <<= range_l;
+ samp += (coeff_p_l * p_l + coeff_pp_l * pp_l) >> 8;
+ CLAMP_S16(samp);
+ pp_l = p_l; p_l = samp;
+ this->decode_buffer[j++] = (unsigned short) samp;
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (this->channels == 2) {
+ /* mode B/C (4 bits per sample / 8 sound units) stereo
+ * - sound units 0,2,4,6 are left channel, 1,3,5,7 are right channel
+ * - sound parameters 0-7 are stored as 16 bytes in the order
+ * "0123012345674567", so inp[x+4] gives sound parameter x while
+ * inp[x] doesn't.
+ * - sound data (4 bits) is shifted left to 16-bit border, then
+ * shifted right by the range parameter, therefore it's shifted
+ * (12-range) bits left.
+ * - other details as before
+ */
+ for (snd_group = 0; snd_group < 18; snd_group++, inp += 128) {
+ for (snd_unit = 0; snd_unit < 8; snd_unit += 2) {
+ /* get left channel coeffs and range */
+ coeff_p_l = xa_adpcm_table[((inp[snd_unit+4] >> 4) & 0x3)];
+ coeff_pp_l = xa_adpcm_table[((inp[snd_unit+4] >> 4) & 0x3) + 4];
+ range_l = 12 - (inp[snd_unit+4] & 0xF);
+
+ /* get right channel coeffs and range */
+ coeff_p_r = xa_adpcm_table[((inp[snd_unit+5] >> 4) & 0x3)];
+ coeff_pp_r = xa_adpcm_table[((inp[snd_unit+5] >> 4) & 0x3) + 4];
+ range_r = 12 - (inp[snd_unit+5] & 0xF);
+
+ for (snd_data = 0; snd_data < 28; snd_data++) {
+ /* left channel */
+ samp = (inp[16 + (snd_data << 2) + (snd_unit >> 1)]) & 0xF;
+ SE_4BIT(samp);
+ samp <<= range_l;
+ samp += (coeff_p_l * p_l + coeff_pp_l * pp_l) >> 8;
+ CLAMP_S16(samp);
+ pp_l = p_l;
+ p_l = samp;
+ this->decode_buffer[j++] = (unsigned short) samp;
+
+ /* right channel */
+ samp = (inp[16 + (snd_data << 2) + (snd_unit >> 1)] >> 4) & 0xF;
+ SE_4BIT(samp);
+ samp <<= range_r;
+ samp += (coeff_p_r * p_r + coeff_pp_r * pp_r) >> 8;
+ CLAMP_S16(samp);
+ pp_r = p_r;
+ p_r = samp;
+ this->decode_buffer[j++] = (unsigned short) samp;
+ }
+ }
+ }
+ }
+ else {
+ /* mode B or C (4 bits per sample / 8 sound units) mono
+ * - other details as before
+ */
+ for (snd_group = 0; snd_group < 18; snd_group++, inp += 128) {
+ for (snd_unit = 0; snd_unit < 8; snd_unit++) {
+ /* get coeffs and range */
+ coeff_p_l = xa_adpcm_table[((inp[snd_unit+4] >> 4) & 0x3)];
+ coeff_pp_l = xa_adpcm_table[((inp[snd_unit+4] >> 4) & 0x3) + 4];
+ range_l = 12 - (inp[snd_unit+4] & 0xF);
+
+ for (snd_data = 0; snd_data < 28; snd_data++) {
+ samp = inp[16 + (snd_data << 2) + (snd_unit >> 1)];
+ if (snd_unit & 1) samp >>= 4; samp &= 0xF;
+ SE_4BIT(samp);
+ samp <<= range_l;
+ samp += (coeff_p_l * p_l + coeff_pp_l * pp_l) >> 8;
+ CLAMP_S16(samp);
+ pp_l = p_l;
+ p_l = samp;
+ this->decode_buffer[j++] = (unsigned short) samp;
+ }
+ }
+ }
+ }
+ }
+
+ /* store decoding history */
+ this->xa_p_l = p_l; this->xa_pp_l = pp_l;
+ this->xa_p_r = p_r; this->xa_pp_r = pp_r;
+
+ /* despatch the decoded audio */
+ i = 0;
+ while (i < j) {
+ audio_buffer_t *audio_buffer;
+ int bytes_to_send;
+
+ audio_buffer= this->stream->audio_out->get_buffer(this->stream->audio_out);
+ if (audio_buffer->mem_size == 0) {
+ printf ("adpcm: Help! Allocated audio buffer with nothing in it!\n");
+ return;
+ }
+
+ if (((j - i) * 2) > audio_buffer->mem_size) {
+ bytes_to_send = audio_buffer->mem_size;
+ }
+ else {
+ bytes_to_send = (j - i) * 2;
+ }
+
+ xine_fast_memcpy(audio_buffer->mem, &this->decode_buffer[i],
+ bytes_to_send);
+
+ audio_buffer->num_frames = bytes_to_send / (2 * this->channels);
+ audio_buffer->vpts = buf->pts;
+ buf->pts = 0;
+ this->stream->audio_out->put_buffer(this->stream->audio_out,
+ audio_buffer, this->stream);
+
+ i += bytes_to_send / 2;
+ }
+
+ /* reset input buffer */
+ this->size = 0;
+}
+
static void adpcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {
adpcm_decoder_t *this = (adpcm_decoder_t *) this_gen;
@@ -1163,7 +1376,7 @@ static void adpcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {
this->size = 0;
/* load the stream information */
- switch (buf->type) {
+ switch (buf->type & 0xFFFF0000) {
case BUF_AUDIO_MSADPCM:
this->stream->meta_info[XINE_META_INFO_AUDIOCODEC] =
@@ -1210,6 +1423,11 @@ static void adpcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {
strdup("Dialogic IMA ADPCM");
break;
+ case BUF_AUDIO_XA_ADPCM:
+ this->stream->meta_info[XINE_META_INFO_AUDIOCODEC] =
+ strdup("CD-ROM/XA ADPCM");
+ break;
+
}
/* if the data was transported in an MS-type file (packet size will be
@@ -1274,6 +1492,20 @@ static void adpcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {
this->decode_buffer = xine_xmalloc(this->out_block_size * 2);
}
+ /* XA blocks are always 2304 bytes of input data. For output, there
+ * are 18 sound groups. These sound groups have 4 sound units (mode A)
+ * or 8 sound units (mode B or mode C). The sound units have 28 sound
+ * data samples. So, either 18*4*28=2016 or 18*8*28=4032 samples per
+ * sector. 2 bytes per sample means 4032 or 8064 bytes per sector.
+ */
+ if ((buf->type & 0xFFFF0000) == BUF_AUDIO_XA_ADPCM) {
+ /* initialise decoder state */
+ this->xa_mode = buf->decoder_info[2];
+ this->xa_p_l = this->xa_pp_l = this->xa_p_r = this->xa_pp_r = 0;
+ /* allocate 2 bytes per sample */
+ this->decode_buffer = xine_xmalloc((this->xa_mode) ? 4032 : 8064);
+ }
+
return;
}
@@ -1302,7 +1534,7 @@ static void adpcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {
/* time to decode a frame */
if (buf->decoder_flags & BUF_FLAG_FRAME_END) {
- switch(buf->type) {
+ switch(buf->type & 0xFFFF0000) {
case BUF_AUDIO_MSADPCM:
ms_adpcm_decode_block(this, buf);
@@ -1339,6 +1571,10 @@ static void adpcm_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) {
case BUF_AUDIO_DIALOGIC_IMA:
dialogic_ima_decode_block(this, buf);
break;
+
+ case BUF_AUDIO_XA_ADPCM:
+ xa_adpcm_decode_block(this, buf);
+ break;
}
}
}
@@ -1428,7 +1664,7 @@ static uint32_t audio_types[] = {
BUF_AUDIO_QTIMAADPCM, BUF_AUDIO_DK3ADPCM,
BUF_AUDIO_DK4ADPCM, BUF_AUDIO_SMJPEG_IMA,
BUF_AUDIO_VQA_IMA, BUF_AUDIO_EA_ADPCM,
- BUF_AUDIO_DIALOGIC_IMA,
+ BUF_AUDIO_DIALOGIC_IMA, BUF_AUDIO_XA_ADPCM,
0
};