/* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ /* This PVA_FF_MovieAtom Class fp the main atom class in the MPEG-4 File that stores all the meta data about the MPEG-4 presentation. */ #define IMPLEMENT_MovieAtom #include "movieatom.h" #include "atomutils.h" #include "a_atomdefs.h" #define MEDIA_TRACK_ID_OFFSET 3 typedef Oscl_Vector PVA_FF_TrackAtomVecType; // Constructor PVA_FF_MovieAtom::PVA_FF_MovieAtom(uint32 fileAuthoringFlags) : PVA_FF_Atom(MOVIE_ATOM) { _success = true; _scalability = -1; _fileType = 0; // Initializations _pAssetInfoCopyRightAtom = NULL; _pAssetInfoAuthorAtom = NULL; _pAssetInfoTitleAtom = NULL; _pAssetInfoDescAtom = NULL; _pAssetInfoPerformerAtom = NULL; _pAssetInfoGenreAtom = NULL; _pAssetInfoRatingAtom = NULL; _pAssetInfoClassificationAtom = NULL; _pAssetInfoKeyWordsAtom = NULL; _pAssetInfoLocationInfoAtom = NULL; _pAssetInfoKeyAlbumAtom = NULL; _pAssetInfoKeyRecordingYearAtom = NULL; _oMovieFragmentEnabled = false; //Movie Fragment : Enable movie fragment mode and create movie extends atom if ((fileAuthoringFlags & PVMP4FF_MOVIE_FRAGMENT_MODE) == (PVMP4FF_MOVIE_FRAGMENT_MODE)) { _oMovieFragmentEnabled = true; PV_MP4_FF_NEW(fp->auditCB, PVA_FF_MovieExtendsAtom, (), _pMovieExtendsAtom); } PV_MP4_FF_NEW(fp->auditCB, PVA_FF_MovieHeaderAtom, ((uint8)0, (uint32)0, fileAuthoringFlags), _pmovieHeaderAtom); PV_MP4_FF_NEW(fp->auditCB, PVA_FF_TrackAtomVecType, (), _pMediaTrackVec); PV_MP4_FF_NEW(fp->auditCB, PVA_FF_TrackAtomVecType, (), _pmpeg4TrackVec); // Create user data atom PV_MP4_FF_NEW(fp->auditCB, PVA_FF_UserDataAtom, (), _puserDataAtom); _puserDataAtom->setParent(this); setTimeScale(DEFAULT_PRESENTATION_TIMESCALE); // Set default value for timescale recomputeSize(); _pmovieHeaderAtom->setParent(this); } // Destructor - delete the vector(s) of TrackAtoms from the heap PVA_FF_MovieAtom::~PVA_FF_MovieAtom() { uint32 i; PV_MP4_FF_DELETE(NULL, PVA_FF_MovieHeaderAtom, _pmovieHeaderAtom); // Delete mpeg4 general tracks for (i = 0; i < _pmpeg4TrackVec->size(); i++) { PV_MP4_FF_DELETE(NULL, PVA_FF_TrackAtom, (*_pmpeg4TrackVec)[i]); } PV_MP4_FF_TEMPLATED_DELETE(NULL, PVA_FF_TrackAtomVecType, Oscl_Vector, _pmpeg4TrackVec); // Delete media tracks for (i = 0; i < _pMediaTrackVec->size(); i++) { PV_MP4_FF_DELETE(NULL, PVA_FF_TrackAtom, (*_pMediaTrackVec)[i]); } PV_MP4_FF_TEMPLATED_DELETE(NULL, PVA_FF_TrackAtomVecType, Oscl_Vector, _pMediaTrackVec); if (_puserDataAtom != NULL) { PV_MP4_FF_DELETE(NULL, PVA_FF_UserDataAtom, _puserDataAtom); } //Movie Fragment : delete mvex atom if (_oMovieFragmentEnabled == true) { PV_MP4_FF_DELETE(NULL, PVA_FF_MovieExtendsAtom, _pMovieExtendsAtom); } } // Add a new PVA_FF_TrackAtom to this container void PVA_FF_MovieAtom::addTrackAtom(PVA_FF_TrackAtom *a) { // Set presentation timescale a->setTimeScale(getTimeScale()); switch (a->getMediaType()) { case MEDIA_TYPE_TEXT://added for timed text track { _fileType |= FILE_TYPE_TIMED_TEXT; _pMediaTrackVec->push_back(a); a->setParent(this); } break; case MEDIA_TYPE_AUDIO: { _fileType |= FILE_TYPE_AUDIO; _pMediaTrackVec->push_back(a); a->setParent(this); // add trex atom in movie fragment mode if (_oMovieFragmentEnabled == true) { PVA_FF_TrackExtendsAtom *pTrexAtom; PV_MP4_FF_NEW(fp->auditCB, PVA_FF_TrackExtendsAtom, (a->getMediaType(), a->getCodecType(), a->getTrackID()), pTrexAtom); _pMovieExtendsAtom->addTrexAtom(pTrexAtom); } } break; case MEDIA_TYPE_VISUAL: { _fileType |= FILE_TYPE_VIDEO; _scalability += 1; _pMediaTrackVec->push_back(a); a->setParent(this); // add trex atom in movie fragment mode if (_oMovieFragmentEnabled == true) { PVA_FF_TrackExtendsAtom *pTrexAtom; PV_MP4_FF_NEW(fp->auditCB, PVA_FF_TrackExtendsAtom, (a->getMediaType(), a->getCodecType(), a->getTrackID()), pTrexAtom); _pMovieExtendsAtom->addTrexAtom(pTrexAtom); } } break; default: PV_MP4_FF_DELETE(NULL, PVA_FF_TrackAtom, a); break; } recomputeSize(); } void PVA_FF_MovieAtom::setTargetBitrate(uint32 trackID, uint32 avgBitRate, uint32 maxBitRate, uint32 bufferSizeDB) { PVA_FF_TrackAtom *track = getMediaTrack(trackID); if (track != NULL) { track->setTargetBitrate(avgBitRate, maxBitRate, bufferSizeDB); } return; } void PVA_FF_MovieAtom::setTimeScale(uint32 trackID, uint32 rate) { PVA_FF_TrackAtom *track = getMediaTrack(trackID); if (track != NULL) { track->setMediaTimeScale(rate); } return; } PVA_FF_TrackAtom* PVA_FF_MovieAtom::getMpeg4Track(int32 index) { if (index < (int32)_pmpeg4TrackVec->size()) { return (*_pmpeg4TrackVec)[index]; } else { return NULL; } } // timescale for ENTIRE presentation void PVA_FF_MovieAtom::setTimeScale(uint32 ts) { // Set timescale in movieheader atom _pmovieHeaderAtom->setTimeScale(ts); } // Get timescale from movieheader atom for ENTIRE presentation uint32 PVA_FF_MovieAtom::getTimeScale() const { return _pmovieHeaderAtom->getTimeScale(); } // Recomputes the size of the current atom by checking all contained atoms void PVA_FF_MovieAtom::recomputeSize() { int32 size = getDefaultSize(); // From base class size += _pmovieHeaderAtom->getSize(); if (_puserDataAtom != NULL) { if (_puserDataAtom->getUserDataAtomVecSize() > 0) { size += _puserDataAtom->getSize(); } } if (_pMediaTrackVec != NULL) { for (uint32 i = 0; i < _pMediaTrackVec->size(); i++) { if ((*_pMediaTrackVec)[i]->getSampleCount() > 0) { size += (*_pMediaTrackVec)[i]->getSize(); } } } // in movie fragment mode add size of mvex if (_oMovieFragmentEnabled == true) { size += _pMovieExtendsAtom->getSize(); } _size = size; // Update the size of the parent atom since this child atom may have changed if (_pparent != NULL) { _pparent->recomputeSize(); } } void PVA_FF_MovieAtom::prepareToRender() { uint32 maxTrackDuration = 0; uint32 creationTime = _pmovieHeaderAtom->getCreationTime(); uint32 modTime = _pmovieHeaderAtom->getModificationTime(); if (_pMediaTrackVec != NULL) { for (uint32 i = 0; i < _pMediaTrackVec->size(); i++) { PVA_FF_TrackAtom *track = (*_pMediaTrackVec)[i]; PVA_FF_TrackHeaderAtom *tkhdPtr = track->getTrackHeaderAtomPtr(); if (tkhdPtr != NULL) { tkhdPtr->setCreationTime(creationTime); tkhdPtr->setModificationTime(modTime); } track->prepareToRender(); uint32 TrackDuration = track->getDuration(); if (TrackDuration > maxTrackDuration) { maxTrackDuration = TrackDuration; } } if (maxTrackDuration > 0) { _pmovieHeaderAtom->setDuration(maxTrackDuration); } } recomputeSize(); return; } // Render this atom to a file stream bool PVA_FF_MovieAtom::renderToFileStream(MP4_AUTHOR_FF_FILE_IO_WRAP *fp) { int32 rendered = 0; // Keep track of number of bytes rendered // Render PVA_FF_Atom type and size if (!renderAtomBaseMembers(fp)) { return false; } rendered += getDefaultSize(); // Render rest of atoms in container // Render the movie header if (!_pmovieHeaderAtom->renderToFileStream(fp)) { return false; } rendered += _pmovieHeaderAtom->getSize(); { if (_puserDataAtom != NULL) { if (_puserDataAtom->getUserDataAtomVecSize() > 0) { if (!_puserDataAtom->renderToFileStream(fp)) { return false; } } } } // Render the object descriptor if (_pMediaTrackVec != NULL) { for (uint32 i = 0; i < _pMediaTrackVec->size(); i++) { if ((*_pMediaTrackVec)[i]->getSampleCount() > 0) { if (!((*_pMediaTrackVec)[i]->renderToFileStream(fp))) { return false; } rendered += (*_pMediaTrackVec)[i]->getSize(); } } } // render MVEX atom in movie fragment mode if (_oMovieFragmentEnabled == true) { if (_pMovieExtendsAtom->getDuration() == 0) { _pMovieExtendsAtom->setMovieFragmentDuration(getDuration()); } if (!_pMovieExtendsAtom->renderToFileStream(fp)) { return false; } rendered += _pMovieExtendsAtom->getSize(); } return true; } void PVA_FF_MovieAtom::addDecoderSpecificInfo(PVA_FF_DecoderSpecificInfo *pinfo, int32 trackID) { PVA_FF_TrackAtom *mediaTrack = getMediaTrack(trackID); mediaTrack->addDecoderSpecificInfo(pinfo); } //added for timed text void PVA_FF_MovieAtom::addTextDecoderSpecificInfo(PVA_FF_TextSampleDescInfo *pinfo, int32 trackID) { PVA_FF_TrackAtom *mediaTrack = getMediaTrack(trackID); mediaTrack->addTextDecoderSpecificInfo(pinfo); } int32 PVA_FF_MovieAtom::getCodecType(uint32 trackID) { PVA_FF_TrackAtom *track = getMediaTrack(trackID); if (track != NULL) { return (track->getCodecType()); } else { return (-1); } } PVA_FF_TrackAtom* PVA_FF_MovieAtom::getMediaTrack(uint32 trackID) { for (uint32 i = 0; i < _pMediaTrackVec->size(); i++) { PVA_FF_TrackHeaderAtom *ptkhdr = (*_pMediaTrackVec)[i]->getTrackHeaderAtomPtr(); if (ptkhdr->getTrackID() == trackID) { return (*_pMediaTrackVec)[i]; } } return NULL; } void PVA_FF_MovieAtom::setMaxBufferSizeDB(uint32 trackID, uint32 max) { PVA_FF_TrackAtom *trackAtom = getMediaTrack(trackID); if (trackAtom != NULL) { trackAtom->setMaxBufferSizeDB(max); } } void PVA_FF_MovieAtom::addSampleToTrack(uint32 trackID, uint8 *psample, uint32 size, uint32 ts, uint8 flags, uint32 baseOffset, bool oChunkStart) { PVA_FF_TrackAtom *mediaTrack; uint32 mediaType = 0; mediaTrack = getMediaTrack(trackID); if (mediaTrack != NULL) { mediaType = mediaTrack->getMediaType(); uint32 mediaTimeScale = mediaTrack->getMediaTimeScale(); if (mediaTimeScale != 0) { uint32 ts_in_milliseconds = (uint32)((ts * 1000.0f) / mediaTimeScale); // Add sample to movie header so can update its _duration _pmovieHeaderAtom->addSample(ts_in_milliseconds); } } if (mediaType == MEDIA_TYPE_VISUAL) { // Updates the members with the next sample received mediaTrack->nextSample(MEDIA_TYPE_VISUAL, psample, size, ts, flags, baseOffset, oChunkStart); } if (mediaType == MEDIA_TYPE_AUDIO) { // Updates the members with the next sample received mediaTrack->nextSample(MEDIA_TYPE_AUDIO, psample, size, ts, flags, baseOffset, oChunkStart); } if (mediaType == MEDIA_TYPE_TEXT) { // Updates the members with the next sample received mediaTrack->nextSample(MEDIA_TYPE_TEXT, psample, size, ts, flags, baseOffset, oChunkStart); } } void PVA_FF_MovieAtom::addTextSampleToTrack(uint32 trackID, uint8 *psample, uint32 size, uint32 ts, uint8 flags, int32 index, uint32 baseOffset, bool oChunkStart) { PVA_FF_TrackAtom *mediaTrack; uint32 mediaType = 0; mediaTrack = getMediaTrack(trackID); if (mediaTrack != NULL) { mediaType = mediaTrack->getMediaType(); uint32 mediaTimeScale = mediaTrack->getMediaTimeScale(); if (mediaTimeScale != 0) { uint32 ts_in_milliseconds = (uint32)((ts * 1000.0f) / mediaTimeScale); // Add sample to movie header so can update its _duration _pmovieHeaderAtom->addSample(ts_in_milliseconds); } } if (mediaType == MEDIA_TYPE_TEXT) { // Updates the members with the next sample received mediaTrack->nextTextSample(MEDIA_TYPE_TEXT, psample, size, ts, flags, index, baseOffset, oChunkStart); } } void PVA_FF_MovieAtom::addSampleToTrack(uint32 trackID, Oscl_Vector & fragmentList, uint32 size, uint32 ts, uint8 flags, uint32 baseOffset, bool oChunkStart) { PVA_FF_TrackAtom *mediaTrack; uint32 mediaType = 0; mediaTrack = getMediaTrack(trackID); if (mediaTrack != NULL) { mediaType = mediaTrack->getMediaType(); uint32 mediaTimeScale = mediaTrack->getMediaTimeScale(); if (mediaTimeScale != 0) { uint32 ts_in_milliseconds = (uint32)((ts * 1000.0f) / mediaTimeScale); // Add sample to movie header so can update its _duration _pmovieHeaderAtom->addSample(ts_in_milliseconds); } } if (mediaType == MEDIA_TYPE_VISUAL) { // Updates the members with the next sample received mediaTrack->nextSample(MEDIA_TYPE_VISUAL, fragmentList, size, ts, flags, baseOffset, oChunkStart); } if (mediaType == MEDIA_TYPE_AUDIO) { // Updates the members with the next sample received mediaTrack->nextSample(MEDIA_TYPE_AUDIO, fragmentList, size, ts, flags, baseOffset, oChunkStart); } if (mediaType == MEDIA_TYPE_TEXT)//added for the support of timed text track { // Updates the members with the next sample received mediaTrack->nextSample(MEDIA_TYPE_AUDIO, fragmentList, size, ts, flags, baseOffset, oChunkStart); } } void PVA_FF_MovieAtom::addTextSampleToTrack(uint32 trackID, Oscl_Vector & fragmentList, uint32 size, uint32 ts, uint8 flags, int32 index, uint32 baseOffset, bool oChunkStart) { PVA_FF_TrackAtom *mediaTrack; uint32 mediaType = 0; mediaTrack = getMediaTrack(trackID); if (mediaTrack != NULL) { mediaType = mediaTrack->getMediaType(); uint32 mediaTimeScale = mediaTrack->getMediaTimeScale(); if (mediaTimeScale != 0) { uint32 ts_in_milliseconds = (uint32)((ts * 1000.0f) / mediaTimeScale); // Add sample to movie header so can update its _duration _pmovieHeaderAtom->addSample(ts_in_milliseconds); } } if (mediaType == MEDIA_TYPE_TEXT)//added for the support of timed text track { // Updates the members with the next sample received mediaTrack->nextTextSample(MEDIA_TYPE_TEXT, fragmentList, size, ts, flags, index, baseOffset, oChunkStart); } } bool PVA_FF_MovieAtom::reAuthorFirstSampleInTrack(uint32 trackID, uint32 size, uint32 baseOffset) { PVA_FF_TrackAtom *mediaTrack; mediaTrack = getMediaTrack(trackID); return ( mediaTrack->reAuthorFirstSample(size, baseOffset)); } void PVA_FF_MovieAtom::populateCommonMetadataAtoms() { if (_pAssetInfoCopyRightAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoCopyRightAtom, (), _pAssetInfoCopyRightAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoCopyRightAtom); } } if (_pAssetInfoAuthorAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoAuthorAtom, (), _pAssetInfoAuthorAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoAuthorAtom); } } if (_pAssetInfoTitleAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoTitleAtom, (), _pAssetInfoTitleAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoTitleAtom); } } if (_pAssetInfoDescAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoDescAtom, (), _pAssetInfoDescAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoDescAtom); } } } void PVA_FF_MovieAtom::createAssetInfoAtoms() { populateCommonMetadataAtoms(); if (_pAssetInfoPerformerAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoPerformerAtom, (), _pAssetInfoPerformerAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoPerformerAtom); } } if (_pAssetInfoGenreAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoGenreAtom, (), _pAssetInfoGenreAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoGenreAtom); } } if (_pAssetInfoRatingAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoRatingAtom, (), _pAssetInfoRatingAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoRatingAtom); } } if (_pAssetInfoClassificationAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoClassificationAtom, (), _pAssetInfoClassificationAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoClassificationAtom); } } if (_pAssetInfoKeyWordsAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoKeyWordsAtom, (), _pAssetInfoKeyWordsAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoKeyWordsAtom); } } if (_pAssetInfoLocationInfoAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoLocationInfoAtom, (), _pAssetInfoLocationInfoAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoLocationInfoAtom); } } if (_pAssetInfoKeyAlbumAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoAlbumAtom, (), _pAssetInfoKeyAlbumAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoKeyAlbumAtom); } } if (_pAssetInfoKeyRecordingYearAtom == NULL) { PV_MP4_FF_NEW(fp->auditCB, PVA_FF_AssetInfoRecordingYearAtom, (), _pAssetInfoKeyRecordingYearAtom); if (_puserDataAtom != NULL) { _puserDataAtom->addAtom(_pAssetInfoKeyRecordingYearAtom); } } } // functions to set and update fragment duration in MVEX atom void PVA_FF_MovieAtom::setMovieFragmentDuration() { _pMovieExtendsAtom->setMovieFragmentDuration(getDuration()); } void PVA_FF_MovieAtom::updateMovieFragmentDuration(uint32 trackID, uint32 ts) { PVA_FF_TrackAtom *mediaTrack; mediaTrack = getMediaTrack(trackID); uint32 mediaTimeScale = mediaTrack->getMediaTimeScale(); if (mediaTimeScale != 0) { uint32 ts_moviescale = (uint32)(ts * ((float)getTimeScale() / (float)mediaTimeScale)); _pMovieExtendsAtom->updateMovieFragmentDuration(ts_moviescale); } } void PVA_FF_MovieAtom::writeMovieFragmentDuration(MP4_AUTHOR_FF_FILE_IO_WRAP* fp) { _pMovieExtendsAtom->writeMovieFragmentDuration(fp); } void PVA_FF_MovieAtom::SetMaxSampleSize(uint32 aTrackID, uint32 aSize) { PVA_FF_TrackAtom *mediaTrack = NULL; mediaTrack = getMediaTrack(aTrackID); if (NULL != mediaTrack) mediaTrack->SetMaxSampleSize(aSize); } void PVA_FF_MovieAtom::writeMaxSampleSize(MP4_AUTHOR_FF_FILE_IO_WRAP *_afp) { for (uint32 i = 0; i < _pMediaTrackVec->size(); i++) { PVA_FF_TrackHeaderAtom *ptkhdr = NULL; ptkhdr = (*_pMediaTrackVec)[i]->getTrackHeaderAtomPtr(); if (NULL != ptkhdr) { uint32 trackID = ptkhdr->getTrackID(); PVA_FF_TrackAtom *mediaTrack = NULL; mediaTrack = getMediaTrack(trackID); if (NULL != mediaTrack) mediaTrack->writeMaxSampleSize(_afp); } } }