/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. (See .) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********/ // "liveMedia" // Copyright (c) 1996-2015 Live Networks, Inc. All rights reserved. // A class that encapsulates a Matroska file. // C++ header #ifndef _MATROSKA_FILE_HH #define _MATROSKA_FILE_HH #ifndef _RTP_SINK_HH #include "RTPSink.hh" #endif #ifndef _HASH_TABLE_HH #include "HashTable.hh" #endif class MatroskaTrack; // forward class MatroskaDemux; // forward class MatroskaFile: public Medium { public: typedef void (onCreationFunc)(MatroskaFile* newFile, void* clientData); static void createNew(UsageEnvironment& env, char const* fileName, onCreationFunc* onCreation, void* onCreationClientData, char const* preferredLanguage = "eng"); // Note: Unlike most "createNew()" functions, this one doesn't return a new object immediately. Instead, because this class // requires file reading (to parse the Matroska 'Track' headers) before a new object can be initialized, the creation of a new // object is signalled by calling - from the event loop - an 'onCreationFunc' that is passed as a parameter to "createNew()". MatroskaTrack* lookup(unsigned trackNumber) const; // Create a demultiplexor for extracting tracks from this file. (Separate clients will typically have separate demultiplexors.) MatroskaDemux* newDemux(); // Parameters of the file ('Segment'); set when the file is parsed: unsigned timecodeScale() { return fTimecodeScale; } // in nanoseconds float segmentDuration() { return fSegmentDuration; } // in units of "timecodeScale()" float fileDuration(); // in seconds char const* fileName() const { return fFileName; } unsigned chosenVideoTrackNumber() { return fChosenVideoTrackNumber; } unsigned chosenAudioTrackNumber() { return fChosenAudioTrackNumber; } unsigned chosenSubtitleTrackNumber() { return fChosenSubtitleTrackNumber; } FramedSource* createSourceForStreaming(FramedSource* baseSource, unsigned trackNumber, unsigned& estBitrate, unsigned& numFiltersInFrontOfTrack); // Takes a data source (which must be a demultiplexed track from this file) and returns // a (possibly modified) data source that can be used for streaming. RTPSink* createRTPSinkForTrackNumber(unsigned trackNumber, Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic); // Creates a "RTPSink" object that would be appropriate for streaming the specified track, // or NULL if no appropriate "RTPSink" exists private: MatroskaFile(UsageEnvironment& env, char const* fileName, onCreationFunc* onCreation, void* onCreationClientData, char const* preferredLanguage); // called only by createNew() virtual ~MatroskaFile(); static void handleEndOfTrackHeaderParsing(void* clientData); void handleEndOfTrackHeaderParsing(); void addTrack(MatroskaTrack* newTrack, unsigned trackNumber); void addCuePoint(double cueTime, u_int64_t clusterOffsetInFile, unsigned blockNumWithinCluster); Boolean lookupCuePoint(double& cueTime, u_int64_t& resultClusterOffsetInFile, unsigned& resultBlockNumWithinCluster); void printCuePoints(FILE* fid); void removeDemux(MatroskaDemux* demux); private: friend class MatroskaFileParser; friend class MatroskaDemux; char const* fFileName; onCreationFunc* fOnCreation; void* fOnCreationClientData; char const* fPreferredLanguage; unsigned fTimecodeScale; // in nanoseconds float fSegmentDuration; // in units of "fTimecodeScale" u_int64_t fSegmentDataOffset, fClusterOffset, fCuesOffset; class MatroskaTrackTable* fTrackTable; HashTable* fDemuxesTable; class CuePoint* fCuePoints; unsigned fChosenVideoTrackNumber, fChosenAudioTrackNumber, fChosenSubtitleTrackNumber; class MatroskaFileParser* fParserForInitialization; }; // We define our own track type codes as bits (powers of 2), so we can use the set of track types as a bitmap, representing a set: // (Note that MATROSKA_TRACK_TYPE_OTHER must be last, and have the largest value.) #define MATROSKA_TRACK_TYPE_VIDEO 0x01 #define MATROSKA_TRACK_TYPE_AUDIO 0x02 #define MATROSKA_TRACK_TYPE_SUBTITLE 0x04 #define MATROSKA_TRACK_TYPE_OTHER 0x08 class MatroskaTrack { public: MatroskaTrack(); virtual ~MatroskaTrack(); // track parameters unsigned trackNumber; u_int8_t trackType; Boolean isEnabled, isDefault, isForced; unsigned defaultDuration; char* name; char* language; char* codecID; unsigned samplingFrequency; unsigned numChannels; char const* mimeType; unsigned codecPrivateSize; u_int8_t* codecPrivate; Boolean codecPrivateUsesH264FormatForH265; // a hack specifically for H.265 video tracks Boolean codecIsOpus; // a hack for Opus audio unsigned headerStrippedBytesSize; u_int8_t* headerStrippedBytes; unsigned subframeSizeSize; // 0 means: frames do not have subframes (the default behavior) Boolean haveSubframes() const { return subframeSizeSize > 0; } }; class MatroskaDemux: public Medium { public: FramedSource* newDemuxedTrack(); FramedSource* newDemuxedTrack(unsigned& resultTrackNumber); // Returns a new stream ("FramedSource" subclass) that represents the next preferred media // track (video, audio, subtitle - in that order) from the file. (Preferred media tracks // are based on the file's language preference.) // This function returns NULL when no more media tracks exist. FramedSource* newDemuxedTrackByTrackNumber(unsigned trackNumber); // As above, but creates a new stream for a specific track number within the Matroska file. // (You should not call this function more than once with the same track number.) // Note: We assume that: // - Every track created by "newDemuxedTrack()" is later read // - All calls to "newDemuxedTrack()" are made before any track is read protected: friend class MatroskaFile; friend class MatroskaFileParser; class MatroskaDemuxedTrack* lookupDemuxedTrack(unsigned trackNumber); MatroskaDemux(MatroskaFile& ourFile); // we're created only by a "MatroskaFile" (a friend) virtual ~MatroskaDemux(); private: friend class MatroskaDemuxedTrack; void removeTrack(unsigned trackNumber); void continueReading(); // called by a demuxed track to tell us that it has a pending read ("doGetNextFrame()") void seekToTime(double& seekNPT); static void handleEndOfFile(void* clientData); void handleEndOfFile(); private: MatroskaFile& fOurFile; class MatroskaFileParser* fOurParser; HashTable* fDemuxedTracksTable; // Used to implement "newServerMediaSubsession()": u_int8_t fNextTrackTypeToCheck; }; #endif