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