ffmpeg / libavformat / psxstr.c @ a2704c97
History | View | Annotate | Download (8.25 KB)
1 | 3f16d933 | Mike Melanson | /*
|
---|---|---|---|
2 | * Sony Playstation (PSX) STR File Demuxer
|
||
3 | * Copyright (c) 2003 The ffmpeg Project
|
||
4 | *
|
||
5 | b78e7197 | Diego Biurrun | * This file is part of FFmpeg.
|
6 | *
|
||
7 | * FFmpeg is free software; you can redistribute it and/or
|
||
8 | 3f16d933 | Mike Melanson | * modify it under the terms of the GNU Lesser General Public
|
9 | * License as published by the Free Software Foundation; either
|
||
10 | b78e7197 | Diego Biurrun | * version 2.1 of the License, or (at your option) any later version.
|
11 | 3f16d933 | Mike Melanson | *
|
12 | b78e7197 | Diego Biurrun | * FFmpeg is distributed in the hope that it will be useful,
|
13 | 3f16d933 | Mike Melanson | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
15 | * Lesser General Public License for more details.
|
||
16 | *
|
||
17 | * You should have received a copy of the GNU Lesser General Public
|
||
18 | b78e7197 | Diego Biurrun | * License along with FFmpeg; if not, write to the Free Software
|
19 | 5509bffa | Diego Biurrun | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
20 | 3f16d933 | Mike Melanson | */
|
21 | |||
22 | /**
|
||
23 | ba87f080 | Diego Biurrun | * @file
|
24 | 3f16d933 | Mike Melanson | * PSX STR file demuxer
|
25 | * by Mike Melanson (melanson@pcisys.net)
|
||
26 | * This module handles streams that have been ripped from Sony Playstation
|
||
27 | * CD games. This demuxer can handle either raw STR files (which are just
|
||
28 | * concatenations of raw compact disc sectors) or STR files with 0x2C-byte
|
||
29 | * RIFF headers, followed by CD sectors.
|
||
30 | */
|
||
31 | |||
32 | 6a5d31ac | Diego Biurrun | #include "libavutil/intreadwrite.h" |
33 | 3f16d933 | Mike Melanson | #include "avformat.h" |
34 | |||
35 | 3a278992 | Mike Melanson | #define RIFF_TAG MKTAG('R', 'I', 'F', 'F') |
36 | #define CDXA_TAG MKTAG('C', 'D', 'X', 'A') |
||
37 | 3f16d933 | Mike Melanson | |
38 | e1002454 | Michael Niedermayer | #define RAW_CD_SECTOR_SIZE 2352 |
39 | 3f16d933 | Mike Melanson | #define RAW_CD_SECTOR_DATA_SIZE 2304 |
40 | e1002454 | Michael Niedermayer | #define VIDEO_DATA_CHUNK_SIZE 0x7E0 |
41 | #define VIDEO_DATA_HEADER_SIZE 0x38 |
||
42 | #define RIFF_HEADER_SIZE 0x2C |
||
43 | 3f16d933 | Mike Melanson | |
44 | #define CDXA_TYPE_MASK 0x0E |
||
45 | #define CDXA_TYPE_DATA 0x08 |
||
46 | #define CDXA_TYPE_AUDIO 0x04 |
||
47 | #define CDXA_TYPE_VIDEO 0x02 |
||
48 | |||
49 | #define STR_MAGIC (0x80010160) |
||
50 | |||
51 | typedef struct StrChannel { |
||
52 | /* video parameters */
|
||
53 | int video_stream_index;
|
||
54 | 73d3a14d | Michael Niedermayer | AVPacket tmp_pkt; |
55 | 3f16d933 | Mike Melanson | |
56 | /* audio parameters */
|
||
57 | int audio_stream_index;
|
||
58 | } StrChannel; |
||
59 | |||
60 | typedef struct StrDemuxContext { |
||
61 | |||
62 | /* a STR file can contain up to 32 channels of data */
|
||
63 | StrChannel channels[32];
|
||
64 | } StrDemuxContext; |
||
65 | |||
66 | ed4e20ac | Diego Biurrun | static const char sync_header[12] = {0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00}; |
67 | 93cb9d7f | Mike Melanson | |
68 | 3f16d933 | Mike Melanson | static int str_probe(AVProbeData *p) |
69 | { |
||
70 | 876ef93d | Michael Niedermayer | uint8_t *sector= p->buf; |
71 | 3f16d933 | Mike Melanson | |
72 | 6e264d45 | Michael Niedermayer | if (p->buf_size < RAW_CD_SECTOR_SIZE)
|
73 | 3f16d933 | Mike Melanson | return 0; |
74 | |||
75 | fead30d4 | Alex Beregszaszi | if ((AV_RL32(&p->buf[0]) == RIFF_TAG) && |
76 | (AV_RL32(&p->buf[8]) == CDXA_TAG)) {
|
||
77 | 3f16d933 | Mike Melanson | |
78 | /* RIFF header seen; skip 0x2C bytes */
|
||
79 | 876ef93d | Michael Niedermayer | sector += RIFF_HEADER_SIZE; |
80 | } |
||
81 | 6e264d45 | Michael Niedermayer | |
82 | 3f16d933 | Mike Melanson | /* look for CD sync header (00, 0xFF x 10, 00) */
|
83 | 876ef93d | Michael Niedermayer | if (memcmp(sector,sync_header,sizeof(sync_header))) |
84 | 3f16d933 | Mike Melanson | return 0; |
85 | |||
86 | 6e264d45 | Michael Niedermayer | if(sector[0x11] >= 32) |
87 | return 0; |
||
88 | if( (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_VIDEO |
||
89 | && (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_AUDIO
|
||
90 | && (sector[0x12] & CDXA_TYPE_MASK) != CDXA_TYPE_DATA)
|
||
91 | return 0; |
||
92 | |||
93 | 3f16d933 | Mike Melanson | /* MPEG files (like those ripped from VCDs) can also look like this;
|
94 | * only return half certainty */
|
||
95 | return 50; |
||
96 | } |
||
97 | |||
98 | static int str_read_header(AVFormatContext *s, |
||
99 | AVFormatParameters *ap) |
||
100 | { |
||
101 | ae628ec1 | Anton Khirnov | AVIOContext *pb = s->pb; |
102 | e4141433 | Nicholas Tung | StrDemuxContext *str = s->priv_data; |
103 | 3f16d933 | Mike Melanson | unsigned char sector[RAW_CD_SECTOR_SIZE]; |
104 | int start;
|
||
105 | int i;
|
||
106 | |||
107 | /* skip over any RIFF header */
|
||
108 | b7effd4e | Anton Khirnov | if (avio_read(pb, sector, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE)
|
109 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
110 | fead30d4 | Alex Beregszaszi | if (AV_RL32(§or[0]) == RIFF_TAG) |
111 | 3f16d933 | Mike Melanson | start = RIFF_HEADER_SIZE; |
112 | else
|
||
113 | start = 0;
|
||
114 | |||
115 | 6b4aa5da | Anton Khirnov | avio_seek(pb, start, SEEK_SET); |
116 | 3f16d933 | Mike Melanson | |
117 | 73d3a14d | Michael Niedermayer | for(i=0; i<32; i++){ |
118 | str->channels[i].video_stream_index= |
||
119 | str->channels[i].audio_stream_index= -1;
|
||
120 | } |
||
121 | |||
122 | a4fe6826 | Michael Niedermayer | s->ctx_flags |= AVFMTCTX_NOHEADER; |
123 | 3f16d933 | Mike Melanson | |
124 | return 0; |
||
125 | } |
||
126 | |||
127 | static int str_read_packet(AVFormatContext *s, |
||
128 | 93cb9d7f | Mike Melanson | AVPacket *ret_pkt) |
129 | 3f16d933 | Mike Melanson | { |
130 | ae628ec1 | Anton Khirnov | AVIOContext *pb = s->pb; |
131 | e4141433 | Nicholas Tung | StrDemuxContext *str = s->priv_data; |
132 | 3f16d933 | Mike Melanson | unsigned char sector[RAW_CD_SECTOR_SIZE]; |
133 | int channel;
|
||
134 | 93cb9d7f | Mike Melanson | AVPacket *pkt; |
135 | a4fe6826 | Michael Niedermayer | AVStream *st; |
136 | 3f16d933 | Mike Melanson | |
137 | e88b67de | Michael Niedermayer | while (1) { |
138 | 3f16d933 | Mike Melanson | |
139 | b7effd4e | Anton Khirnov | if (avio_read(pb, sector, RAW_CD_SECTOR_SIZE) != RAW_CD_SECTOR_SIZE)
|
140 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
141 | 3f16d933 | Mike Melanson | |
142 | channel = sector[0x11];
|
||
143 | if (channel >= 32) |
||
144 | return AVERROR_INVALIDDATA;
|
||
145 | |||
146 | switch (sector[0x12] & CDXA_TYPE_MASK) { |
||
147 | |||
148 | case CDXA_TYPE_DATA:
|
||
149 | case CDXA_TYPE_VIDEO:
|
||
150 | 73d3a14d | Michael Niedermayer | { |
151 | 3f16d933 | Mike Melanson | |
152 | fead30d4 | Alex Beregszaszi | int current_sector = AV_RL16(§or[0x1C]); |
153 | int sector_count = AV_RL16(§or[0x1E]); |
||
154 | int frame_size = AV_RL32(§or[0x24]); |
||
155 | fdb5932e | Michael Niedermayer | |
156 | if(!( frame_size>=0 |
||
157 | && current_sector < sector_count |
||
158 | && sector_count*VIDEO_DATA_CHUNK_SIZE >=frame_size)){ |
||
159 | av_log(s, AV_LOG_ERROR, "Invalid parameters %d %d %d\n", current_sector, sector_count, frame_size);
|
||
160 | 435a6082 | Michael Niedermayer | break;
|
161 | fdb5932e | Michael Niedermayer | } |
162 | |||
163 | a4fe6826 | Michael Niedermayer | if(str->channels[channel].video_stream_index < 0){ |
164 | /* allocate a new AVStream */
|
||
165 | st = av_new_stream(s, 0);
|
||
166 | if (!st)
|
||
167 | return AVERROR(ENOMEM);
|
||
168 | av_set_pts_info(st, 64, 1, 15); |
||
169 | |||
170 | str->channels[channel].video_stream_index = st->index; |
||
171 | |||
172 | 72415b2a | Stefano Sabatini | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
173 | a4fe6826 | Michael Niedermayer | st->codec->codec_id = CODEC_ID_MDEC; |
174 | st->codec->codec_tag = 0; /* no fourcc */ |
||
175 | st->codec->width = AV_RL16(§or[0x28]);
|
||
176 | st->codec->height = AV_RL16(§or[0x2A]);
|
||
177 | } |
||
178 | |||
179 | 3f16d933 | Mike Melanson | /* if this is the first sector of the frame, allocate a pkt */
|
180 | 73d3a14d | Michael Niedermayer | pkt = &str->channels[channel].tmp_pkt; |
181 | fdb5932e | Michael Niedermayer | |
182 | if(pkt->size != sector_count*VIDEO_DATA_CHUNK_SIZE){
|
||
183 | if(pkt->data)
|
||
184 | av_log(s, AV_LOG_ERROR, "missmatching sector_count\n");
|
||
185 | av_free_packet(pkt); |
||
186 | if (av_new_packet(pkt, sector_count*VIDEO_DATA_CHUNK_SIZE))
|
||
187 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
188 | 3f16d933 | Mike Melanson | |
189 | a2704c97 | Anton Khirnov | pkt->pos= avio_tell(pb) - RAW_CD_SECTOR_SIZE; |
190 | 115329f1 | Diego Biurrun | pkt->stream_index = |
191 | 3f16d933 | Mike Melanson | str->channels[channel].video_stream_index; |
192 | } |
||
193 | |||
194 | fdb5932e | Michael Niedermayer | memcpy(pkt->data + current_sector*VIDEO_DATA_CHUNK_SIZE, |
195 | sector + VIDEO_DATA_HEADER_SIZE, |
||
196 | VIDEO_DATA_CHUNK_SIZE); |
||
197 | |||
198 | 93cb9d7f | Mike Melanson | if (current_sector == sector_count-1) { |
199 | fdb5932e | Michael Niedermayer | pkt->size= frame_size; |
200 | 93cb9d7f | Mike Melanson | *ret_pkt = *pkt; |
201 | fdb5932e | Michael Niedermayer | pkt->data= NULL;
|
202 | pkt->size= -1;
|
||
203 | 93cb9d7f | Mike Melanson | return 0; |
204 | } |
||
205 | 3f16d933 | Mike Melanson | |
206 | } |
||
207 | break;
|
||
208 | |||
209 | case CDXA_TYPE_AUDIO:
|
||
210 | a4fe6826 | Michael Niedermayer | if(str->channels[channel].audio_stream_index < 0){ |
211 | ad9c1055 | Michael Niedermayer | int fmt = sector[0x13]; |
212 | a4fe6826 | Michael Niedermayer | /* allocate a new AVStream */
|
213 | st = av_new_stream(s, 0);
|
||
214 | if (!st)
|
||
215 | return AVERROR(ENOMEM);
|
||
216 | |||
217 | str->channels[channel].audio_stream_index = st->index; |
||
218 | |||
219 | 72415b2a | Stefano Sabatini | st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
220 | a4fe6826 | Michael Niedermayer | st->codec->codec_id = CODEC_ID_ADPCM_XA; |
221 | st->codec->codec_tag = 0; /* no fourcc */ |
||
222 | st->codec->channels = (fmt&1)?2:1; |
||
223 | st->codec->sample_rate = (fmt&4)?18900:37800; |
||
224 | // st->codec->bit_rate = 0; //FIXME;
|
||
225 | st->codec->block_align = 128;
|
||
226 | |||
227 | av_set_pts_info(st, 64, 128, st->codec->sample_rate); |
||
228 | } |
||
229 | 44369b45 | Michael Niedermayer | pkt = ret_pkt; |
230 | if (av_new_packet(pkt, 2304)) |
||
231 | return AVERROR(EIO);
|
||
232 | memcpy(pkt->data,sector+24,2304); |
||
233 | |||
234 | pkt->stream_index = |
||
235 | str->channels[channel].audio_stream_index; |
||
236 | return 0; |
||
237 | 93cb9d7f | Mike Melanson | break;
|
238 | 3f16d933 | Mike Melanson | default:
|
239 | fd147f23 | Michael Niedermayer | av_log(s, AV_LOG_WARNING, "Unknown sector type %02X\n", sector[0x12]); |
240 | 3f16d933 | Mike Melanson | /* drop the sector and move on */
|
241 | break;
|
||
242 | } |
||
243 | |||
244 | if (url_feof(pb))
|
||
245 | 6f3e0b21 | Panagiotis Issaris | return AVERROR(EIO);
|
246 | 3f16d933 | Mike Melanson | } |
247 | } |
||
248 | |||
249 | static int str_read_close(AVFormatContext *s) |
||
250 | { |
||
251 | e4141433 | Nicholas Tung | StrDemuxContext *str = s->priv_data; |
252 | 66602c67 | Michael Niedermayer | int i;
|
253 | for(i=0; i<32; i++){ |
||
254 | if(str->channels[i].tmp_pkt.data)
|
||
255 | av_free_packet(&str->channels[i].tmp_pkt); |
||
256 | } |
||
257 | 3f16d933 | Mike Melanson | |
258 | return 0; |
||
259 | } |
||
260 | |||
261 | c6610a21 | Diego Elio Pettenò | AVInputFormat ff_str_demuxer = { |
262 | 3f16d933 | Mike Melanson | "psxstr",
|
263 | bde15e74 | Stefano Sabatini | NULL_IF_CONFIG_SMALL("Sony Playstation STR format"),
|
264 | 3f16d933 | Mike Melanson | sizeof(StrDemuxContext),
|
265 | str_probe, |
||
266 | str_read_header, |
||
267 | str_read_packet, |
||
268 | str_read_close, |
||
269 | }; |