/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef ISOCOMPOSITOR_H_ #define ISOCOMPOSITOR_H_ #include "mozilla/EndianUtils.h" #include "nsTArray.h" #include "ISOTrackMetadata.h" #include "EncodedFrameContainer.h" namespace mozilla { class Box; class ISOControl; /** * This class collects elementary stream data to form a fragment. * ISOMediaWriter will check if the data is enough; if yes, the corresponding * moof will be created and write to ISOControl. * Each audio and video has its own fragment and only one during the whole * life cycle, when a fragment is formed in ISOControl, Flush() needs to * be called to reset it. */ class FragmentBuffer { public: // aTrackType: it could be Audio_Track or Video_Track. // aFragDuration: it is the fragment duration. (microsecond per unit) // Audio and video have the same fragment duration. FragmentBuffer(uint32_t aTrackType, uint32_t aFragDuration); ~FragmentBuffer(); // Get samples of first fragment, that will swap all the elements in the // mFragArray[0] when aFlush = true, and caller is responsible for drop // EncodedFrame reference count. nsresult GetFirstFragment(nsTArray>& aFragment, bool aFlush = false); // Add sample frame to the last element fragment of mFragArray. If sample // number is enough, it will append a new fragment element. And the new // sample will be added to the new fragment element of mFragArray. nsresult AddFrame(EncodedFrame* aFrame); // Get total sample size of first complete fragment size. uint32_t GetFirstFragmentSampleSize(); // Get sample number of first complete fragment. uint32_t GetFirstFragmentSampleNumber(); // Check if it accumulates enough frame data. // It returns true when data is enough to form a fragment. bool HasEnoughData(); // Called by ISOMediaWriter when TrackEncoder has sent the last frame. The // remains frame data will form the last moof and move the state machine to // in ISOMediaWriter to last phrase. nsresult SetEndOfStream() { mEOS = true; return NS_OK; } bool EOS() { return mEOS; } // CSD (codec specific data), it is generated by encoder and the data depends // on codec type. This data will be sent as a special frame from encoder to // ISOMediaWriter and pass to this class via AddFrame(). nsresult GetCSD(nsTArray& aCSD); bool HasCSD() { return mCSDFrame; } uint32_t GetType() { return mTrackType; } void SetLastFragmentLastFrameTime(uint32_t aTime) { mLastFrameTimeOfLastFragment = aTime; } uint32_t GetLastFragmentLastFrameTime() { return mLastFrameTimeOfLastFragment; } private: uint32_t mTrackType; // Fragment duration, microsecond per unit. uint32_t mFragDuration; // Media start time, microsecond per unit. // Together with mFragDuration, mFragmentNumber and EncodedFrame->GetTimeStamp(), // when the difference between current frame time and mMediaStartTime is // exceeded current fragment ceiling timeframe, that means current fragment has // enough data and a new element in mFragArray will be added. uint64_t mMediaStartTime; // Current fragment number. It will be increase when a new element of // mFragArray is created. // Note: // It only means the fragment number of current accumulated frames, not // the current 'creating' fragment mFragNum in ISOControl. uint32_t mFragmentNumber; // The last frame time stamp of last fragment. It is for calculating the // play duration of first frame in current fragment. The frame duration is // defined as "current frame timestamp - last frame timestamp" here. So it // needs to keep the last timestamp of last fragment. uint32_t mLastFrameTimeOfLastFragment; // Array of fragments, each element has enough samples to form a // complete fragment. nsTArray>> mFragArray; // Codec specific data frame, it will be generated by encoder and send to // ISOMediaWriter through WriteEncodedTrack(). The data will be vary depends // on codec type. RefPtr mCSDFrame; // END_OF_STREAM from ContainerWriter bool mEOS; }; /** * ISOControl will be carried to each box when box is created. It is the main * bridge for box to output stream to ContainerWriter and retrieve information. * ISOControl acts 3 different roles: * 1. Holds the pointer of audio metadata, video metadata, fragment and * pass them to boxes. * 2. Provide the functions to generate the base structure of MP4; they are * GenerateFtyp, GenerateMoov, GenerateMoof, and GenerateMfra. * 3. The actually writer used by MuxOperation::Write() in each box. It provides * writing methods for different kind of data; they are Write, WriteArray, * WriteBits...etc. */ class ISOControl { friend class Box; public: ISOControl(uint32_t aMuxingType); ~ISOControl(); nsresult GenerateFtyp(); nsresult GenerateMoov(); nsresult GenerateMoof(uint32_t aTrackType); // Swap elementary stream pointer to output buffers. uint32_t WriteAVData(nsTArray& aArray); uint32_t Write(uint8_t* aBuf, uint32_t aSize); uint32_t Write(uint8_t aData); template uint32_t Write(T aData) { MOZ_ASSERT(!mBitCount); aData = NativeEndian::swapToNetworkOrder(aData); Write((uint8_t*)&aData, sizeof(T)); return sizeof(T); } template uint32_t WriteArray(const T &aArray, uint32_t aSize) { MOZ_ASSERT(!mBitCount); uint32_t size = 0; for (uint32_t i = 0; i < aSize; i++) { size += Write(aArray[i]); } return size; } uint32_t WriteFourCC(const char* aType); // Bit writing. Note: it needs to be byte-boundary before using // others non-bit writing function. uint32_t WriteBits(uint64_t aBits, size_t aNumBits); // This is called by GetContainerData and swap all the buffers to aOutputBuffers. nsresult GetBufs(nsTArray>* aOutputBufs); // Presentation time in seconds since midnight, Jan. 1, 1904, in UTC time. uint32_t GetTime(); // current fragment number uint32_t GetCurFragmentNumber() { return mFragNum; } nsresult SetFragment(FragmentBuffer* aFragment); FragmentBuffer* GetFragment(uint32_t aType); uint32_t GetMuxingType() { return mMuxingType; } nsresult SetMetadata(TrackMetadataBase* aTrackMeta); nsresult GetAudioMetadata(RefPtr& aAudMeta); nsresult GetVideoMetadata(RefPtr& aVidMeta); // Track ID is the Metadata index in mMetaArray. It allows only 1 audio // track and 1 video track in this muxer. In this muxer, it is prohibt to have // mutiple audio track or video track in the same file. uint32_t GetTrackID(TrackMetadataBase::MetadataKind aKind); uint32_t GetNextTrackID(); bool HasAudioTrack(); bool HasVideoTrack(); private: uint32_t GetBufPos(); nsresult FlushBuf(); // One of value in TYPE_XXX, defined in ISOMediaWriter. uint32_t mMuxingType; // Audio and video fragments are owned by ISOMediaWriter. // They don't need to worry about pointer going stale because ISOMediaWriter's // lifetime is longer than ISOControl. FragmentBuffer* mAudioFragmentBuffer; FragmentBuffer* mVideoFragmentBuffer; // Generated fragment number uint32_t mFragNum; // The (index + 1) will be the track ID. nsTArray> mMetaArray; // Array of output buffers. // To save memory usage, audio/video sample will be swapped into a new element // of this array. // // For example, // mOutBuffers[0] --> boxes (allocated by muxer) // mOutBuffers[1] --> video raw data (allocated by encoder) // mOutBuffers[2] --> video raw data (allocated by encoder) // mOutBuffers[3] --> video raw data (allocated by encoder) // mOutBuffers[4] --> boxes (allocated by muxer) // mOutBuffers[5] --> audio raw data (allocated by encoder) // ...etc. // nsTArray> mOutBuffers; // Accumulate output size from Write(). uint64_t mOutputSize; // Bit writing operation. Note: the mBitCount should be 0 before any // byte-boundary writing method be called (Write(uint32_t), Write(uint16_t)...etc); // otherwise, there will be assertion on these functions. uint8_t mBitCount; uint8_t mBit; }; } #endif