Hello everyone,
I was very excited to see a PSX STR demuxer listed in xine, as I thought
that meant I could watch the STR movies. Sadly, it was just a demuxer. And
it also failed to recognise many of my str files.
So, I've enhanced the demux_str.c plugin, and I've also added the CDXA
method to the adpcm.c plugin. This is attached as a patch.
I haven't yet started on the MDEC (video) stream, although I have source
from Playstation emulators to do this. It looks very much like a tweaked
form of MPEG, with only i-frames, 16x16 macroblocks and RLE instead of huffman.
However, I have some problems so far and I wanted to ask the developers for
their opinions:
- Stereo ADPCM decoding (mode B/C) works fine, however, mono ADPCM decoding
(mode B/C) always fails. Despite setting the correct sample rate and number
of channels, and also calculating the right pts position, mono decoding
always sounds like it's speeded up, and continually interspersed with gaps
of silence. It sounds like the samples are being played too quickly, then
the gap is introduced to wait for the remaining duration of the time the
samples should have played for. I have checked the B/C mono decoding loop,
it is decoding the correct number of samples. What could be wrong?
To help you out, here is a short stereo STR file:
http://kyz.mine.nu/psx/Um_Jammer_Lammy/EU_Version/logo.iki
and a short mono STR file:
http://kyz.mine.nu/psx/Broken_Sword/river1.str
If you need a long mono STR file, try:
http://kyz.mine.nu/psx/Um_Jammer_Lammy/EU_Version/st3.xa
- The time-code on the GUI doesn't change. I can accept it if this is
because the video decoder isn't implemented, however this also happens in
purely audio streams. What am I doing wrong?
- Currently, the demuxer only plays the first discovered channel with video
frames and the first discovered channel with audio frames. How would it be
possible for the user to pick video/audio channels with an MRL, and what
would be the best way to switch audio/video channels during playback?
There is the little problem that video frames are timed 'locally', i.e. if
frame 0 of a video starts at 10 minutes into the stream, its pts will be
00:00:00 despite the 'global' time being 00:10:00. The same is true of audio
decoding at the moment, it simply multiplies the number of audio frames
decoded by the time each decoded frame plays for. Completely accurate if
both video and audio begin at the start of a stream, but too simple for
seeking around.
For seeking around, the CD timecode can generally be trusted for a rough
estimate of where you are in the stream, but it can't be used as a pts code
for video/audio sync -- each CD sector has a different timecode, and the 1
audio sector that plays for the frame stored in the previous 7 video sectors
is 7/75 seconds late.
- Some streams can be very hairy - Look at
http://kyz.mine.nu/r4.png - it is
mostly audio (blue), video (green) with EOF markers (yellow), but right at
the end of channel 1 are some mixed video/audio frames (cyan) -- this is the
intro movie to Ridge Racer type 4. As a user, I'd be interested in playing
that, but as there are no details about that video until the first video
frame, which is several minutes into that stream, so I can't provide video
codec initialisation data. How could this be done?
As it's probably unacceptable to scan the entire stream in advance when
playing, is perhaps the answer to write a PSX input plugin, which will do
proper scanning from the CD, and also look up / generate CDXA maps, as
stored in ~/.cdxa ? This can't be done by the demuxer, as the filenames of
the maps are prefixed by the CD title and full path of the file the stream
is from, however this isn't stored in the stream header.
Regards
Stuart
diff -r -U 4 xine-lib.cvs/src/demuxers/demux_str.c
xine-lib.new/src/demuxers/demux_str.c
--- xine-lib.cvs/src/demuxers/demux_str.c 2003-02-12 15:40:47.000000000
+0000
+++ xine-lib.new/src/demuxers/demux_str.c 2003-02-12 15:43:26.000000000
+0000
@@ -23,8 +23,89 @@
*
* $Id: demux_str.c,v 1.4 2003/01/26 15:56:21 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
#include "config.h"
#endif
@@ -39,13 +120,27 @@
#include "compat.h"
#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 ) | \
( (long)(unsigned char)(ch1) << 16 ) | \
@@ -79,13 +174,17 @@
off_t data_start;
off_t data_size;
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];
+ unsigned int video_channel;
+ unsigned int audio_channel;
+ unsigned int audio_frame;
+ unsigned int audio_frame_duration;
char last_mrl[1024];
-
} demux_str_t;
typedef struct {
@@ -101,61 +200,158 @@
/* 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;
+ this->bih[0].biWidth = this->bih[0].biHeight = 0;
+ this->audio_channel = this->video_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;
+ local_offset = 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;
+ this->data_start = (off_t) local_offset;
- /* 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->audio_channel = this->video_channel = -1;
+ for (channel = 0; channel < STR_MAX_CHANNELS; channel++) {
+ this->channel_type[channel] = 0;
+ }
- /* check if this an audio or video sector (bit 1 = video, bit 2 =
- * audio) */
- if ((check_bytes[this->data_start + 0x12] & 0x06) == 0x2) {
-
- /* video is suspected */
-
- this->bih.biWidth = LE_16(&check_bytes[this->data_start + 0x28]);
- this->bih.biHeight = LE_16(&check_bytes[this->data_start + 0x2A]);
-
- /* sanity check for the width and height */
- if ((this->bih.biWidth <= 0) ||
- (this->bih.biWidth > 320) ||
- (this->bih.biHeight <= 0) ||
- (this->bih.biHeight > 240))
+ /* we need to check up to 32 sectors for up to 32 audio/video channels */
+ for (sector = 0; sector < STR_MAX_CHANNELS; sector++) {
+
+#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
+
+ /* 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;
+ }
+
+ /* 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)) {
+ if (LE_32(&check_bytes[local_offset + 0x18]) == STR_MAGIC) {
+ /* store the first PSX STR video channel we see */
+ if (this->video_channel == -1) this->video_channel = (int) channel;
+ this->bih[channel].biWidth =
+ LE_16(&check_bytes[local_offset + 0x18 + 0x10]);
+ this->bih[channel].biHeight =
+ LE_16(&check_bytes[local_offset + 0x18 + 0x12]);
+ this->channel_type[channel] |= CDXA_TYPE_VIDEO;
+ }
+ }
+ break;
+
+ case CDXA_TYPE_AUDIO:
+ /* first time we have seen audio in this channel? */
+ if (!(this->channel_type[channel] & CDXA_TYPE_AUDIO)) {
+ /* store the first audio channel we see */
+ if (this->audio_channel == -1) this->audio_channel = (int) channel;
+ this->audio_info[channel] = check_bytes[local_offset + 0x13];
+ this->channel_type[channel] |= CDXA_TYPE_AUDIO;
+ }
+ 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) {
@@ -165,17 +361,27 @@
unsigned int frame_number;
buf_element_t *buf;
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) {
+ /* ensure we're only playing sectors for this audio or video channel */
+ if (sector[0x11] != this->video_channel &&
+ sector[0x11] != this->audio_channel)
+ return 0;
+ switch (sector[0x12] & CDXA_TYPE_MASK) {
+ case CDXA_TYPE_VIDEO:
+ case CDXA_TYPE_DATA:
/* video chunk */
- frame_number = LE_32(§or[0x20]);
+
+ if (LE_32(§or[0x18 + 0x00]) != STR_MAGIC)
+ return 0;
+
+ frame_number = LE_32(§or[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;
@@ -183,42 +389,78 @@
buf->pts = frame_number * FRAME_DURATION;
buf->extra_info->input_time = buf->pts / 90;
/* constant size chunk */
- buf->size = 2048;
- memcpy(buf->content, §or[0x38], 2048);
+ buf->size = 2304;
+ xine_fast_memcpy(buf->content, §or[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(§or[0x1C]) + 1) == LE_16(§or[0x1E]))
+ if ((LE_16(§or[0x18+0x04]) + 1) == LE_16(§or[0x18+0x06]))
buf->decoder_flags |= BUF_FLAG_FRAME_END;
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->size = 2304;
+ buf->pts = this->audio_frame++ * this->audio_frame_duration;
+ xine_fast_memcpy(buf->content, §or[0x18], 2304);
+ buf->type = BUF_AUDIO_XA_ADPCM;
+ 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;
+ char audio_info = 0;
buf_element_t *buf;
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;
+ if (this->video_channel != -1) {
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] =
+ this->bih[this->video_channel].biWidth;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] =
+ this->bih[this->video_channel].biHeight;
+ }
+ else {
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 1;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_WIDTH] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_VIDEO_HEIGHT] = 0;
+ }
+
+ if (this->audio_channel != -1) {
+ audio_info = this->audio_info[this->audio_channel];
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1;
+ 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;
+ }
+ else {
+ this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = 0;
+ this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = 0;
+ }
/* send start buffers */
xine_demux_control_start(this->stream);
@@ -233,15 +475,27 @@
this->video_fifo->put (this->video_fifo, buf);
/* send init info to the audio decoder */
if (this->audio_fifo) {
+ this->audio_frame = 0;
+ this->audio_frame_duration = (audio_info & 0x10) ? 2016 : 4032;
+ if (audio_info & 0x01) this->audio_frame_duration >>= 1;
+ this->audio_frame_duration = (this->audio_frame_duration * 90000) /
+ ((audio_info & 0x04) ? 18900 : 37800);
+#ifdef LOG
+ printf("PSX STR: audio frame duration = ((%d / %d) * 90000) / %d = %d\n",
+ (audio_info & 0x10) ? 2016 : 4032,
+ (audio_info & 0x01) ? 2 : 1,
+ (audio_info & 0x04) ? 18900 : 37800,
+ this->audio_frame_duration);
+#endif
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;
+ 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);
}
}
@@ -257,9 +511,8 @@
xine_demux_control_newpts(this->stream, 0, 0);
this->status = DEMUX_OK;
-
/* reposition at the start of the sectors */
this->input->seek(this->input, this->data_start, SEEK_SET);
}
@@ -290,16 +543,21 @@
void *data, int data_type) {
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) {
input_plugin_t *input = (input_plugin_t *) input_gen;
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;
}
this = xine_xmalloc (sizeof (demux_str_t));
@@ -331,9 +589,9 @@
}
break;
case METHOD_BY_EXTENSION: {
- char *ending, *mrl;
+ char *ending, *mrl, i, *extn;
mrl = input->get_mrl (input);
ending = strrchr(mrl, '.');
@@ -342,9 +600,19 @@
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;
}
@@ -353,9 +621,8 @@
return NULL;
}
}
-
break;
default:
free (this);
@@ -374,20 +641,14 @@
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);
}
void *demux_str_init_plugin (xine_t *xine, void *data) {
diff -r -U 4 xine-lib.cvs/src/libxineadec/adpcm.c
xine-lib.new/src/libxineadec/adpcm.c
--- xine-lib.cvs/src/libxineadec/adpcm.c 2003-02-12 15:41:29.000000000
+0000
+++ xine-lib.new/src/libxineadec/adpcm.c 2003-02-12 15:39:05.000000000
+0000
@@ -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@xxxxxx)
+ * - based on information in the USENET post by Jac Goudsmit (jac@xxxxxxxx)
+ * <01bbc34c$dbf64020$f9c8a8c0@xxxxxxxxxxxxx>
+ * - 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.27 2003/01/08 01:02:30 miguelfreitas Exp $
*/
#include <stdio.h>
@@ -82,8 +89,12 @@
0, 240, 460, 392, 0, 0, -208, -220, 0, 1,
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 \
((QT_IMA_ADPCM_BLOCK_SIZE - QT_IMA_ADPCM_PREAMBLE_SIZE) * 2)
@@ -133,8 +144,14 @@
unsigned short *decode_buffer;
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;
/*
* decode_ima_nibbles
@@ -1146,8 +1163,204 @@
/* reset buffer */
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 / 4);
+ 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;
if (buf->decoder_flags & BUF_FLAG_HEADER) {
@@ -1209,8 +1422,13 @@
this->stream->meta_info[XINE_META_INFO_AUDIOCODEC] =
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
* non-0 indicating an audio header), create a decode buffer */
@@ -1270,8 +1488,22 @@
/* allocate 2 bytes per sample */
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 == 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;
}
if (!this->output_open) {
@@ -1335,8 +1567,12 @@
case BUF_AUDIO_DIALOGIC_IMA:
dialogic_ima_decode_block(this, buf);
break;
+
+ case BUF_AUDIO_XA_ADPCM:
+ xa_adpcm_decode_block(this, buf);
+ break;
}
}
}
@@ -1424,9 +1660,9 @@
BUF_AUDIO_MSADPCM, BUF_AUDIO_MSIMAADPCM,
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
};
static decoder_info_t dec_info_audio = {