3rdparty/licenseReport: Add seperate LGPL checks
[haiku.git] / src / add-ons / media / plugins / mp4_reader / libMP4 / MP4FileReader.cpp
blobc26f5cd8591d3707b986b1d1b2d140db65096840
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 "MP4Parser.h"
28 #include "MP4FileReader.h"
30 #include <DataIO.h>
31 #include <SupportKit.h>
33 #include <iostream>
36 extern AtomBase *GetAtom(BPositionIO *pStream);
39 MP4FileReader::MP4FileReader(BPositionIO *pStream)
41 theStream = pStream;
43 // Find Size of Stream, need to rethink this for non seekable streams
44 theStream->Seek(0,SEEK_END);
45 StreamSize = theStream->Position();
46 theStream->Seek(0,SEEK_SET);
47 TotalChildren = 0;
49 theMVHDAtom = NULL;
53 MP4FileReader::~MP4FileReader()
55 theStream = NULL;
56 theMVHDAtom = NULL;
60 bool
61 MP4FileReader::IsEndOfData(off_t pPosition)
63 AtomBase* aAtomBase;
65 // check all mdat atoms to make sure pPosition is within one of them
66 for (uint32 index=0; index < CountChildAtoms('mdat'); index++) {
67 aAtomBase = GetChildAtom(uint32('mdat'),index);
68 if ((aAtomBase) && (aAtomBase->GetAtomSize() > 8)) {
69 MDATAtom *aMDATAtom = dynamic_cast<MDATAtom *>(aAtomBase);
70 if (pPosition >= aMDATAtom->GetAtomOffset() && pPosition <= aMDATAtom->GetEOF()) {
71 return false;
76 return true;
80 bool
81 MP4FileReader::IsEndOfFile(off_t position)
83 return (position >= StreamSize);
87 bool
88 MP4FileReader::IsEndOfFile()
90 return theStream->Position() >= StreamSize;
94 bool
95 MP4FileReader::AddChild(AtomBase *childAtom)
97 if (childAtom) {
98 atomChildren.push_back(childAtom);
99 TotalChildren++;
100 return true;
102 return false;
106 AtomBase *
107 MP4FileReader::GetChildAtom(uint32 patomType, uint32 offset)
109 for (uint32 i = 0; i < TotalChildren; i++) {
110 if (atomChildren[i]->IsType(patomType)) {
111 // found match, skip if offset non zero.
112 if (offset == 0) {
113 return atomChildren[i];
114 } else {
115 offset--;
117 } else {
118 if (atomChildren[i]->IsContainer()) {
119 // search container
120 AtomBase *aAtomBase = (dynamic_cast<AtomContainer *>(atomChildren[i])->GetChildAtom(patomType, offset));
121 if (aAtomBase) {
122 // found in container
123 return aAtomBase;
125 // not found
129 return NULL;
133 uint32
134 MP4FileReader::CountChildAtoms(uint32 patomType)
136 uint32 count = 0;
138 while (GetChildAtom(patomType, count) != NULL) {
139 count++;
141 return count;
144 TRAKAtom *
145 MP4FileReader::GetTrack(uint32 streamIndex)
147 if (tracks[streamIndex] == NULL) {
148 AtomBase *aAtomBase = GetChildAtom(uint32('trak'), streamIndex);
149 if (aAtomBase) {
150 tracks[streamIndex] = (dynamic_cast<TRAKAtom *>(aAtomBase));
154 if (tracks[streamIndex] != NULL) {
155 return tracks[streamIndex];
158 #ifdef DEBUG
159 char msg[100]; sprintf(msg, "Bad Stream Index %ld\n", streamIndex);
160 DEBUGGER(msg);
161 #endif
163 TRESPASS();
165 return NULL; // Never to happen
169 MVHDAtom*
170 MP4FileReader::GetMVHDAtom()
172 AtomBase *aAtomBase;
174 if (theMVHDAtom == NULL) {
175 aAtomBase = GetChildAtom(uint32('mvhd'));
176 theMVHDAtom = dynamic_cast<MVHDAtom *>(aAtomBase);
179 // Assert(theMVHDAtom != NULL,"Movie has no movie header atom");
180 return theMVHDAtom;
184 uint32
185 MP4FileReader::GetMovieTimeScale()
187 return GetMVHDAtom()->GetTimeScale();
191 bigtime_t
192 MP4FileReader::GetMovieDuration()
194 return bigtime_t((GetMVHDAtom()->GetDuration() * 1000000.0) / GetMovieTimeScale());
198 uint32
199 MP4FileReader::GetStreamCount()
201 // count the number of tracks in the file
202 return CountChildAtoms(uint32('trak'));
206 bigtime_t
207 MP4FileReader::GetVideoDuration(uint32 streamIndex)
209 if (IsVideo(streamIndex)) {
210 return GetTrack(streamIndex)->Duration(1);
213 return 0;
217 bigtime_t
218 MP4FileReader::GetAudioDuration(uint32 streamIndex)
220 if (IsAudio(streamIndex)) {
221 return GetTrack(streamIndex)->Duration(1);
224 return 0;
228 bigtime_t
229 MP4FileReader::GetMaxDuration()
231 int32 videoIndex = -1;
232 int32 audioIndex = -1;
234 // find the active video and audio tracks
235 for (uint32 i = 0; i < GetStreamCount(); i++) {
236 if (GetTrack(i)->IsActive()) {
237 if (GetTrack(i)->IsAudio()) {
238 audioIndex = int32(i);
240 if (GetTrack(i)->IsVideo()) {
241 videoIndex = int32(i);
246 if (videoIndex >= 0 && audioIndex >= 0) {
247 return max_c(GetVideoDuration(videoIndex),
248 GetAudioDuration(audioIndex));
250 if (videoIndex < 0 && audioIndex >= 0) {
251 return GetAudioDuration(audioIndex);
253 if (videoIndex >= 0 && audioIndex < 0) {
254 return GetVideoDuration(videoIndex);
257 return 0;
261 uint32
262 MP4FileReader::GetFrameCount(uint32 streamIndex)
264 return GetTrack(streamIndex)->FrameCount();
267 uint32
268 MP4FileReader::GetAudioChunkCount(uint32 streamIndex)
270 if (IsAudio(streamIndex)) {
271 return GetTrack(streamIndex)->GetTotalChunks();
274 return 0;
277 bool
278 MP4FileReader::IsVideo(uint32 streamIndex)
280 return GetTrack(streamIndex)->IsVideo();
284 bool
285 MP4FileReader::IsAudio(uint32 streamIndex)
287 return GetTrack(streamIndex)->IsAudio();
291 uint32
292 MP4FileReader::GetFirstFrameInChunk(uint32 streamIndex, uint32 pChunkIndex)
294 return GetTrack(streamIndex)->GetFirstSampleInChunk(pChunkIndex);
298 uint32
299 MP4FileReader::GetNoFramesInChunk(uint32 streamIndex, uint32 pChunkIndex)
301 return GetTrack(streamIndex)->GetNoSamplesInChunk(pChunkIndex);
304 uint32
305 MP4FileReader::GetChunkForFrame(uint32 streamIndex, uint32 pFrameNo) {
306 uint32 OffsetInChunk;
307 return GetTrack(streamIndex)->GetChunkForSample(pFrameNo, &OffsetInChunk);
311 uint64
312 MP4FileReader::GetOffsetForFrame(uint32 streamIndex, uint32 pFrameNo)
314 TRAKAtom *aTrakAtom = GetTrack(streamIndex);
316 if (pFrameNo < aTrakAtom->FrameCount()) {
317 // Get time for Frame
318 bigtime_t Time = aTrakAtom->GetTimeForFrame(pFrameNo);
320 // Get Sample for Time
321 uint32 SampleNo = aTrakAtom->GetSampleForTime(Time);
323 // Get Chunk For Sample and the offset for the frame within that chunk
324 uint32 OffsetInChunk;
325 uint32 ChunkIndex = aTrakAtom->GetChunkForSample(SampleNo, &OffsetInChunk);
326 // Get Offset For Chunk
327 uint64 OffsetNo = aTrakAtom->GetOffsetForChunk(ChunkIndex);
329 if (ChunkIndex != 0) {
330 uint32 SizeForSample;
331 // Adjust the Offset for the Offset in the chunk
332 if (aTrakAtom->IsSingleSampleSize()) {
333 SizeForSample = aTrakAtom->GetSizeForSample(SampleNo);
334 OffsetNo = OffsetNo + (OffsetInChunk * SizeForSample);
335 } else {
336 // This is bad news performance wise
337 for (uint32 i=1;i<=OffsetInChunk;i++) {
338 SizeForSample = aTrakAtom->GetSizeForSample(SampleNo-i);
339 OffsetNo = OffsetNo + SizeForSample;
344 // printf("frame %ld, time %Ld, sample %ld, Chunk %ld, OffsetInChunk %ld, Offset %Ld\n",pFrameNo, Time, SampleNo, ChunkNo, OffsetInChunk, OffsetNo);
346 return OffsetNo;
349 return 0;
352 uint64
353 MP4FileReader::GetOffsetForChunk(uint32 streamIndex, uint32 pChunkIndex)
355 return GetTrack(streamIndex)->GetOffsetForChunk(pChunkIndex);
359 status_t
360 MP4FileReader::ParseFile()
362 AtomBase *aChild;
363 while (IsEndOfFile() == false) {
364 aChild = GetAtom(theStream);
365 if (AddChild(aChild)) {
366 aChild->ProcessMetaData();
370 #ifdef DEBUG
371 // Debug info
372 for (uint32 i=0;i<TotalChildren;i++) {
373 atomChildren[i]->DisplayAtoms();
375 #endif
377 return B_OK;
381 const mp4_main_header*
382 MP4FileReader::MovMainHeader()
384 // Fill In theMainHeader
385 // uint32 micro_sec_per_frame;
386 // uint32 max_bytes_per_sec;
387 // uint32 padding_granularity;
388 // uint32 flags;
389 // uint32 total_frames;
390 // uint32 initial_frames;
391 // uint32 streams;
392 // uint32 suggested_buffer_size;
393 // uint32 width;
394 // uint32 height;
396 uint32 videoStream = 0;
398 theMainHeader.streams = GetStreamCount();
399 theMainHeader.flags = 0;
400 theMainHeader.initial_frames = 0;
402 while (videoStream < theMainHeader.streams) {
403 if (IsVideo(videoStream) && IsActive(videoStream))
404 break;
406 videoStream++;
409 if (videoStream >= theMainHeader.streams) {
410 theMainHeader.width = 0;
411 theMainHeader.height = 0;
412 theMainHeader.total_frames = 0;
413 theMainHeader.suggested_buffer_size = 0;
414 theMainHeader.micro_sec_per_frame = 0;
415 } else {
416 theMainHeader.width = VideoFormat(videoStream)->width;
417 theMainHeader.height = VideoFormat(videoStream)->height;
418 theMainHeader.total_frames = GetFrameCount(videoStream);
419 theMainHeader.suggested_buffer_size = theMainHeader.width * theMainHeader.height * VideoFormat(videoStream)->bit_count / 8;
420 theMainHeader.micro_sec_per_frame = uint32(1000000.0 / VideoFormat(videoStream)->FrameRate);
423 theMainHeader.padding_granularity = 0;
424 theMainHeader.max_bytes_per_sec = 0;
426 return &theMainHeader;
430 const AudioMetaData *
431 MP4FileReader::AudioFormat(uint32 streamIndex, size_t *size)
433 if (IsAudio(streamIndex)) {
434 AtomBase *aAtomBase = GetChildAtom(uint32('trak'),streamIndex);
436 if (aAtomBase) {
437 TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
439 aAtomBase = aTrakAtom->GetChildAtom(uint32('stsd'),0);
440 if (aAtomBase) {
441 STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase);
443 // Fill in the AudioMetaData structure
444 AudioDescription aAudioDescription = aSTSDAtom->GetAsAudio();
446 theAudio.compression = aAudioDescription.codecid;
447 theAudio.codecSubType = aAudioDescription.codecSubType;
449 theAudio.NoOfChannels = aAudioDescription.theAudioSampleEntry.ChannelCount;
451 // Fix for broken mp4's with 0 SampleSize, default to 16 bits
452 if (aAudioDescription.theAudioSampleEntry.SampleSize == 0) {
453 theAudio.SampleSize = 16;
454 } else {
455 theAudio.SampleSize = aAudioDescription.theAudioSampleEntry.SampleSize;
458 theAudio.SampleRate = aAudioDescription.theAudioSampleEntry.SampleRate;
459 theAudio.FrameSize = aAudioDescription.FrameSize;
460 if (aAudioDescription.BufferSize == 0) {
461 theAudio.BufferSize = uint32((theAudio.SampleSize * theAudio.NoOfChannels * theAudio.FrameSize) / 8);
462 } else {
463 theAudio.BufferSize = aAudioDescription.BufferSize;
466 theAudio.BitRate = aAudioDescription.BitRate;
468 theAudio.theDecoderConfig = aAudioDescription.theDecoderConfig;
469 theAudio.DecoderConfigSize = aAudioDescription.DecoderConfigSize;
471 return &theAudio;
476 return NULL;
479 const VideoMetaData*
480 MP4FileReader::VideoFormat(uint32 streamIndex)
482 if (IsVideo(streamIndex)) {
483 AtomBase *aAtomBase = GetChildAtom(uint32('trak'),streamIndex);
485 if (aAtomBase) {
486 TRAKAtom *aTrakAtom = dynamic_cast<TRAKAtom *>(aAtomBase);
488 aAtomBase = aTrakAtom->GetChildAtom(uint32('stsd'),0);
489 if (aAtomBase) {
490 STSDAtom *aSTSDAtom = dynamic_cast<STSDAtom *>(aAtomBase);
491 VideoDescription aVideoDescription = aSTSDAtom->GetAsVideo();
493 theVideo.compression = aVideoDescription.codecid;
494 theVideo.codecSubType = aVideoDescription.codecSubType;
496 theVideo.width = aVideoDescription.theVideoSampleEntry.Width;
497 theVideo.height = aVideoDescription.theVideoSampleEntry.Height;
498 theVideo.planes = aVideoDescription.theVideoSampleEntry.Depth;
499 theVideo.BufferSize = aVideoDescription.theVideoSampleEntry.Width * aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Depth / 8;
500 theVideo.bit_count = aVideoDescription.theVideoSampleEntry.Depth;
501 theVideo.image_size = aVideoDescription.theVideoSampleEntry.Height * aVideoDescription.theVideoSampleEntry.Width;
502 theVideo.HorizontalResolution = aVideoDescription.theVideoSampleEntry.HorizontalResolution;
503 theVideo.VerticalResolution = aVideoDescription.theVideoSampleEntry.VerticalResolution;
504 theVideo.FrameCount = aVideoDescription.theVideoSampleEntry.FrameCount;
506 theVideo.theDecoderConfig = aVideoDescription.theDecoderConfig;
507 theVideo.DecoderConfigSize = aVideoDescription.DecoderConfigSize;
509 aAtomBase = aTrakAtom->GetChildAtom(uint32('stts'),0);
510 if (aAtomBase) {
511 STTSAtom *aSTTSAtom = dynamic_cast<STTSAtom *>(aAtomBase);
513 theVideo.FrameRate = ((aSTTSAtom->GetSUMCounts() * 1000000.0) / aTrakAtom->Duration(1));
515 return &theVideo;
521 return NULL;
525 const mp4_stream_header*
526 MP4FileReader::StreamFormat(uint32 streamIndex)
528 if (IsActive(streamIndex) == false) {
529 return NULL;
532 // Fill In a Stream Header
533 theStreamHeader.length = 0;
535 if (IsVideo(streamIndex)) {
536 theStreamHeader.rate = uint32(1000000.0*VideoFormat(streamIndex)->FrameRate);
537 theStreamHeader.scale = 1000000L;
538 theStreamHeader.length = GetFrameCount(streamIndex);
541 if (IsAudio(streamIndex)) {
542 theStreamHeader.rate = uint32(AudioFormat(streamIndex)->SampleRate);
543 theStreamHeader.scale = 1;
544 theStreamHeader.length = GetFrameCount(streamIndex);
545 theStreamHeader.sample_size = AudioFormat(streamIndex)->SampleSize;
546 theStreamHeader.suggested_buffer_size = AudioFormat(streamIndex)->BufferSize;
549 return &theStreamHeader;
553 uint32
554 MP4FileReader::GetFrameSize(uint32 streamIndex, uint32 pFrameNo)
556 if (pFrameNo < GetTrack(streamIndex)->FrameCount()) {
557 uint32 SampleNo = GetTrack(streamIndex)->GetSampleForFrame(pFrameNo);
558 return GetTrack(streamIndex)->GetSizeForSample(SampleNo);
561 return 0;
564 bool
565 MP4FileReader::isValidFrame(uint32 streamIndex, uint32 pFrameNo) {
566 return (pFrameNo < GetTrack(streamIndex)->FrameCount());
569 bool
570 MP4FileReader::isValidChunkIndex(uint32 streamIndex, uint32 pChunkIndex) {
571 return (pChunkIndex > 0 && pChunkIndex <= GetTrack(streamIndex)->ChunkCount());
575 uint32
576 MP4FileReader::GetChunkSize(uint32 streamIndex, uint32 pChunkIndex)
578 return GetTrack(streamIndex)->GetChunkSize(pChunkIndex);
582 bool
583 MP4FileReader::IsKeyFrame(uint32 streamIndex, uint32 pFrameNo)
585 if (IsAudio(streamIndex)) {
586 return true;
589 return GetTrack(streamIndex)->IsSyncSample(pFrameNo);
592 bigtime_t
593 MP4FileReader::GetTimeForFrame(uint32 streamIndex, uint32 pFrameNo) {
594 // Calculate PTS
595 return GetTrack(streamIndex)->GetTimeForFrame(pFrameNo);
599 uint32
600 MP4FileReader::GetFrameForTime(uint32 streamIndex, bigtime_t time) {
601 return GetTrack(streamIndex)->GetFrameForTime(time);
605 uint32
606 MP4FileReader::GetFrameForSample(uint32 streamIndex, uint32 sample) {
607 if (IsVideo(streamIndex)) {
608 return sample; // frame = sample for video
611 if (IsAudio(streamIndex)) {
612 bigtime_t time = bigtime_t((sample * 1000000.0) / GetTrack(streamIndex)->GetSampleRate());
613 return GetTrack(streamIndex)->GetFrameForTime(time);
616 return 0;
620 uint32
621 MP4FileReader::GetSampleForTime(uint32 streamIndex, bigtime_t time) {
622 return GetTrack(streamIndex)->GetSampleForTime(time);
626 uint32
627 MP4FileReader::GetTimeForSample(uint32 streamIndex, uint32 sample) {
628 return GetTimeForFrame(streamIndex, GetFrameForSample(streamIndex, sample));
632 bool
633 MP4FileReader::GetBufferForChunk(uint32 streamIndex, uint32 pChunkIndex, off_t *start, uint32 *size, bool *keyframe, bigtime_t *time, uint32 *framesInBuffer) {
635 if (isValidChunkIndex(streamIndex, pChunkIndex) == false) {
636 *start = 0;
637 *size = 0;
638 *keyframe = false;
639 *time = 0;
640 *framesInBuffer = 0;
641 return false;
644 *start = GetOffsetForChunk(streamIndex, pChunkIndex);
645 *size = GetChunkSize(streamIndex, pChunkIndex);
647 uint32 frameNo = GetFirstFrameInChunk(streamIndex, pChunkIndex);
649 if ((*start > 0) && (*size > 0)) {
650 *keyframe = IsKeyFrame(streamIndex, frameNo);
653 *framesInBuffer = GetNoFramesInChunk(streamIndex, pChunkIndex);
654 *time = GetTimeForFrame(streamIndex, frameNo);
656 if (IsEndOfFile(*start + *size) || IsEndOfData(*start + *size)) {
657 return false;
660 return *start > 0 && *size > 0;
664 bool
665 MP4FileReader::GetBufferForFrame(uint32 streamIndex, uint32 pFrameNo, off_t *start, uint32 *size, bool *keyframe, bigtime_t *time, uint32 *framesInBuffer) {
666 // Get the offset for a given frame and the size of the remaining bytes in the chunk
667 // framesInBuffer is how many frames there are in the buffer.
669 if (isValidFrame(streamIndex, pFrameNo) == false) {
670 *start = 0;
671 *size = 0;
672 *keyframe = false;
673 *time = 0;
674 *framesInBuffer = 0;
675 return false;
678 *start = GetOffsetForFrame(streamIndex, pFrameNo);
679 *size = GetFrameSize(streamIndex, pFrameNo);
681 if ((*start > 0) && (*size > 0)) {
682 *keyframe = IsKeyFrame(streamIndex, pFrameNo);
685 *framesInBuffer = 1;
686 *time = GetTimeForFrame(streamIndex, pFrameNo);
688 if (IsEndOfFile(*start + *size) || IsEndOfData(*start + *size)) {
689 return false;
692 return *start > 0 && *size > 0;
696 bool
697 MP4FileReader::IsActive(uint32 streamIndex)
699 // Don't use helper method streamIndex is likely invalid
700 AtomBase *aAtomBase = GetChildAtom(uint32('trak'), streamIndex);
701 if (aAtomBase) {
702 return dynamic_cast<TRAKAtom *>(aAtomBase)->IsActive();
705 return false;
709 /* static */
710 bool
711 MP4FileReader::IsSupported(BPositionIO *source)
713 AtomBase *aAtom = GetAtom(source);
714 if (aAtom) {
715 if (dynamic_cast<FTYPAtom *>(aAtom)) {
716 aAtom->ProcessMetaData();
717 printf("ftyp atom found checking brands...");
718 // MP4 files start with a ftyp atom that does not contain a qt brand
719 if (!dynamic_cast<FTYPAtom *>(aAtom)->HasBrand(uint32('qt '))) {
720 printf("no quicktime brand found must be mp4\n");
721 return true;
722 } else {
723 printf("quicktime brand found\n");
728 printf("NO ftyp atom found, cannot be mp4\n");
730 return false;