/*---------------------------------------------------------------------- | AP4_LinearReader::AP4_LinearReader +---------------------------------------------------------------------*/ AP4_LinearReader::AP4_LinearReader(AP4_Movie& movie, AP4_ByteStream* fragment_stream, AP4_Size max_buffer) : m_Movie(movie), m_Fragment(NULL), m_FragmentStream(fragment_stream), m_NextFragmentPosition(0), m_BufferFullness(0), m_BufferFullnessPeak(0), m_MaxBufferFullness(max_buffer), m_Mfra(NULL) { m_HasFragments = movie.HasFragments(); if (fragment_stream) { fragment_stream->AddReference(); fragment_stream->Tell(m_NextFragmentPosition); } }
/*---------------------------------------------------------------------- | CheckWarning +---------------------------------------------------------------------*/ static bool CheckWarning(AP4_ByteStream& stream, AP4_ProtectionKeyMap& key_map, Method method) { AP4_File file(stream, AP4_DefaultAtomFactory::Instance, true); AP4_Movie* movie = file.GetMovie(); if (!movie) { if (method != METHOD_MPEG_CENC && method != METHOD_PIFF_CBC && method != METHOD_PIFF_CTR) { fprintf(stderr, "WARNING: no movie atom found in input file\n"); return false; } } bool warning = false; switch (method) { case METHOD_MPEG_CENC: case METHOD_PIFF_CBC: case METHOD_PIFF_CTR: if (movie && !movie->HasFragments()) { fprintf(stderr, "WARNING: MPEG-CENC method only applies to fragmented files\n"); warning = true; } break; default: break; } if (movie) { for (unsigned int i=0; i<movie->GetTracks().ItemCount(); i++) { AP4_Track* track; AP4_Result result = movie->GetTracks().Get(i, track); if (AP4_FAILED(result)) return false; const AP4_DataBuffer* key = key_map.GetKey(track->GetId()); if (key == NULL) { fprintf(stderr, "WARNING: track ID %d will not be encrypted\n", track->GetId()); warning = true; } } } stream.Seek(0); return warning; }
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { if (argc < 3) { PrintUsageAndExit(); } // default options Options.segment_duration = 0; Options.pmt_pid = 0x100; Options.audio_pid = 0x101; Options.video_pid = 0x102; Options.verbose = false; Options.playlist = NULL; Options.playlist_hls_version = 3; Options.input = NULL; Options.output = NULL; Options.segment_duration_threshold = DefaultSegmentDurationThreshold; // parse command line AP4_Result result; char** args = argv+1; while (const char* arg = *args++) { if (!strcmp(arg, "--segment")) { if (*args == NULL) { fprintf(stderr, "ERROR: --segment requires a number\n"); return 1; } Options.segment_duration = strtoul(*args++, NULL, 10); } else if (!strcmp(arg, "--segment-duration-threshold")) { if (*args == NULL) { fprintf(stderr, "ERROR: --segment-duration-threshold requires a number\n"); return 1; } Options.segment_duration_threshold = strtoul(*args++, NULL, 10); } else if (!strcmp(arg, "--verbose")) { Options.verbose = true; } else if (!strcmp(arg, "--pmt-pid")) { if (*args == NULL) { fprintf(stderr, "ERROR: --pmt-pid requires a number\n"); return 1; } Options.pmt_pid = strtoul(*args++, NULL, 10); } else if (!strcmp(arg, "--audio-pid")) { if (*args == NULL) { fprintf(stderr, "ERROR: --audio-pid requires a number\n"); return 1; } Options.audio_pid = strtoul(*args++, NULL, 10); } else if (!strcmp(arg, "--video-pid")) { if (*args == NULL) { fprintf(stderr, "ERROR: --video-pid requires a number\n"); return 1; } Options.video_pid = strtoul(*args++, NULL, 10); } else if (!strcmp(arg, "--playlist")) { if (*args == NULL) { fprintf(stderr, "ERROR: --playlist requires a filename\n"); return 1; } Options.playlist = *args++; } else if (!strcmp(arg, "--playlist-hls-version")) { if (*args == NULL) { fprintf(stderr, "ERROR: --playlist-hls-version requires a number\n"); return 1; } Options.playlist_hls_version = strtoul(*args++, NULL, 10); if (Options.playlist_hls_version ==0) { fprintf(stderr, "ERROR: --playlist-hls-version requires number > 0\n"); return 1; } } else if (Options.input == NULL) { Options.input = arg; } else if (Options.output == NULL) { Options.output = arg; } else { fprintf(stderr, "ERROR: unexpected argument\n"); return 1; } } // check args if (Options.input == NULL) { fprintf(stderr, "ERROR: missing input file name\n"); return 1; } if (Options.output == NULL) { fprintf(stderr, "ERROR: missing output file name\n"); return 1; } // create the input stream AP4_ByteStream* input = NULL; result = AP4_FileByteStream::Create(Options.input, AP4_FileByteStream::STREAM_MODE_READ, input); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open input (%d)\n", result); return 1; } // open the file AP4_File* input_file = new AP4_File(*input, AP4_DefaultAtomFactory::Instance, true); // get the movie AP4_SampleDescription* sample_description; AP4_Movie* movie = input_file->GetMovie(); if (movie == NULL) { fprintf(stderr, "ERROR: no movie in file\n"); return 1; } // get the audio and video tracks AP4_Track* audio_track = movie->GetTrack(AP4_Track::TYPE_AUDIO); AP4_Track* video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO); if (audio_track == NULL && video_track == NULL) { fprintf(stderr, "ERROR: no suitable tracks found\n"); delete input_file; input->Release(); return 1; } // create the appropriate readers AP4_LinearReader* linear_reader = NULL; SampleReader* audio_reader = NULL; SampleReader* video_reader = NULL; if (movie->HasFragments()) { // create a linear reader to get the samples linear_reader = new AP4_LinearReader(*movie, input); if (audio_track) { linear_reader->EnableTrack(audio_track->GetId()); audio_reader = new FragmentedSampleReader(*linear_reader, audio_track->GetId()); } if (video_track) { linear_reader->EnableTrack(video_track->GetId()); video_reader = new FragmentedSampleReader(*linear_reader, video_track->GetId()); } } else { if (audio_track) { audio_reader = new TrackSampleReader(*audio_track); } if (video_track) { video_reader = new TrackSampleReader(*video_track); } } // create an MPEG2 TS Writer AP4_Mpeg2TsWriter writer(Options.pmt_pid); AP4_Mpeg2TsWriter::SampleStream* audio_stream = NULL; AP4_Mpeg2TsWriter::SampleStream* video_stream = NULL; // add the audio stream if (audio_track) { sample_description = audio_track->GetSampleDescription(0); if (sample_description == NULL) { fprintf(stderr, "ERROR: unable to parse audio sample description\n"); goto end; } unsigned int stream_type = 0; unsigned int stream_id = 0; if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_MP4A) { stream_type = AP4_MPEG2_STREAM_TYPE_ISO_IEC_13818_7; stream_id = AP4_MPEG2_TS_DEFAULT_STREAM_ID_AUDIO; } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AC_3 || sample_description->GetFormat() == AP4_SAMPLE_FORMAT_EC_3) { stream_type = AP4_MPEG2_STREAM_TYPE_ATSC_AC3; stream_id = AP4_MPEG2_TS_STREAM_ID_PRIVATE_STREAM_1; } else { fprintf(stderr, "ERROR: audio codec not supported\n"); return 1; } result = writer.SetAudioStream(audio_track->GetMediaTimeScale(), stream_type, stream_id, audio_stream, Options.audio_pid); if (AP4_FAILED(result)) { fprintf(stderr, "could not create audio stream (%d)\n", result); goto end; } } // add the video stream if (video_track) { sample_description = video_track->GetSampleDescription(0); if (sample_description == NULL) { fprintf(stderr, "ERROR: unable to parse video sample description\n"); goto end; } // decide on the stream type unsigned int stream_type = 0; unsigned int stream_id = AP4_MPEG2_TS_DEFAULT_STREAM_ID_VIDEO; if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC1 || sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC2 || sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 || sample_description->GetFormat() == AP4_SAMPLE_FORMAT_AVC4) { stream_type = AP4_MPEG2_STREAM_TYPE_AVC; } else if (sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HEV1 || sample_description->GetFormat() == AP4_SAMPLE_FORMAT_HVC1) { stream_type = AP4_MPEG2_STREAM_TYPE_HEVC; } else { fprintf(stderr, "ERROR: video codec not supported\n"); return 1; } result = writer.SetVideoStream(video_track->GetMediaTimeScale(), stream_type, stream_id, video_stream, Options.video_pid); if (AP4_FAILED(result)) { fprintf(stderr, "could not create video stream (%d)\n", result); goto end; } } result = WriteSamples(writer, audio_track, audio_reader, audio_stream, video_track, video_reader, video_stream, Options.segment_duration_threshold); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: failed to write samples (%d)\n", result); } end: delete input_file; input->Release(); delete linear_reader; delete audio_reader; delete video_reader; return result == AP4_SUCCESS?0:1; }