vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / plugins / au_reader / au_reader.cpp
blob6f45c99bfcfc7b20215e2a4a86cd9151e65fe753
1 /*
2 * Copyright (c) 2003-2004, Marcus Overhagen
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
22 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
23 * OF THE POSSIBILITY OF SUCH DAMAGE.
25 #include <stdio.h>
26 #include <string.h>
27 #include <malloc.h>
28 #include <DataIO.h>
29 #include <ByteOrder.h>
30 #include <InterfaceDefs.h>
31 #include "RawFormats.h"
32 #include "au_reader.h"
34 //#define TRACE_AU_READER
35 #ifdef TRACE_AU_READER
36 #define TRACE printf
37 #else
38 #define TRACE(a...)
39 #endif
41 #define BUFFER_SIZE 16384
43 #define UINT32(a) ((uint32)B_BENDIAN_TO_HOST_INT32((a)))
45 auReader::auReader()
47 TRACE("auReader::auReader\n");
48 fBuffer = 0;
52 auReader::~auReader()
54 if (fBuffer)
55 free(fBuffer);
59 const char *
60 auReader::Copyright()
62 return ".au & .snd reader, " B_UTF8_COPYRIGHT " by Marcus Overhagen";
66 status_t
67 auReader::Sniff(int32 *streamCount)
69 TRACE("auReader::Sniff\n");
71 fSource = dynamic_cast<BPositionIO *>(Reader::Source());
72 if (!fSource) {
73 TRACE("auReader::Sniff: not a BPositionIO\n");
74 return B_ERROR;
77 int64 filesize = Source()->Seek(0, SEEK_END);
78 if (filesize < sizeof(struct snd_header)) {
79 TRACE("auReader::Sniff: File too small\n");
80 return B_ERROR;
83 struct snd_header header;
85 if (sizeof(header) != Source()->ReadAt(0, &header, sizeof(header))) {
86 TRACE("auReader::Sniff: header reading failed\n");
87 return B_ERROR;
90 if (UINT32(header.magic) != SND_MAGIC) {
91 TRACE("auReader::Sniff: header not recognized\n");
92 return B_ERROR;
95 TRACE("auReader::Sniff: we found something that looks like:\n");
97 TRACE(" data_start %ld\n", UINT32(header.data_start));
98 TRACE(" data_size %ld\n", UINT32(header.data_size));
99 TRACE(" data_format %ld\n", UINT32(header.data_format));
100 TRACE(" sampling_rate %ld\n", UINT32(header.sampling_rate));
101 TRACE(" channel_count %ld\n", UINT32(header.channel_count));
103 fDataStart = UINT32(header.data_start);
104 fDataSize = UINT32(header.data_size);
105 fChannelCount = UINT32(header.channel_count);
106 fFrameRate = UINT32(header.sampling_rate);
107 fFormatCode = UINT32(header.data_format);
109 if (fDataStart > filesize) {
110 TRACE("auReader::Sniff: data start too large\n");
111 return B_ERROR;
113 if (fDataStart + fDataSize > filesize)
114 fDataSize = filesize - fDataStart;
115 if (fDataSize < 1) {
116 TRACE("auReader::Sniff: data size too small\n");
117 return B_ERROR;
119 if (fChannelCount < 1)
120 fChannelCount = 1;
121 if (fFrameRate < 1)
122 fFrameRate = 44100;
124 switch (fFormatCode) {
125 case SND_FORMAT_UNSPECIFIED: TRACE("SND_FORMAT_UNSPECIFIED\n"); break;
126 case SND_FORMAT_MULAW_8: TRACE("SND_FORMAT_MULAW_8\n"); break;
127 case SND_FORMAT_LINEAR_8: TRACE("SND_FORMAT_LINEAR_8\n"); break;
128 case SND_FORMAT_LINEAR_16: TRACE("SND_FORMAT_LINEAR_16\n"); break;
129 case SND_FORMAT_LINEAR_24: TRACE("SND_FORMAT_LINEAR_24\n"); break;
130 case SND_FORMAT_LINEAR_32: TRACE("SND_FORMAT_LINEAR_32\n"); break;
131 case SND_FORMAT_FLOAT: TRACE("SND_FORMAT_FLOAT\n"); break;
132 case SND_FORMAT_DOUBLE: TRACE("SND_FORMAT_DOUBLE\n"); break;
133 case SND_FORMAT_INDIRECT: TRACE("SND_FORMAT_INDIRECT\n"); break;
134 case SND_FORMAT_NESTED: TRACE("SND_FORMAT_NESTED\n"); break;
135 case SND_FORMAT_DSP_CORE: TRACE("SND_FORMAT_DSP_CORE\n"); break;
136 case SND_FORMAT_DSP_DATA_8: TRACE("SND_FORMAT_DSP_DATA_8\n"); break;
137 case SND_FORMAT_DSP_DATA_16: TRACE("SND_FORMAT_DSP_DATA_16\n"); break;
138 case SND_FORMAT_DSP_DATA_24: TRACE("SND_FORMAT_DSP_DATA_24\n"); break;
139 case SND_FORMAT_DSP_DATA_32: TRACE("SND_FORMAT_DSP_DATA_32\n"); break;
140 case SND_FORMAT_DISPLAY: TRACE("SND_FORMAT_DISPLAY\n"); break;
141 case SND_FORMAT_MULAW_SQUELCH: TRACE("SND_FORMAT_MULAW_SQUELCH\n"); break;
142 case SND_FORMAT_EMPHASIZED: TRACE("SND_FORMAT_EMPHASIZED\n"); break;
143 case SND_FORMAT_COMPRESSED: TRACE("SND_FORMAT_COMPRESSED\n"); break;
144 case SND_FORMAT_COMPRESSED_EMPHASIZED: TRACE("SND_FORMAT_COMPRESSED_EMPHASIZED\n"); break;
145 case SND_FORMAT_DSP_COMMANDS: TRACE("SND_FORMAT_DSP_COMMANDS\n"); break;
146 case SND_FORMAT_DSP_COMMANDS_SAMPLES: TRACE("SND_FORMAT_DSP_COMMANDS_SAMPLES\n"); break;
147 case SND_FORMAT_ADPCM_G721: TRACE("SND_FORMAT_ADPCM_G721\n"); break;
148 case SND_FORMAT_ADPCM_G722: TRACE("SND_FORMAT_ADPCM_G722\n"); break;
149 case SND_FORMAT_ADPCM_G723_3: TRACE("SND_FORMAT_ADPCM_G723_3\n"); break;
150 case SND_FORMAT_ADPCM_G723_5: TRACE("SND_FORMAT_ADPCM_G723_5\n"); break;
151 case SND_FORMAT_ALAW_8: TRACE("SND_FORMAT_ALAW_8\n"); break;
154 switch (fFormatCode) {
155 case SND_FORMAT_MULAW_8:
156 fBitsPerSample = 8; fRaw = false; break;
157 case SND_FORMAT_LINEAR_8:
158 fBitsPerSample = 8; fRaw = true; break;
159 case SND_FORMAT_LINEAR_16:
160 fBitsPerSample = 16; fRaw = true; break;
161 case SND_FORMAT_LINEAR_24:
162 fBitsPerSample = 24; fRaw = true; break;
163 case SND_FORMAT_LINEAR_32:
164 fBitsPerSample = 32; fRaw = true; break;
165 case SND_FORMAT_FLOAT:
166 fBitsPerSample = 32; fRaw = true; break;
167 case SND_FORMAT_DOUBLE:
168 fBitsPerSample = 64; fRaw = true; break;
169 case SND_FORMAT_ADPCM_G721:
170 fBitsPerSample = 4; fRaw = false; break;
171 case SND_FORMAT_ADPCM_G722:
172 fBitsPerSample = 8; fRaw = false; break;
173 case SND_FORMAT_ADPCM_G723_3:
174 fBitsPerSample = 3; fRaw = false; break;
175 case SND_FORMAT_ADPCM_G723_5:
176 fBitsPerSample = 5; fRaw = false; break;
177 case SND_FORMAT_ALAW_8:
178 fBitsPerSample = 8; fRaw = false; break;
179 default:
180 fBitsPerSample = 0; break;
182 if (fBitsPerSample == 0) {
183 TRACE("auReader::Sniff: sample format not recognized\n");
184 return B_ERROR;
187 fFrameCount = (8 * fDataSize) / (fChannelCount * fBitsPerSample);
188 fDuration = (1000000LL * fFrameCount) / fFrameRate;
189 fBitsPerFrame = fChannelCount * fBitsPerSample;
190 fBlockAlign = fBitsPerFrame;
191 while (fBlockAlign % 8 && fBlockAlign < 1000)
192 fBlockAlign += fBlockAlign;
193 if (fBlockAlign % 8) {
194 TRACE("auReader::Sniff: can't find block alignment, fChannelCount %d, fBitsPerSample %d\n", fChannelCount, fBitsPerSample);
195 return B_ERROR;
197 fBlockAlign /= 8;
199 fPosition = 0;
201 fBufferSize = (BUFFER_SIZE / fBlockAlign) * fBlockAlign;
202 fBuffer = malloc(fBufferSize);
204 TRACE(" fDataStart %Ld\n", fDataStart);
205 TRACE(" fDataSize %Ld\n", fDataSize);
206 TRACE(" fFrameCount %Ld\n", fFrameCount);
207 TRACE(" fDuration %Ld\n", fDuration);
208 TRACE(" fChannelCount %d\n", fChannelCount);
209 TRACE(" fFrameRate %ld\n", fFrameRate);
210 TRACE(" fBitsPerSample %d\n", fBitsPerSample);
211 TRACE(" fBlockAlign %d\n", fBlockAlign);
212 TRACE(" fFormatCode %ld\n", fFormatCode);
213 TRACE(" fRaw %d\n", fRaw);
215 BMediaFormats formats;
216 if (fRaw) {
217 // a raw PCM format
218 media_format_description description;
219 description.family = B_BEOS_FORMAT_FAMILY;
220 description.u.beos.format = B_BEOS_FORMAT_RAW_AUDIO;
221 formats.GetFormatFor(description, &fFormat);
222 fFormat.u.raw_audio.frame_rate = (fFrameRate == 8012) ? SND_RATE_8012 : fFrameRate;
223 fFormat.u.raw_audio.channel_count = fChannelCount;
224 switch (fFormatCode) {
225 case SND_FORMAT_LINEAR_8:
226 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_UCHAR;
227 break;
228 case SND_FORMAT_LINEAR_16:
229 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_SHORT;
230 break;
231 case SND_FORMAT_LINEAR_24:
232 fFormat.u.raw_audio.format = B_AUDIO_FORMAT_INT24;
233 break;
234 case SND_FORMAT_LINEAR_32:
235 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_INT;
236 break;
237 case SND_FORMAT_FLOAT:
238 fFormat.u.raw_audio.format = media_raw_audio_format::B_AUDIO_FLOAT;
239 break;
240 case SND_FORMAT_DOUBLE:
241 fFormat.u.raw_audio.format = B_AUDIO_FORMAT_FLOAT64;
242 break;
243 default:
244 TRACE("auReader::Sniff: unhandled raw format\n");
245 return B_ERROR;
247 fFormat.u.raw_audio.byte_order = B_MEDIA_BIG_ENDIAN;
248 fFormat.u.raw_audio.buffer_size = fBufferSize;
249 } else {
250 // some encoded format
251 media_format_description description;
252 description.family = B_MISC_FORMAT_FAMILY;
253 description.u.misc.file_format = 'au';
254 description.u.misc.codec = fFormatCode;
255 formats.GetFormatFor(description, &fFormat);
256 fFormat.u.encoded_audio.output.frame_rate = fFrameRate;
257 fFormat.u.encoded_audio.output.channel_count = fChannelCount;
260 *streamCount = 1;
261 return B_OK;
265 void
266 auReader::GetFileFormatInfo(media_file_format *mff)
268 mff->capabilities = media_file_format::B_READABLE
269 | media_file_format::B_KNOWS_RAW_AUDIO
270 | media_file_format::B_KNOWS_ENCODED_AUDIO
271 | media_file_format::B_IMPERFECTLY_SEEKABLE;
272 mff->family = B_MISC_FORMAT_FAMILY;
273 mff->version = 100;
274 strcpy(mff->mime_type, "audio/x-au");
275 strcpy(mff->file_extension, "au");
276 strcpy(mff->short_name, "Sun audio file");
277 strcpy(mff->pretty_name, "Sun audio file");
281 status_t
282 auReader::AllocateCookie(int32 streamNumber, void **cookie)
284 return B_OK;
287 status_t
288 auReader::FreeCookie(void *cookie)
290 return B_OK;
294 status_t
295 auReader::GetStreamInfo(void *cookie, int64 *frameCount, bigtime_t *duration,
296 media_format *format, const void **infoBuffer, size_t *infoSize)
298 *frameCount = fFrameCount;
299 *duration = fDuration;
300 *format = fFormat;
301 *infoBuffer = 0;
302 *infoSize = 0;
303 return B_OK;
307 status_t
308 auReader::Seek(void *cookie,
309 uint32 seekTo,
310 int64 *frame, bigtime_t *time)
312 int64 pos;
314 if (seekTo & B_MEDIA_SEEK_TO_FRAME) {
315 if (fRaw)
316 pos = (*frame * fBitsPerFrame) / 8;
317 else
318 pos = (*frame * fDataSize) / fFrameCount;
319 pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
320 TRACE("auReader::Seek to frame %Ld, pos %Ld\n", *frame, pos);
321 } else if (seekTo & B_MEDIA_SEEK_TO_TIME) {
322 if (fRaw)
323 pos = (*time * fFrameRate * fBitsPerFrame) / (1000000LL * 8);
324 else
325 pos = (*time * fDataSize) / fDuration;
326 pos = (pos / fBlockAlign) * fBlockAlign; // round down to a block start
327 TRACE("auReader::Seek to time %Ld, pos %Ld\n", *time, pos);
328 } else {
329 return B_ERROR;
332 if (fRaw)
333 *frame = (8 * pos) / fBitsPerFrame;
334 else
335 *frame = (pos * fFrameCount) / fDataSize;
336 *time = (*frame * 1000000LL) / fFrameRate;
338 TRACE("auReader::Seek newtime %Ld\n", *time);
339 TRACE("auReader::Seek newframe %Ld\n", *frame);
341 if (pos < 0 || pos > fDataSize) {
342 TRACE("auReader::Seek invalid position %Ld\n", pos);
343 return B_ERROR;
346 fPosition = pos;
347 return B_OK;
351 status_t
352 auReader::GetNextChunk(void *cookie,
353 const void **chunkBuffer, size_t *chunkSize,
354 media_header *mediaHeader)
356 // XXX it might be much better to not return any start_time information for encoded formats here,
357 // XXX and instead use the last time returned from seek and count forward after decoding.
358 mediaHeader->start_time = (((8 * fPosition) / fBitsPerFrame) * 1000000LL) / fFrameRate;
359 mediaHeader->file_pos = fDataStart + fPosition;
361 int64 maxreadsize = fDataSize - fPosition;
362 int32 readsize = fBufferSize;
363 if (maxreadsize < readsize)
364 readsize = maxreadsize;
365 if (readsize == 0)
366 return B_LAST_BUFFER_ERROR;
368 if (readsize != Source()->ReadAt(fDataStart + fPosition, fBuffer, readsize)) {
369 TRACE("auReader::GetNextChunk: unexpected read error\n");
370 return B_ERROR;
373 // XXX if the stream has more than two channels, we need to reorder channel data here
375 fPosition += readsize;
376 *chunkBuffer = fBuffer;
377 *chunkSize = readsize;
378 return B_OK;
382 Reader *
383 auReaderPlugin::NewReader()
385 return new auReader;
389 MediaPlugin *instantiate_plugin()
391 return new auReaderPlugin;