/********** 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 3 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 **********/ // Copyright (c) 1996-2020, Live Networks, Inc. All rights reserved // A test program that reads a ".mkv" (i.e., Matroska) file, demultiplexes each track // (video, audio, subtitles), and outputs each track to a file. // main program #include #include UsageEnvironment* env; char const* programName; char const* inputFileName; // An array of structures representing the state of the video, audio, and subtitle tracks: static struct { unsigned trackNumber; FramedSource* source; FileSink* sink; } trackState[3]; void onMatroskaFileCreation(MatroskaFile* newFile, void* clientData); // forward void usage() { *env << "usage: " << programName << " \n"; exit(1); } int main(int argc, char** argv) { // Begin by setting up our usage environment: TaskScheduler* scheduler = BasicTaskScheduler::createNew(); env = BasicUsageEnvironment::createNew(*scheduler); // Parse the command line: programName = argv[0]; if (argc != 2) usage(); inputFileName = argv[1]; // Arrange to create a "MatroskaFile" object for the specified file. // (Note that this object is not created immediately, but instead via a callback.) MatroskaFile::createNew(*env, inputFileName, onMatroskaFileCreation, NULL); env->taskScheduler().doEventLoop(); // does not return return 0; // only to prevent compiler warning } void play(); // forward void onMatroskaFileCreation(MatroskaFile* matroskaFile, void* /*clientData*/) { // Create a new demultiplexor for the file: MatroskaDemux* matroskaDemux = matroskaFile->newDemux(); // Create source streams and file sinks for each preferred track; unsigned numActiveTracks = 0; for (unsigned i = 0; i < 3; ++i) { unsigned trackNumber; trackState[i].source = matroskaDemux->newDemuxedTrack(trackNumber); trackState[i].trackNumber = trackNumber; trackState[i].sink = NULL; // by default; may get changed below if (trackState[i].source == NULL) continue; char const* mimeType = matroskaFile->trackMIMEType(trackNumber); if (mimeType == NULL || mimeType[0] == '\0') continue; fprintf(stderr, "#####@@@@@ MatroskaDemuxedTrack for mimeType %s is %p\n", mimeType, trackState[i].source); // Create the file name from "mimeType" by replacing "/" with "-", and adding the // track number at the end: char* fileName = new char[strlen(mimeType) + 100/*more than enough space*/]; sprintf(fileName, "%s-%d", mimeType, trackNumber); for (unsigned j = 0; fileName[j] != '\0'; ++j) { if (fileName[j] == '/') { fileName[j] = '-'; break; } } trackState[i].sink = matroskaFile->createFileSinkForTrackNumber(trackNumber, fileName); if (trackState[i].sink != NULL) { ++numActiveTracks; fprintf(stderr, "Created output file \"%s\" for track %d\n", fileName, trackNumber); } } if (numActiveTracks == 0) { *env << "Error: The Matroska file \"" << inputFileName << "\" has no streamable tracks\n"; *env << "(Perhaps the file does not exist, or is not a 'Matroska' file.)\n"; exit(1); } // Start the streaming: play(); } void afterPlaying(void* /*clientData*/) { *env << "...done reading from file\n"; // Stop playing all sinks, then close the source streams // (which will also close the demultiplexor itself): unsigned i; for (i = 0; i < 3; ++i) { if (trackState[i].sink != NULL) trackState[i].sink->stopPlaying(); Medium::close(trackState[i].source); trackState[i].source = NULL; } // Finally, close the sinks: for (i = 0; i < 3; ++i) Medium::close(trackState[i].sink); exit(0); } void play() { *env << "Beginning to read from file...\n"; // Start playing each track's RTP sink from its corresponding source: for (unsigned i = 0; i < 3; ++i) { if (trackState[i].sink != NULL && trackState[i].source != NULL) { trackState[i].sink->startPlaying(*trackState[i].source, afterPlaying, NULL); } } }