vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / media / plugins / asf_reader / ASFFileReader.cpp
bloba71b8a9b9f68c781a53baacf62f2fc84bfe42a69
1 /*
2 * Copyright (c) 2005, David McPaul
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.
27 #include "ASFFileReader.h"
29 #include <DataIO.h>
30 #include <SupportKit.h>
32 #include <iostream>
35 ASFFileReader::ASFFileReader(BPositionIO *pStream)
37 theStream = pStream;
39 // Find Size of Stream, need to rethink this for non seekable streams
40 theStream->Seek(0,SEEK_END);
41 StreamSize = theStream->Position();
42 theStream->Seek(0,SEEK_SET);
43 packet = asf_packet_create();
47 ASFFileReader::~ASFFileReader()
49 if (packet) {
50 asf_packet_destroy(packet);
51 packet = NULL;
54 if (asfFile) {
55 asf_close(asfFile);
56 asfFile = NULL;
59 theStream = NULL;
60 streams.clear();
63 status_t
64 ASFFileReader::ParseFile()
66 asf_iostream_t ioStream;
67 ioStream.opaque = theStream;
68 ioStream.read = &ASFFileReader::read;
69 ioStream.write = &ASFFileReader::write;
70 ioStream.seek = &ASFFileReader::seek;
72 asfFile = asf_open_cb(&ioStream);
74 int result = asf_init(asfFile);
76 if (result != 0) {
77 printf("error initialising asf asf_init returned %d\n",result);
78 return B_ERROR;
81 asf_metadata_t *metadata = asf_header_get_metadata(asfFile);
83 if (metadata) {
84 printf("Title %s\n",metadata->title);
85 printf("Artist %s\n",metadata->artist);
86 printf("Copyright %s\n",metadata->copyright);
87 printf("Description %s\n",metadata->description);
88 printf("Rating %s\n",metadata->rating);
89 printf("Additional Entries %d\n",metadata->extended_count);
91 asf_metadata_destroy(metadata);
94 uint32 totalStreams = getStreamCount();
95 StreamEntry streamEntry;
97 for (uint32 i=0;i < totalStreams;i++) {
98 streamEntry.streamIndex = i;
99 streams.push_back(streamEntry);
102 ParseIndex();
104 // load the first packet
105 if (asf_get_packet(asfFile, packet) < 0) {
106 printf("Could not get first packet\n");
107 return B_ERROR;
110 return B_OK;
114 bool
115 ASFFileReader::IsEndOfData(off_t pPosition)
117 return true;
121 bool
122 ASFFileReader::IsEndOfFile(off_t position)
124 return (position >= StreamSize);
128 bool
129 ASFFileReader::IsEndOfFile()
131 return theStream->Position() >= StreamSize;
134 uint32
135 ASFFileReader::getStreamCount()
137 return asf_get_stream_count(asfFile) + 1;
140 bool
141 ASFFileReader::getAudioFormat(uint32 streamIndex, ASFAudioFormat *format)
143 asf_waveformatex_t *audioHeader;
144 asf_stream_t *stream;
146 if (IsAudio(streamIndex)) {
147 stream = asf_get_stream(asfFile, streamIndex);
149 if (stream) {
150 audioHeader = (asf_waveformatex_t *)(stream->properties);
151 format->Compression = audioHeader->wFormatTag;
152 format->NoChannels = audioHeader->nChannels;
153 format->SamplesPerSec = audioHeader->nSamplesPerSec;
154 format->AvgBytesPerSec = audioHeader->nAvgBytesPerSec;
155 format->BlockAlign = audioHeader->nBlockAlign;
156 format->BitsPerSample = audioHeader->wBitsPerSample;
157 format->extraDataSize = audioHeader->cbSize;
158 format->extraData = audioHeader->data;
160 return true;
164 return false;
167 bool
168 ASFFileReader::getVideoFormat(uint32 streamIndex, ASFVideoFormat *format)
170 asf_bitmapinfoheader_t *videoHeader;
171 asf_stream_t *stream;
173 if (IsVideo(streamIndex)) {
174 stream = asf_get_stream(asfFile, streamIndex);
176 if (stream) {
177 videoHeader = (asf_bitmapinfoheader_t *)(stream->properties);
178 format->Compression = videoHeader->biCompression;
179 format->VideoWidth = videoHeader->biWidth;
180 format->VideoHeight = videoHeader->biHeight;
181 format->Planes = videoHeader->biPlanes;
182 format->BitCount = videoHeader->biBitCount;
183 format->extraDataSize = videoHeader->biSize - 40;
184 format->extraData = videoHeader->data;
186 if (stream->flags & ASF_STREAM_FLAG_EXTENDED) {
187 format->FrameScale = stream->extended_properties->avg_time_per_frame;
188 format->FrameRate = 10000000L;
189 printf("num avg time per frame for video %Ld\n",stream->extended_properties->avg_time_per_frame);
192 return true;
196 return false;
200 bigtime_t
201 ASFFileReader::getStreamDuration(uint32 streamIndex)
203 if (streamIndex < streams.size()) {
204 return streams[streamIndex].getDuration();
207 asf_stream_t *stream;
209 stream = asf_get_stream(asfFile, streamIndex);
211 if (stream) {
212 if (stream->flags & ASF_STREAM_FLAG_EXTENDED) {
213 printf("STREAM %ld end time %Ld, start time %Ld\n",streamIndex, stream->extended_properties->end_time, stream->extended_properties->start_time);
214 if (stream->extended_properties->end_time - stream->extended_properties->start_time > 0) {
215 return stream->extended_properties->end_time - stream->extended_properties->start_time;
220 return asf_get_duration(asfFile) / 10L;
223 uint32
224 ASFFileReader::getFrameCount(uint32 streamIndex)
226 if (streamIndex < streams.size()) {
227 return streams[streamIndex].getFrameCount();
230 return 0;
233 bool
234 ASFFileReader::IsVideo(uint32 streamIndex)
236 asf_stream_t *stream;
238 stream = asf_get_stream(asfFile, streamIndex);
240 if (stream) {
241 return ((stream->type == ASF_STREAM_TYPE_VIDEO) && (stream->flags & ASF_STREAM_FLAG_AVAILABLE));
244 return false;
248 bool
249 ASFFileReader::IsAudio(uint32 streamIndex)
251 asf_stream_t *stream;
253 stream = asf_get_stream(asfFile, streamIndex);
255 if (stream) {
256 return ((stream->type == ASF_STREAM_TYPE_AUDIO) && (stream->flags & ASF_STREAM_FLAG_AVAILABLE));
259 return false;
262 IndexEntry
263 ASFFileReader::GetIndex(uint32 streamIndex, uint32 frameNo)
265 return streams[streamIndex].GetIndex(frameNo);
268 bool
269 ASFFileReader::HasIndex(uint32 streamIndex, uint32 frameNo)
271 if (streamIndex < streams.size()) {
272 return streams[streamIndex].HasIndex(frameNo);
275 return false;
278 uint32
279 ASFFileReader::GetFrameForTime(uint32 streamIndex, bigtime_t time)
281 if (streamIndex < streams.size()) {
282 return streams[streamIndex].GetIndex(time).frameNo;
285 return 0;
288 void
289 ASFFileReader::ParseIndex() {
290 // Try to build some sort of useful index
291 // packet->send_time seems to be a better presentation time stamp than pts though
293 if (asf_seek_to_msec(asfFile,0) < 0) {
294 printf("Seek to start of stream failed\n");
297 asf_payload_t *payload;
299 while (asf_get_packet(asfFile, packet) > 0) {
300 for (int i=0;i<packet->payload_count;i++) {
301 payload = (asf_payload_t *)(&packet->payloads[i]);
302 // printf("Payload %d Stream %d Keyframe %d send time %Ld pts %Ld id %d size %d\n",i+1,payload->stream_number,payload->key_frame, 1000L * bigtime_t(packet->send_time), 1000L * bigtime_t(payload->pts), payload->media_object_number, payload->datalen);
303 if (payload->stream_number < streams.size()) {
304 streams[payload->stream_number].AddPayload(payload->media_object_number, payload->key_frame, 1000L * payload->pts, payload->datalen, false);
309 for (uint32 i=0;i<streams.size();i++) {
310 streams[i].AddPayload(0, false, 0, 0, true);
311 streams[i].setDuration(1000L * (packet->send_time + packet->duration));
314 if (asf_seek_to_msec(asfFile,0) < 0) {
315 printf("Seek to start of stream failed\n");
319 bool
320 ASFFileReader::GetNextChunkInfo(uint32 streamIndex, uint32 pFrameNo,
321 char **buffer, uint32 *size, bool *keyframe, bigtime_t *pts)
323 // Ok, Need to join payloads together that have the same payload->media_object_number
324 asf_payload_t *payload;
325 int64_t seekResult;
326 int packetSize;
328 IndexEntry indexEntry = GetIndex(streamIndex, pFrameNo);
330 if (indexEntry.noPayloads == 0) {
331 // No index entry
332 printf("No Index entry for frame %ld\n",pFrameNo);
333 return false;
336 // printf("Stream %ld need pts %Ld, packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration));
338 if (1000LL * packet->send_time > indexEntry.pts || 1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
339 seekResult = asf_seek_to_msec(asfFile, indexEntry.pts/1000);
340 if (seekResult >= 0) {
341 // printf("Stream %ld seeked to %Ld got %Ld\n",streamIndex,indexEntry.pts, 1000L * seekResult);
342 packetSize = asf_get_packet(asfFile, packet);
343 if (packetSize <= 0) {
344 printf("Failed to Get Packet after seek result (%d)\n",packetSize);
345 return false;
347 } else if (seekResult == ASF_ERROR_SEEKABLE) {
348 // Stream not seekeable. Is what we want forward in the stream, if so seek using Get Packet
349 if (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
350 while (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
351 packetSize = asf_get_packet(asfFile, packet);
352 if (packetSize <= 0) {
353 printf("Failed to Seek using Get Packet result (%d)\n",packetSize);
354 return false;
356 // printf("Stream %ld searching forward for pts %Ld, got packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration));
358 } else {
359 // seek to 0 and read forward, going to be a killer on performance
360 seekResult = asf_seek_to_msec(asfFile, 0);
361 while (1000LL * (packet->send_time + packet->duration) < indexEntry.pts) {
362 packetSize = asf_get_packet(asfFile, packet);
363 if (packetSize <= 0) {
364 printf("Failed to Seek using Get Packet result (%d)\n",packetSize);
365 return false;
367 // printf("Stream %ld searching forward from 0 for pts %Ld, got packet start %Ld packet end %Ld\n",streamIndex,indexEntry.pts,1000LL * packet->send_time,1000LL * (packet->send_time + packet->duration));
370 } else {
371 printf("Seek failed\n");
372 return false;
376 // fillin some details
377 *size = indexEntry.dataSize;
378 *keyframe = indexEntry.keyFrame;
379 *pts = indexEntry.pts;
381 uint32 expectedPayloads = indexEntry.noPayloads;
382 uint32 offset = 0;
384 for (int i=0;i<packet->payload_count;i++) {
385 payload = (asf_payload_t *)(&packet->payloads[i]);
386 // find the first payload matching the id we want and then
387 // combine the next x payloads where x is the noPayloads in indexEntry
388 if (payload->media_object_number == indexEntry.id && payload->stream_number == streamIndex) {
389 // copy data to buffer
390 memcpy(*buffer + offset, payload->data, payload->datalen);
391 offset += payload->datalen;
392 expectedPayloads--;
394 if (expectedPayloads == 0) {
395 return true;
400 // combine packets into a single buffer
401 packetSize = asf_get_packet(asfFile, packet);
402 while ((packetSize > 0) && (expectedPayloads > 0)) {
403 for (int i=0;i<packet->payload_count;i++) {
404 payload = (asf_payload_t *)(&packet->payloads[i]);
405 // find the first payload matching the id we want and then
406 // combine the next x payloads where x is the noPayloads in indexEntry
407 if (payload->media_object_number == indexEntry.id && payload->stream_number == streamIndex) {
408 // copy data to buffer
409 memcpy(*buffer + offset, payload->data, payload->datalen);
410 offset += payload->datalen;
411 expectedPayloads--;
412 if (expectedPayloads == 0) {
413 return true;
417 packetSize = asf_get_packet(asfFile, packet);
420 if (packetSize == ASF_ERROR_EOF) {
421 printf("Unexpected EOF file truncated?\n");
422 } else {
423 printf("EOF? %ld,%d\n",expectedPayloads, packetSize);
426 return false;
430 /* static */
431 bool
432 ASFFileReader::IsSupported(BPositionIO *source)
434 // Read first 4 bytes and if they match 30 26 b2 75 we have a asf file
435 uint8 header[4];
436 bool supported = false;
438 off_t position = source->Position();
440 if (source->Read(&header[0],4) == 4) {
441 supported = header[0] == 0x30 &&
442 header[1] == 0x26 &&
443 header[2] == 0xb2 &&
444 header[3] == 0x75;
447 source->Seek(position,SEEK_SET);
449 return supported;
452 /* static */
453 int32_t
454 ASFFileReader::read(void *opaque, void *buffer, int32_t size)
456 // opaque is the BPositionIO
457 return reinterpret_cast<BPositionIO *>(opaque)->Read(buffer, size);
460 /* static */
461 int32_t
462 ASFFileReader::write(void *opaque, void *buffer, int32_t size)
464 return reinterpret_cast<BPositionIO *>(opaque)->Write(buffer, size);
467 /* static */
468 int64_t
469 ASFFileReader::seek(void *opaque, int64_t offset)
471 return reinterpret_cast<BPositionIO *>(opaque)->Seek(offset, SEEK_SET);