2 * Copyright (c) 2005, David McPaul based on avi_reader copyright (c) 2004 Marcus Overhagen
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 "asf_reader.h"
28 #include "RawFormats.h"
30 #include <ByteOrder.h>
32 #include <InterfaceDefs.h>
33 #include <MediaFormats.h>
34 #include <StopWatch.h>
41 #define TRACE_ASF_READER
42 #ifdef TRACE_ASF_READER
48 #define ERROR(a...) fprintf(stderr, a)
62 uint32 bytes_per_sec_rate
;
63 uint32 bytes_per_sec_scale
;
70 uint32 frames_per_sec_rate
;
71 uint32 frames_per_sec_scale
;
75 asfReader::asfReader()
78 TRACE("asfReader::asfReader\n");
82 asfReader::~asfReader()
89 asfReader::Copyright()
91 return "asf_reader " B_UTF8_COPYRIGHT
" by David McPaul";
96 asfReader::Sniff(int32
*streamCount
)
98 TRACE("asfReader::Sniff\n");
100 BPositionIO
*pos_io_source
;
102 pos_io_source
= dynamic_cast<BPositionIO
*>(Reader::Source());
103 if (!pos_io_source
) {
104 TRACE("asfReader::Sniff: not a BPositionIO\n");
108 if (!ASFFileReader::IsSupported(pos_io_source
)) {
109 TRACE("asfReader::Sniff: unsupported file type\n");
113 TRACE("asfReader::Sniff: this stream seems to be supported\n");
115 theFileReader
= new ASFFileReader(pos_io_source
);
116 if (B_OK
!= theFileReader
->ParseFile()) {
117 ERROR("asfReader::Sniff: error parsing file\n");
121 *streamCount
= theFileReader
->getStreamCount();
123 TRACE("asfReader detected %ld streams\n",*streamCount
);
129 asfReader::GetFileFormatInfo(media_file_format
*mff
)
131 mff
->capabilities
= media_file_format::B_READABLE
132 | media_file_format::B_KNOWS_ENCODED_VIDEO
133 | media_file_format::B_KNOWS_ENCODED_AUDIO
134 | media_file_format::B_IMPERFECTLY_SEEKABLE
;
135 mff
->family
= B_MISC_FORMAT_FAMILY
;
137 strcpy(mff
->mime_type
, "video/asf");
138 strcpy(mff
->file_extension
, "asf");
139 strcpy(mff
->short_name
, "ASF");
140 strcpy(mff
->pretty_name
, "Microsoft (ASF) file format");
144 asfReader::AllocateCookie(int32 streamNumber
, void **_cookie
)
151 asf_cookie
*cookie
= new asf_cookie
;
154 cookie
->stream
= streamNumber
;
156 cookie
->buffer_size
= 0;
157 cookie
->frame_pos
= 0;
159 BMediaFormats formats
;
160 media_format
*format
= &cookie
->format
;
161 media_format_description description
;
163 ASFAudioFormat audioFormat
;
164 ASFVideoFormat videoFormat
;
166 if (theFileReader
->getVideoFormat(streamNumber
,&videoFormat
)) {
167 TRACE("Stream %ld is Video\n",streamNumber
);
168 char cc1
,cc2
,cc3
,cc4
;
170 cc1
= (char)((videoFormat
.Compression
>> 24) & 0xff);
171 cc2
= (char)((videoFormat
.Compression
>> 16) & 0xff);
172 cc3
= (char)((videoFormat
.Compression
>> 8) & 0xff);
173 cc4
= (char)((videoFormat
.Compression
>> 0) & 0xff);
175 TRACE("Compression %c%c%c%c\n", cc1
,cc2
,cc3
,cc4
);
177 TRACE("Width %ld\n",videoFormat
.VideoWidth
);
178 TRACE("Height %ld\n",videoFormat
.VideoHeight
);
179 TRACE("Planes %d\n",videoFormat
.Planes
);
180 TRACE("BitCount %d\n",videoFormat
.BitCount
);
182 codecID
= B_BENDIAN_TO_HOST_INT32(videoFormat
.Compression
);
184 cookie
->audio
= false;
185 cookie
->line_count
= videoFormat
.VideoHeight
;
186 cookie
->frame_size
= 1;
188 cookie
->duration
= theFileReader
->getStreamDuration(streamNumber
);
189 cookie
->frame_count
= theFileReader
->getFrameCount(streamNumber
);
191 TRACE("frame_count %Ld\n", cookie
->frame_count
);
192 TRACE("duration %.6f (%Ld)\n", cookie
->duration
/ 1E6
, cookie
->duration
);
193 TRACE("calculated fps=%Ld\n", cookie
->frame_count
* 1000000LL / cookie
->duration
);
195 // asf does not have a frame rate! The extended descriptor defines an average time per frame which is generally useless.
196 if (videoFormat
.FrameScale
&& videoFormat
.FrameRate
) {
197 cookie
->frames_per_sec_rate
= videoFormat
.FrameRate
;
198 cookie
->frames_per_sec_scale
= videoFormat
.FrameScale
;
199 TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (using average time per frame)\n", cookie
->frames_per_sec_rate
, cookie
->frames_per_sec_scale
);
201 cookie
->frames_per_sec_rate
= cookie
->frame_count
;
202 cookie
->frames_per_sec_scale
= cookie
->duration
/ 1000000LL;
203 TRACE("frames_per_sec_rate %ld, frames_per_sec_scale %ld (duration over frame count)\n", cookie
->frames_per_sec_rate
, cookie
->frames_per_sec_scale
);
206 description
.family
= B_AVI_FORMAT_FAMILY
;
207 description
.u
.avi
.codec
= videoFormat
.Compression
;
209 if (B_OK
!= formats
.GetFormatFor(description
, format
))
210 format
->type
= B_MEDIA_ENCODED_VIDEO
;
212 // Can we just define a set of bitrates instead of a field rate?
213 // format->u.encoded_video.max_bit_rate = 8 * theFileReader->MovMainHeader()->max_bytes_per_sec;
214 // format->u.encoded_video.avg_bit_rate = format->u.encoded_video.max_bit_rate / 2; // XXX fix this
215 format
->u
.encoded_video
.output
.field_rate
= cookie
->frames_per_sec_rate
/ (float)cookie
->frames_per_sec_scale
;
217 format
->u
.encoded_video
.avg_bit_rate
= 1;
218 format
->u
.encoded_video
.max_bit_rate
= 1;
220 format
->u
.encoded_video
.frame_size
= videoFormat
.VideoWidth
* videoFormat
.VideoHeight
* videoFormat
.Planes
/ 8;
221 format
->u
.encoded_video
.output
.display
.bytes_per_row
= videoFormat
.Planes
/ 8 * videoFormat
.VideoWidth
;
223 format
->u
.encoded_video
.output
.display
.bytes_per_row
+= format
->u
.encoded_video
.output
.display
.bytes_per_row
& 1;
225 switch (videoFormat
.BitCount
) {
227 format
->u
.encoded_video
.output
.display
.format
= B_RGB15_BIG
;
230 format
->u
.encoded_video
.output
.display
.format
= B_RGB24_BIG
;
233 format
->u
.encoded_video
.output
.display
.format
= B_RGB32_BIG
;
236 format
->u
.encoded_video
.output
.display
.format
= B_NO_COLOR_SPACE
;
237 format
->u
.encoded_video
.frame_size
= videoFormat
.VideoWidth
* videoFormat
.VideoHeight
* 8 / 8;
240 format
->u
.encoded_video
.output
.display
.line_width
= videoFormat
.VideoWidth
;
241 format
->u
.encoded_video
.output
.display
.line_count
= videoFormat
.VideoHeight
;
242 format
->u
.encoded_video
.output
.display
.pixel_offset
= 0;
243 format
->u
.encoded_video
.output
.display
.line_offset
= 0;
244 format
->u
.encoded_video
.output
.display
.flags
= 0;
245 format
->u
.encoded_video
.output
.interlace
= 1; // 1: progressive
246 format
->u
.encoded_video
.output
.first_active
= 0;
247 format
->u
.encoded_video
.output
.last_active
= format
->u
.encoded_video
.output
.display
.line_count
- 1;
248 format
->u
.encoded_video
.output
.orientation
= B_VIDEO_TOP_LEFT_RIGHT
;
249 format
->u
.encoded_video
.output
.pixel_width_aspect
= 1;
250 format
->u
.encoded_video
.output
.pixel_height_aspect
= 1;
252 TRACE("max_bit_rate %.3f\n", format
->u
.encoded_video
.max_bit_rate
);
253 TRACE("field_rate %.3f\n", format
->u
.encoded_video
.output
.field_rate
);
255 // Set the Decoder Config
256 size
= videoFormat
.extraDataSize
;
257 data
= videoFormat
.extraData
;
259 TRACE("Video Decoder Config Found Size is %ld\n",size
);
260 if (format
->SetMetaData(data
, size
) != B_OK
) {
261 ERROR("Failed to set Decoder Config\n");
266 #ifdef TRACE_ASF_READER
267 if (videoFormat
.extraData
) {
268 uint8
*p
= (uint8
*)videoFormat
.extraData
;
269 TRACE("extra_data: %ld: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
270 size
, p
[0], p
[1], p
[2], p
[3], p
[4], p
[5], p
[6], p
[7], p
[8], p
[9]);
276 // Put the codeid in the user data in case someone wants it
277 format
->user_data_type
= B_CODEC_TYPE_INFO
;
278 *(uint32
*)format
->user_data
= codecID
; format
->user_data
[4] = 0;
281 cookie
->buffer_size
= ((videoFormat
.VideoWidth
* videoFormat
.VideoHeight
* 4) + 15) & ~15; // WRONG Find max input buffer size needed
282 cookie
->buffer
= new char [cookie
->buffer_size
];
288 if (theFileReader
->getAudioFormat(streamNumber
,&audioFormat
)) {
289 TRACE("Stream %ld is Audio\n",streamNumber
);
290 TRACE("Format 0x%x\n",audioFormat
.Compression
);
291 TRACE("Channels %d\n",audioFormat
.NoChannels
);
292 TRACE("SampleRate %ld\n",audioFormat
.SamplesPerSec
);
293 TRACE("ByteRate %ld\n",audioFormat
.AvgBytesPerSec
);
294 TRACE("BlockAlign %d\n",audioFormat
.BlockAlign
);
295 TRACE("Bits %d\n",audioFormat
.BitsPerSample
);
297 //uint32 sampleSize = (audioFormat.NoChannels * audioFormat.BitsPerSample / 8);
299 cookie
->audio
= true;
300 cookie
->duration
= theFileReader
->getStreamDuration(streamNumber
);
301 // Calculate sample count using duration
302 cookie
->frame_count
= (cookie
->duration
* audioFormat
.SamplesPerSec
) / 1000000LL;
303 cookie
->frame_pos
= 0;
304 cookie
->frames_per_sec_rate
= audioFormat
.SamplesPerSec
;
305 cookie
->frames_per_sec_scale
= 1;
306 cookie
->bytes_per_sec_rate
= audioFormat
.AvgBytesPerSec
;
307 cookie
->bytes_per_sec_scale
= 1;
309 TRACE("Chunk Count %ld\n", theFileReader
->getFrameCount(streamNumber
));
310 TRACE("audio frame_count %Ld, duration %.6f\n", cookie
->frame_count
, cookie
->duration
/ 1E6
);
312 if (audioFormat
.Compression
== 0x0001) {
314 description
.family
= B_BEOS_FORMAT_FAMILY
;
315 description
.u
.beos
.format
= B_BEOS_FORMAT_RAW_AUDIO
;
316 if (formats
.GetFormatFor(description
, format
) < B_OK
)
317 format
->type
= B_MEDIA_RAW_AUDIO
;
318 format
->u
.raw_audio
.frame_rate
= audioFormat
.SamplesPerSec
;
319 format
->u
.raw_audio
.channel_count
= audioFormat
.NoChannels
;
320 if (audioFormat
.BitsPerSample
<= 8)
321 format
->u
.raw_audio
.format
= B_AUDIO_FORMAT_UINT8
;
322 else if (audioFormat
.BitsPerSample
<= 16)
323 format
->u
.raw_audio
.format
= B_AUDIO_FORMAT_INT16
;
324 else if (audioFormat
.BitsPerSample
<= 24)
325 format
->u
.raw_audio
.format
= B_AUDIO_FORMAT_INT24
;
326 else if (audioFormat
.BitsPerSample
<= 32)
327 format
->u
.raw_audio
.format
= B_AUDIO_FORMAT_INT32
;
329 ERROR("asfReader::AllocateCookie: unhandled bits per sample %d\n", audioFormat
.BitsPerSample
);
332 format
->u
.raw_audio
.format
|= B_AUDIO_FORMAT_CHANNEL_ORDER_WAVE
;
333 format
->u
.raw_audio
.byte_order
= B_MEDIA_LITTLE_ENDIAN
;
334 format
->u
.raw_audio
.buffer_size
= audioFormat
.BlockAlign
;
335 } else if (audioFormat
.Compression
== 0xa) {
336 // Windows Media Speech
339 // some encoded format
340 description
.family
= B_WAV_FORMAT_FAMILY
;
341 description
.u
.wav
.codec
= audioFormat
.Compression
;
342 if (formats
.GetFormatFor(description
, format
) < B_OK
)
343 format
->type
= B_MEDIA_ENCODED_AUDIO
;
344 format
->u
.encoded_audio
.bit_rate
= 8 * audioFormat
.AvgBytesPerSec
;
345 format
->u
.encoded_audio
.output
.frame_rate
= audioFormat
.SamplesPerSec
;
346 format
->u
.encoded_audio
.output
.channel_count
= audioFormat
.NoChannels
;
347 format
->u
.encoded_audio
.output
.buffer_size
= audioFormat
.BlockAlign
;
348 cookie
->frame_size
= audioFormat
.BlockAlign
== 0 ? 1 : audioFormat
.BlockAlign
;
350 TRACE("audio: bit_rate %.3f, frame_rate %.1f, channel_count %lu, frame_size %ld\n",
351 format
->u
.encoded_audio
.bit_rate
,
352 format
->u
.encoded_audio
.output
.frame_rate
,
353 format
->u
.encoded_audio
.output
.channel_count
,
357 cookie
->buffer_size
= (audioFormat
.BlockAlign
+ 15) & ~15;
358 cookie
->buffer
= new char [cookie
->buffer_size
];
360 // TODO: this doesn't seem to work (it's not even a fourcc)
361 format
->user_data_type
= B_CODEC_TYPE_INFO
;
362 *(uint32
*)format
->user_data
= audioFormat
.Compression
; format
->user_data
[4] = 0;
364 // Set the Decoder Config
365 size
= audioFormat
.extraDataSize
;
366 data
= audioFormat
.extraData
;
368 TRACE("Audio Decoder Config Found Size is %ld\n",size
);
369 if (format
->SetMetaData(data
, size
) != B_OK
) {
370 ERROR("Failed to set Decoder Config\n");
375 #ifdef TRACE_ASF_READER
376 if (audioFormat
.extraData
) {
377 uint8
*p
= (uint8
*)audioFormat
.extraData
;
378 TRACE("extra_data: %ld: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
379 size
, p
[0], p
[1], p
[2], p
[3], p
[4], p
[5], p
[6], p
[7], p
[8], p
[9]);
394 asfReader::FreeCookie(void *_cookie
)
396 asf_cookie
*cookie
= (asf_cookie
*)_cookie
;
398 delete [] cookie
->buffer
;
406 asfReader::GetStreamInfo(void *_cookie
, int64
*frameCount
, bigtime_t
*duration
,
407 media_format
*format
, const void **infoBuffer
, size_t *infoSize
)
409 asf_cookie
*cookie
= (asf_cookie
*)_cookie
;
410 ASFAudioFormat audioFormat
;
411 ASFVideoFormat videoFormat
;
414 *frameCount
= cookie
->frame_count
;
415 *duration
= cookie
->duration
;
416 *format
= cookie
->format
;
420 // Copy metadata to infoBuffer
421 if (theFileReader
->getVideoFormat(cookie
->stream
,&videoFormat
)) {
422 *infoSize
= videoFormat
.extraDataSize
;
423 *infoBuffer
= videoFormat
.extraData
;
424 } else if (theFileReader
->getAudioFormat(cookie
->stream
,&audioFormat
)) {
425 *infoSize
= audioFormat
.extraDataSize
;
426 *infoBuffer
= audioFormat
.extraData
;
428 ERROR("No stream Info for stream %d\n",cookie
->stream
);
431 TRACE("GetStreamInfo (%d) fc %Ld duration %Ld extra %ld\n",cookie
->stream
,*frameCount
,*duration
,*infoSize
);
438 asfReader::Seek(void *cookie
, uint32 flags
, int64
*frame
, bigtime_t
*time
)
440 // seek to the requested time or frame
441 asf_cookie
*asfCookie
= (asf_cookie
*)cookie
;
443 if (flags
& B_MEDIA_SEEK_TO_TIME
) {
444 // frame = (time * rate) / fps / 1000000LL
445 *frame
= ((*time
* asfCookie
->frames_per_sec_rate
) / (int64
)asfCookie
->frames_per_sec_scale
) / 1000000LL;
446 asfCookie
->frame_pos
= theFileReader
->GetFrameForTime(asfCookie
->stream
,*time
);
449 if (flags
& B_MEDIA_SEEK_TO_FRAME
) {
450 // time = frame * 1000000LL * fps / rate
451 *time
= (*frame
* 1000000LL * (int64
)asfCookie
->frames_per_sec_scale
) / asfCookie
->frames_per_sec_rate
;
452 asfCookie
->frame_pos
= theFileReader
->GetFrameForTime(asfCookie
->stream
,*time
);
455 TRACE("asfReader::Seek: seekTo%s%s%s%s, time %Ld, frame %Ld\n",
456 (flags
& B_MEDIA_SEEK_TO_TIME
) ? " B_MEDIA_SEEK_TO_TIME" : "",
457 (flags
& B_MEDIA_SEEK_TO_FRAME
) ? " B_MEDIA_SEEK_TO_FRAME" : "",
458 (flags
& B_MEDIA_SEEK_CLOSEST_FORWARD
) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
459 (flags
& B_MEDIA_SEEK_CLOSEST_BACKWARD
) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
466 asfReader::FindKeyFrame(void* cookie
, uint32 flags
,
467 int64
* frame
, bigtime_t
* time
)
469 // Find the nearest keyframe to the given time or frame.
471 asf_cookie
*asfCookie
= (asf_cookie
*)cookie
;
472 IndexEntry indexEntry
;
474 if (flags
& B_MEDIA_SEEK_TO_TIME
) {
475 // convert time to frame as we seek by frame
476 // frame = (time * rate) / fps / 1000000LL
477 *frame
= ((*time
* asfCookie
->frames_per_sec_rate
) / (int64
)asfCookie
->frames_per_sec_scale
) / 1000000LL;
480 TRACE("asfReader::FindKeyFrame: seekTo%s%s%s%s, time %Ld, frame %Ld\n",
481 (flags
& B_MEDIA_SEEK_TO_TIME
) ? " B_MEDIA_SEEK_TO_TIME" : "",
482 (flags
& B_MEDIA_SEEK_TO_FRAME
) ? " B_MEDIA_SEEK_TO_FRAME" : "",
483 (flags
& B_MEDIA_SEEK_CLOSEST_FORWARD
) ? " B_MEDIA_SEEK_CLOSEST_FORWARD" : "",
484 (flags
& B_MEDIA_SEEK_CLOSEST_BACKWARD
) ? " B_MEDIA_SEEK_CLOSEST_BACKWARD" : "",
487 if (asfCookie
->audio
== false) {
488 // Only video has keyframes
489 if (flags
& B_MEDIA_SEEK_CLOSEST_FORWARD
|| flags
& B_MEDIA_SEEK_CLOSEST_BACKWARD
) {
490 indexEntry
= theFileReader
->GetIndex(asfCookie
->stream
,*frame
);
492 while (indexEntry
.noPayloads
> 0 && indexEntry
.keyFrame
== false && *frame
> 0) {
493 if (flags
& B_MEDIA_SEEK_CLOSEST_BACKWARD
) {
498 indexEntry
= theFileReader
->GetIndex(asfCookie
->stream
,*frame
);
501 if (indexEntry
.noPayloads
== 0) {
507 *time
= *frame
* 1000000LL * (int64
)asfCookie
->frames_per_sec_scale
/ asfCookie
->frames_per_sec_rate
;
513 asfReader::GetNextChunk(void *_cookie
, const void **chunkBuffer
,
514 size_t *chunkSize
, media_header
*mediaHeader
)
516 asf_cookie
*cookie
= (asf_cookie
*)_cookie
;
520 if (theFileReader
->GetNextChunkInfo(cookie
->stream
, cookie
->frame_pos
, &(cookie
->buffer
), &size
, &keyframe
, &mediaHeader
->start_time
) == false) {
521 TRACE("LAST BUFFER : Stream %d (%ld)\n",cookie
->stream
, cookie
->frame_pos
);
524 return B_LAST_BUFFER_ERROR
;
529 mediaHeader
->type
= B_MEDIA_ENCODED_AUDIO
;
530 mediaHeader
->u
.encoded_audio
.buffer_flags
= keyframe
? B_MEDIA_KEY_FRAME
: 0;
533 mediaHeader
->type
= B_MEDIA_ENCODED_VIDEO
;
534 mediaHeader
->u
.encoded_video
.field_flags
= keyframe
? B_MEDIA_KEY_FRAME
: 0;
535 mediaHeader
->u
.encoded_video
.first_active_line
= 0;
536 mediaHeader
->u
.encoded_video
.line_count
= cookie
->line_count
;
537 mediaHeader
->u
.encoded_video
.field_number
= 0;
538 mediaHeader
->u
.encoded_video
.field_sequence
= cookie
->frame_pos
;
541 TRACE(" stream %d: frame %ld start time %.6f Size %ld key frame %s\n",cookie
->stream
, cookie
->frame_pos
, mediaHeader
->start_time
/ 1000000.0, size
, keyframe
? "true" : "false");
545 *chunkBuffer
= cookie
->buffer
;
553 asfReaderPlugin::NewReader()
555 return new asfReader
;
559 MediaPlugin
*instantiate_plugin()
561 return new asfReaderPlugin
;