osdir.com
mailing list archive

Subject: patch: PSX STR demux improvement + CDXA ADPCM - msg#00179

List: video.xine.devel

Date: Prev Next Index Thread: Prev Next Index
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(&sector[0x20]);
+
+ if (LE_32(&sector[0x18 + 0x00]) != STR_MAGIC)
+ 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;
@@ -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, &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;

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, &sector[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 = {
Was this page helpful?
Yes No
Thread at a glance:

Previous Message by Date: click to view message preview

Re: xine development process [was: xine and post plugin segfault.]

Hi guenter, > > Uohm, well, maybe I understood Miguel wrong in the details, but > > it's still my opinion that too much branching causes too much > > confusion and we all seem to agree on that. > > definitely - actually this was the starting point of this discussion > ;) when it was proposed to create all sorts of experimental branches > for every new feature (that's what it sounded like to me). > > however, create small release branches for every release still seems > like a very good idea to me This is something I can easily agree with. Michael -- /* James M doesn't say fuck enough. */ 2.4.3 linux/net/core/netfilter.c ------------------------------------------------------- This SF.NET email is sponsored by: SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See! http://www.vasoftware.com

Next Message by Date: click to view message preview

Are DVD menus undetectable??

Hi all, I'm currently looking into a way to detect the presence of interactive DVD content, eg menus, in order to automatically launch appropriate controls (like xine-ui's DVD navigator window). Currently, xine doesn't provide any interface for that, probably because for dvdnav, there is no real difference between just video or an (animated) menu... However, it may be possible to detect the presence of a sensitive area on the screen, no? Any ideas? Cheers, Siggi ------------------------------------------------------- This SF.NET email is sponsored by: SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See! http://www.vasoftware.com

Previous Message by Thread: click to view message preview

does libxine read id3?

hi, i'm thinking about using the xine engine in an audioplayer type application because xine is able to play mp3/ogg/flac/wav/whatever audio formats... but i need to be able to read the meta-info.. that means the id3(v2) tags for mp3 files and the tags in the ogg-vorbis and flac files... does libxine support reading those tags? thanks, gabor ------------------------------------------------------- This SF.NET email is sponsored by: SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See! http://www.vasoftware.com

Next Message by Thread: click to view message preview

Re: patch: PSX STR demux improvement + CDXA ADPCM

Hi Stuart, First off, many thanks for taking the initiative on this. It is something I have been wanting to get to for some time now. On Wed, 12 Feb 2003, Stuart Caie wrote: > 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. I would personally prefer to see the MDEC subsystem incorporated into the common ffmpeg codebase. Someone on the MPlayer list recently posted a standalone player that can decode the MDEC data: http://www.mplayerhq.hu/pipermail/mplayer-dev-eng/2003-February/016189.html Since it is very close to MPEG, it makes sense to put it in ffmpeg's lavc. It sounds like there are several variations of MDEC data and I am not clear on the differences. > 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? This probably accounts for the mono problem: + audio_buffer->num_frames = (bytes_to_send / 4); This implies that each frame is 4 bytes which is true for stereo (2 bytes for each of the 2 channels). Mono data will only have 2 bytes per frame. The correct formula would be: # of frames = bytes_to_send / 2 / channels Try it out and see if it fixes anything. If it does, you can send the patch directly to me. > 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 I have taken the liberty of mirroring these samples at: ftp://ftp.mplayerhq.hu/MPlayer/samples/game-formats/psx-str/ > - 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? Audio, video: The time code works the same way. It keys off the extra_info field in the buffer. This structure has input_pos, input_length, and input_time fields that need to be filled in for the timecode to be accurate. Check the CDDA demuxer for a simple example. > - 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? This problem exists since I don't completely understand how these files work nor the structure of the PSX discs. But I will tell you that my eventual goal is to adapt an input plugin to be able to play the media directly from a PSX disc. Though I am still not entirely clear on how to do this. > 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. Using the extra_data fields you will be able to set the timecodes to correspond with the actual times, if you so choose. > - 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 I've always wondered what that cdxa screen means... > 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? You should be able to dynamically change the properties of the audio and video on the fly. > 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. Quite reasonable. Input plugins are known to do such things (DVD input caches CSS keys, CDDA input checks online CD database). Thanks for the work and keep us posted. I will be testing out your patch in the near future. -- -Mike Melanson ------------------------------------------------------- This sf.net email is sponsored by:ThinkGeek Welcome to geek heaven. http://thinkgeek.com/sf
Loading Comments...
Home | News | Patents | Sitemap | FAQ | advertise

Advertising by