2 * Copyright (c) 2005, David McPaul
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"
30 #include <SupportKit.h>
35 ASFFileReader::ASFFileReader(BPositionIO
*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()
50 asf_packet_destroy(packet
);
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
);
77 printf("error initialising asf asf_init returned %d\n",result
);
81 asf_metadata_t
*metadata
= asf_header_get_metadata(asfFile
);
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
);
104 // load the first packet
105 if (asf_get_packet(asfFile
, packet
) < 0) {
106 printf("Could not get first packet\n");
115 ASFFileReader::IsEndOfData(off_t pPosition
)
122 ASFFileReader::IsEndOfFile(off_t position
)
124 return (position
>= StreamSize
);
129 ASFFileReader::IsEndOfFile()
131 return theStream
->Position() >= StreamSize
;
135 ASFFileReader::getStreamCount()
137 return asf_get_stream_count(asfFile
) + 1;
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
);
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
;
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
);
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
);
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
);
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;
224 ASFFileReader::getFrameCount(uint32 streamIndex
)
226 if (streamIndex
< streams
.size()) {
227 return streams
[streamIndex
].getFrameCount();
234 ASFFileReader::IsVideo(uint32 streamIndex
)
236 asf_stream_t
*stream
;
238 stream
= asf_get_stream(asfFile
, streamIndex
);
241 return ((stream
->type
== ASF_STREAM_TYPE_VIDEO
) && (stream
->flags
& ASF_STREAM_FLAG_AVAILABLE
));
249 ASFFileReader::IsAudio(uint32 streamIndex
)
251 asf_stream_t
*stream
;
253 stream
= asf_get_stream(asfFile
, streamIndex
);
256 return ((stream
->type
== ASF_STREAM_TYPE_AUDIO
) && (stream
->flags
& ASF_STREAM_FLAG_AVAILABLE
));
263 ASFFileReader::GetIndex(uint32 streamIndex
, uint32 frameNo
)
265 return streams
[streamIndex
].GetIndex(frameNo
);
269 ASFFileReader::HasIndex(uint32 streamIndex
, uint32 frameNo
)
271 if (streamIndex
< streams
.size()) {
272 return streams
[streamIndex
].HasIndex(frameNo
);
279 ASFFileReader::GetFrameForTime(uint32 streamIndex
, bigtime_t time
)
281 if (streamIndex
< streams
.size()) {
282 return streams
[streamIndex
].GetIndex(time
).frameNo
;
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");
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
;
328 IndexEntry indexEntry
= GetIndex(streamIndex
, pFrameNo
);
330 if (indexEntry
.noPayloads
== 0) {
332 printf("No Index entry for frame %ld\n",pFrameNo
);
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
);
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
);
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));
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
);
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));
371 printf("Seek failed\n");
376 // fillin some details
377 *size
= indexEntry
.dataSize
;
378 *keyframe
= indexEntry
.keyFrame
;
379 *pts
= indexEntry
.pts
;
381 uint32 expectedPayloads
= indexEntry
.noPayloads
;
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
;
394 if (expectedPayloads
== 0) {
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
;
412 if (expectedPayloads
== 0) {
417 packetSize
= asf_get_packet(asfFile
, packet
);
420 if (packetSize
== ASF_ERROR_EOF
) {
421 printf("Unexpected EOF file truncated?\n");
423 printf("EOF? %ld,%d\n",expectedPayloads
, packetSize
);
432 ASFFileReader::IsSupported(BPositionIO
*source
)
434 // Read first 4 bytes and if they match 30 26 b2 75 we have a asf file
436 bool supported
= false;
438 off_t position
= source
->Position();
440 if (source
->Read(&header
[0],4) == 4) {
441 supported
= header
[0] == 0x30 &&
447 source
->Seek(position
,SEEK_SET
);
454 ASFFileReader::read(void *opaque
, void *buffer
, int32_t size
)
456 // opaque is the BPositionIO
457 return reinterpret_cast<BPositionIO
*>(opaque
)->Read(buffer
, size
);
462 ASFFileReader::write(void *opaque
, void *buffer
, int32_t size
)
464 return reinterpret_cast<BPositionIO
*>(opaque
)->Write(buffer
, size
);
469 ASFFileReader::seek(void *opaque
, int64_t offset
)
471 return reinterpret_cast<BPositionIO
*>(opaque
)->Seek(offset
, SEEK_SET
);