/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { AP4_Result result = AP4_SUCCESS; // parse the command line if (argc != 2) PrintUsageAndExit(); // create the input stream AP4_ByteStream* input; try { input = new AP4_FileByteStream(argv[1], AP4_FileByteStream::STREAM_MODE_READ); } catch (AP4_Exception) { fprintf(stderr, "ERROR: cannot open input file (%s)\n", argv[1]); return 1; } AP4_File* file = new AP4_File(*input); AP4_Movie* movie = file->GetMovie(); if (movie != NULL) { // get a hint track reader AP4_Track* hint_track = movie->GetTrack(AP4_Track::TYPE_HINT, 1); if (hint_track == NULL) { AP4_Debug("No hint track in this movie\n"); return AP4_FAILURE; } AP4_HintTrackReader reader(*hint_track, *movie, 0x01020304); AP4_String rtp_file_name(argv[1]); rtp_file_name += ".rtp"; // display the sdp AP4_String sdp; reader.GetSdpText(sdp); AP4_Debug("sdp:\n%s\n\n", sdp.c_str()); // dump the packet result = DumpRtpPackets(reader, rtp_file_name.c_str()); if (AP4_FAILED(result)) goto bail; } else { AP4_Debug("No movie found in the file\n"); return AP4_FAILURE; } bail: delete file; input->Release(); return result; }
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { if (argc != 2) { PrintUsageAndExit(); } const char* input_filename = argv[1]; // open the input AP4_ByteStream* input = NULL; try { input = new AP4_FileByteStream(input_filename, AP4_FileByteStream::STREAM_MODE_READ); } catch (AP4_Exception) { fprintf(stderr, "ERROR: cannot open input file (%s)\n", input_filename); return 1; } // get the movie AP4_File* file = new AP4_File(*input); AP4_Movie* movie = file->GetMovie(); CHECK(movie != NULL); AP4_Track* video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO); CHECK(video_track != NULL); AP4_Ordinal index; index = video_track->GetNearestSyncSampleIndex(0, true); CHECK(index == 0); index = video_track->GetNearestSyncSampleIndex(0, false); CHECK(index == 0); index = video_track->GetNearestSyncSampleIndex(1, true); CHECK(index == 0); index = video_track->GetNearestSyncSampleIndex(1, false); CHECK(index == 12); index = video_track->GetNearestSyncSampleIndex(52, true); CHECK(index == 48); index = video_track->GetNearestSyncSampleIndex(52, false); CHECK(index == video_track->GetSampleCount()); // cleanup delete file; input->Release(); return 0; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | AP4_HintTrackReader::AP4_HintTrackReader +---------------------------------------------------------------------*/ AP4_HintTrackReader::AP4_HintTrackReader(AP4_Track& hint_track, AP4_Movie& movie, AP4_UI32 ssrc) : m_HintTrack(hint_track), m_MediaTrack(NULL), m_MediaTimeScale(0), m_RtpSampleData(NULL), m_Ssrc(ssrc), m_SampleIndex(0), m_PacketIndex(0), m_RtpSequenceStart(0), m_RtpTimeStampStart(0), m_RtpTimeScale(0) { // get the media track AP4_TrakAtom* hint_trak_atom = hint_track.UseTrakAtom(); AP4_Atom* atom = hint_trak_atom->FindChild("tref/hint"); if (atom != NULL) { AP4_UI32 media_track_id = AP4_DYNAMIC_CAST(AP4_TrefTypeAtom, atom)->GetTrackIds()[0]; m_MediaTrack = movie.GetTrack(media_track_id); // get the media time scale m_MediaTimeScale = m_MediaTrack->GetMediaTimeScale(); } // initiate random generator srand((int)time(NULL)); // rtp sequence start init TODO!! m_RtpSequenceStart = (AP4_UI16)(rand()&0xFFFF); // rtp timestamp start init TODO!! m_RtpTimeStampStart = rand(); // rtp time scale atom = hint_trak_atom->FindChild("mdia/minf/stbl/rtp /tims"); if (atom) { AP4_TimsAtom* tims = AP4_DYNAMIC_CAST(AP4_TimsAtom, atom); m_RtpTimeScale = tims->GetTimeScale(); } // generate a random ssrc if = 0 if (m_Ssrc == 0) { m_Ssrc = rand(); } // get the first sample GetRtpSample(0); }
/*---------------------------------------------------------------------- | 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); } }
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { if (argc < 3) { PrintUsageAndExit(); } // parse command line AP4_Result result; char** args = argv+1; unsigned char key[16]; bool key_option = false; if (!strcmp(*args, "--key")) { if (argc != 5) { fprintf(stderr, "ERROR: invalid command line\n"); return 1; } ++args; if (AP4_ParseHex(*args++, key, 16)) { fprintf(stderr, "ERROR: invalid hex format for key\n"); return 1; } key_option = true; } // create the input stream AP4_ByteStream* input = NULL; result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_READ, input); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open input (%d)\n", result); } // create the output stream AP4_ByteStream* output = NULL; result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_WRITE, output); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open output (%d)\n", result); } // open the file AP4_File* input_file = new AP4_File(*input); // get the movie AP4_SampleDescription* sample_description; AP4_Track* video_track; AP4_Movie* movie = input_file->GetMovie(); if (movie == NULL) { fprintf(stderr, "ERROR: no movie in file\n"); goto end; } // get the video track video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO); if (video_track == NULL) { fprintf(stderr, "ERROR: no video track found\n"); goto end; } // check that the track is of the right type sample_description = video_track->GetSampleDescription(0); if (sample_description == NULL) { fprintf(stderr, "ERROR: unable to parse sample description\n"); goto end; } // show info AP4_Debug("Video Track:\n"); AP4_Debug(" duration: %ld ms\n", video_track->GetDurationMs()); AP4_Debug(" sample count: %ld\n", video_track->GetSampleCount()); switch (sample_description->GetType()) { case AP4_SampleDescription::TYPE_HEVC: WriteSamples(video_track, sample_description, output); break; case AP4_SampleDescription::TYPE_PROTECTED: if (!key_option) { fprintf(stderr, "ERROR: encrypted tracks require a key\n"); goto end; } DecryptAndWriteSamples(video_track, sample_description, key, output); break; default: fprintf(stderr, "ERROR: unsupported sample type\n"); break; } end: delete input_file; input->Release(); output->Release(); return 0; }
bool Session::initialize() { // Get URN's wich are supported by this addon if (!license_type_.empty()) GetSupportedDecrypterURN(dashtree_.adp_pssh_); // Open mpd file const char* delim(strrchr(mpdFileURL_.c_str(), '/')); if (!delim) { xbmc->Log(ADDON::LOG_ERROR, "Invalid mpdURL: / expected (%s)", mpdFileURL_.c_str()); return false; } dashtree_.base_url_ = std::string(mpdFileURL_.c_str(), (delim - mpdFileURL_.c_str()) + 1); if (!dashtree_.open(mpdFileURL_.c_str()) || dashtree_.empty()) { xbmc->Log(ADDON::LOG_ERROR, "Could not open / parse mpdURL (%s)", mpdFileURL_.c_str()); return false; } xbmc->Log(ADDON::LOG_INFO, "Successfully parsed .mpd file. Download speed: %0.4f Bytes/s", dashtree_.download_speed_); if (dashtree_.encryptionState_ == dash::DASHTree::ENCRYTIONSTATE_ENCRYPTED) { xbmc->Log(ADDON::LOG_ERROR, "Unable to handle decryption. Unsupported!"); return false; } uint32_t min_bandwidth(0), max_bandwidth(0); { int buf; xbmc->GetSetting("MINBANDWIDTH", (char*)&buf); min_bandwidth = buf; xbmc->GetSetting("MAXBANDWIDTH", (char*)&buf); max_bandwidth = buf; } // create SESSION::STREAM objects. One for each AdaptationSet unsigned int i(0); const dash::DASHTree::AdaptationSet *adp; for (std::vector<STREAM*>::iterator b(streams_.begin()), e(streams_.end()); b != e; ++b) SAFE_DELETE(*b); streams_.clear(); while ((adp = dashtree_.GetAdaptationSet(i++))) { streams_.push_back(new STREAM(dashtree_, adp->type_)); STREAM &stream(*streams_.back()); stream.stream_.prepare_stream(adp, width_, height_, min_bandwidth, max_bandwidth); const dash::DASHTree::Representation *rep(stream.stream_.getRepresentation()); stream.info_.m_Width = rep->width_; stream.info_.m_Height = rep->height_; stream.info_.m_Aspect = rep->aspect_; stream.info_.m_pID = i; switch (adp->type_) { case dash::DASHTree::VIDEO: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_VIDEO; break; case dash::DASHTree::AUDIO: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_AUDIO; break; case dash::DASHTree::TEXT: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_TELETEXT; break; default: break; } // we currently use only the first track! std::string::size_type pos = rep->codecs_.find(","); if (pos == std::string::npos) pos = rep->codecs_.size(); strncpy(stream.info_.m_codecInternalName, rep->codecs_.c_str(), pos); stream.info_.m_codecInternalName[pos] = 0; if (rep->codecs_.find("mp4a") == 0) strcpy(stream.info_.m_codecName, "aac"); else if (rep->codecs_.find("ec-3") == 0 || rep->codecs_.find("ac-3") == 0) strcpy(stream.info_.m_codecName, "eac3"); else if (rep->codecs_.find("avc") == 0) strcpy(stream.info_.m_codecName, "h264"); else if (rep->codecs_.find("hevc") == 0) strcpy(stream.info_.m_codecName, "hevc"); stream.info_.m_FpsRate = rep->fpsRate_; stream.info_.m_FpsScale = rep->fpsScale_; stream.info_.m_SampleRate = rep->samplingRate_; stream.info_.m_Channels = rep->channelCount_; stream.info_.m_Bandwidth = rep->bandwidth_; strcpy(stream.info_.m_language, adp->language_.c_str()); } // Try to initialize an SingleSampleDecryptor if (dashtree_.encryptionState_) { AP4_DataBuffer init_data; if (dashtree_.adp_pssh_.second == "FILE") { std::string strkey(dashtree_.adp_pssh_.first.substr(9)); while (size_t pos = strkey.find('-') != std::string::npos) strkey.erase(pos, 1); if (strkey.size() != 32) { xbmc->Log(ADDON::LOG_ERROR, "Key system mismatch (%s)!", dashtree_.adp_pssh_.first.c_str()); return false; } unsigned char key_system[16]; AP4_ParseHex(strkey.c_str(), key_system, 16); Session::STREAM *stream(streams_[0]); stream->enabled = true; stream->stream_.start_stream(0); stream->stream_.select_stream(true); stream->input_ = new AP4_DASHStream(&stream->stream_); stream->input_file_ = new AP4_File(*stream->input_, AP4_DefaultAtomFactory::Instance, true); AP4_Movie* movie = stream->input_file_->GetMovie(); if (movie == NULL) { xbmc->Log(ADDON::LOG_ERROR, "No MOOV in stream!"); stream->disable(); return false; } AP4_Array<AP4_PsshAtom*>& pssh = movie->GetPsshAtoms(); for (unsigned int i = 0; !init_data.GetDataSize() && i < pssh.ItemCount(); i++) { if (memcmp(pssh[i]->GetSystemId(), key_system, 16) == 0) init_data.AppendData(pssh[i]->GetData().GetData(), pssh[i]->GetData().GetDataSize()); } if (!init_data.GetDataSize()) { xbmc->Log(ADDON::LOG_ERROR, "Could not extract license from video stream (PSSH not found)"); stream->disable(); return false; } stream->disable(); } else { init_data.SetBufferSize(1024); unsigned int init_data_size(1024); b64_decode(dashtree_.pssh_.second.data(), dashtree_.pssh_.second.size(), init_data.UseData(), init_data_size); init_data.SetDataSize(init_data_size); } return (single_sample_decryptor_ = CreateSingleSampleDecrypter(init_data))!=0; } return true; }
void EnableStreamAtPTS(int streamid, uint64_t pts) { xbmc->Log(ADDON::LOG_DEBUG, "EnableStreamAtPTS(%d, %" PRIi64, streamid, pts); if (!session) return; Session::STREAM *stream(session->GetStream(streamid)); if (!stream) return; if (~pts) { if (stream->enabled) return; stream->enabled = true; stream->stream_.start_stream(0); stream->stream_.select_stream(true); stream->input_ = new AP4_DASHStream(&stream->stream_); stream->input_file_ = new AP4_File(*stream->input_, AP4_DefaultAtomFactory::Instance, true); AP4_Movie* movie = stream->input_file_->GetMovie(); if (movie == NULL) { xbmc->Log(ADDON::LOG_ERROR, "No MOOV in stream!"); return stream->disable(); } static const AP4_Track::Type TIDC[dash::DASHTree::STREAM_TYPE_COUNT] = { AP4_Track::TYPE_UNKNOWN, AP4_Track::TYPE_VIDEO, AP4_Track::TYPE_AUDIO, AP4_Track::TYPE_TEXT }; AP4_Track *track = movie->GetTrack(TIDC[stream->stream_.get_type()]); if (!track) { xbmc->Log(ADDON::LOG_ERROR, "No suitable track found in stream"); return stream->disable(); } stream->reader_ = new FragmentedSampleReader(stream->input_, movie, track, streamid, session->GetSingleSampleDecryptor()); // ExtraData is now available...... stream->info_.m_ExtraData = stream->reader_->GetExtraData(); stream->info_.m_ExtraSize = stream->reader_->GetExtraDataSize(); // Set the session Changed to force new GetStreamInfo call from kodi -> addon session->CheckChange(true); if ((pts > 0 && !session->SeekTime(static_cast<double>(pts)*0.000001f, streamid)) ||(pts <= 0 && !AP4_SUCCEEDED(stream->reader_->ReadSample()))) return stream->disable(); // Maybe we have changed information for hints after parsing the first packet... stream->reader_->GetVideoInformation(stream->info_.m_Width, stream->info_.m_Height); stream->reader_->GetAudioInformation(stream->info_.m_Channels); return; } return stream->disable(); }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | Mp4ParserInput_SetStream +---------------------------------------------------------------------*/ BLT_METHOD Mp4ParserInput_SetStream(BLT_InputStreamUser* _self, ATX_InputStream* stream, const BLT_MediaType* stream_media_type) { Mp4Parser* self = ATX_SELF_M(input, Mp4Parser, BLT_InputStreamUser); BLT_Result result = BLT_ERROR_INVALID_MEDIA_FORMAT; /* check media type */ if (stream_media_type == NULL || (stream_media_type->id != self->input.audio_media_type.id && stream_media_type->id != self->input.video_media_type.id)) { return BLT_ERROR_INVALID_MEDIA_TYPE; } /* if we had a file before, release it now */ delete self->input.mp4_file; self->input.mp4_file = NULL; self->input.slow_seek = false; /* create an adapter for the stream */ AP4_ByteStream* stream_adapter = new ATX_InputStream_To_AP4_ByteStream_Adapter(stream); /* check if the source can seek quickly or not */ { ATX_Properties* stream_properties = ATX_CAST(stream, ATX_Properties); if (stream_properties) { ATX_PropertyValue property_value; result = ATX_Properties_GetProperty(stream_properties, ATX_INPUT_STREAM_PROPERTY_SEEK_SPEED, &property_value); if (ATX_SUCCEEDED(result) && property_value.type == ATX_PROPERTY_VALUE_TYPE_INTEGER && property_value.data.integer <= ATX_INPUT_STREAM_SEEK_SPEED_SLOW) { AP4_ByteStream* buffered = new AP4_BufferedInputStream(*stream_adapter); ATX_LOG_FINE("using no-seek mode, source is slow"); stream_adapter->Release(); stream_adapter = buffered; self->input.slow_seek = true; } } } /* parse the MP4 file */ ATX_LOG_FINE("parsing MP4 file"); self->input.mp4_file = new AP4_File(*stream_adapter, AP4_DefaultAtomFactory::Instance, true); /* parse until moov only */ stream_adapter->Release(); // get the global file info AP4_Movie* movie = self->input.mp4_file->GetMovie(); if (movie == NULL) { ATX_LOG_FINE("no movie in file"); goto fail; } // update the stream info BLT_StreamInfo stream_info; stream_info.type = BLT_STREAM_TYPE_MULTIPLEXED; stream_info.id = 0; stream_info.duration = movie->GetDurationMs(); stream_info.mask = BLT_STREAM_INFO_MASK_TYPE | BLT_STREAM_INFO_MASK_ID | BLT_STREAM_INFO_MASK_DURATION; BLT_Stream_SetInfo(ATX_BASE(self, BLT_BaseMediaNode).context, &stream_info); // create a linear reader if the source is slow-seeking if (self->input.slow_seek) { self->input.reader = new AP4_LinearReader(*movie); } // setup the tracks result = Mp4Parser_SetupAudioOutput(self, movie); if (BLT_FAILED(result)) goto fail; result = Mp4Parser_SetupVideoOutput(self, movie); if (BLT_FAILED(result)) goto fail; // check that we have at least one media track if (self->audio_output.track == NULL && self->video_output.track == NULL) { ATX_LOG_FINE("no media track found"); goto fail; } return BLT_SUCCESS; fail: delete self->input.mp4_file; self->input.mp4_file = NULL; self->audio_output.track = NULL; self->video_output.track = NULL; return result; }
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { if (argc < 2) { PrintUsageAndExit(); } // default options Options.verbose = false; Options.input = NULL; Options.init_segment_name = AP4_SPLIT_DEFAULT_INIT_SEGMENT_NAME; Options.media_segment_name = AP4_SPLIT_DEFAULT_MEDIA_SEGMENT_NAME; Options.pattern_params = AP4_SPLIT_DEFAULT_PATTERN_PARAMS; Options.start_number = 1; Options.track_id = 0; Options.audio_only = false; Options.video_only = false; Options.init_only = false; Options.track_filter = 0; // parse command line AP4_Result result; char** args = argv+1; while (const char* arg = *args++) { if (!strcmp(arg, "--verbose")) { Options.verbose = true; } else if (!strcmp(arg, "--init-segment")) { if (*args == NULL) { fprintf(stderr, "ERROR: missing argument after --init-segment option\n"); return 1; } Options.init_segment_name = *args++; } else if (!strcmp(arg, "--media-segment")) { if (*args == NULL) { fprintf(stderr, "ERROR: missing argument after --media-segment option\n"); return 1; } Options.media_segment_name = *args++; } else if (!strcmp(arg, "--pattern-parameters")) { if (*args == NULL) { fprintf(stderr, "ERROR: missing argument after --pattern-params option\n"); return 1; } Options.pattern_params = *args++; } else if (!strcmp(arg, "--track-id")) { Options.track_id = strtoul(*args++, NULL, 10); } else if (!strcmp(arg, "--start-number")) { Options.start_number = strtoul(*args++, NULL, 10); } else if (!strcmp(arg, "--init-only")) { Options.init_only = true; } else if (!strcmp(arg, "--audio")) { Options.audio_only = true; } else if (!strcmp(arg, "--video")) { Options.video_only = true; } else if (Options.input == NULL) { Options.input = 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.audio_only && (Options.video_only || Options.track_id)) || (Options.video_only && (Options.audio_only || Options.track_id)) || (Options.track_id && (Options.audio_only || Options.video_only))) { fprintf(stderr, "ERROR: --audio, --video and --track-id options are mutualy exclusive\n"); return 1; } if (strlen(Options.pattern_params) < 1) { fprintf(stderr, "ERROR: --pattern-params argument is too short\n"); return 1; } if (strlen(Options.pattern_params) > 2) { fprintf(stderr, "ERROR: --pattern-params argument is too long\n"); return 1; } const char* cursor = Options.pattern_params; while (*cursor) { if (*cursor != 'I' && *cursor != 'N') { fprintf(stderr, "ERROR: invalid pattern parameter '%c'\n", *cursor); return 1; } ++cursor; } // 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; } // get the movie AP4_File* file = new AP4_File(*input, AP4_DefaultAtomFactory::Instance, true); AP4_Movie* movie = file->GetMovie(); if (movie == NULL) { fprintf(stderr, "no movie found in file\n"); return 1; } // filter tracks if required if (Options.audio_only) { AP4_Track* track = movie->GetTrack(AP4_Track::TYPE_AUDIO); if (track == NULL) { fprintf(stderr, "--audio option specified, but no audio track found\n"); return 1; } Options.track_filter = track->GetId(); } else if (Options.video_only) { AP4_Track* track = movie->GetTrack(AP4_Track::TYPE_VIDEO); if (track == NULL) { fprintf(stderr, "--video option specified, but no video track found\n"); return 1; } Options.track_filter = track->GetId(); } else if (Options.track_id) { AP4_Track* track = movie->GetTrack(Options.track_id); if (track == NULL) { fprintf(stderr, "--track-id option specified, but no such track found\n"); return 1; } Options.track_filter = track->GetId(); } // save the init segment AP4_ByteStream* output = NULL; result = AP4_FileByteStream::Create(Options.init_segment_name, AP4_FileByteStream::STREAM_MODE_WRITE, output); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open output file (%d)\n", result); return 1; } AP4_FtypAtom* ftyp = file->GetFileType(); if (ftyp) { result = ftyp->Write(*output); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot write ftyp segment (%d)\n", result); return 1; } } if (Options.track_filter) { AP4_MoovAtom* moov = movie->GetMoovAtom(); // only keep the 'trak' atom that we need AP4_List<AP4_Atom>::Item* child = moov->GetChildren().FirstItem(); while (child) { AP4_Atom* atom = child->GetData(); child = child->GetNext(); if (atom->GetType() == AP4_ATOM_TYPE_TRAK) { AP4_TrakAtom* trak = (AP4_TrakAtom*)atom; AP4_TkhdAtom* tkhd = (AP4_TkhdAtom*)trak->GetChild(AP4_ATOM_TYPE_TKHD); if (tkhd && tkhd->GetTrackId() != Options.track_filter) { atom->Detach(); delete atom; } } } // only keep the 'trex' atom that we need AP4_ContainerAtom* mvex = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moov->GetChild(AP4_ATOM_TYPE_MVEX)); if (mvex) { child = mvex->GetChildren().FirstItem(); while (child) { AP4_Atom* atom = child->GetData(); child = child->GetNext(); if (atom->GetType() == AP4_ATOM_TYPE_TREX) { AP4_TrexAtom* trex = AP4_DYNAMIC_CAST(AP4_TrexAtom, atom); if (trex && trex->GetTrackId() != Options.track_filter) { atom->Detach(); delete atom; } } } } } result = movie->GetMoovAtom()->Write(*output); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot write init segment (%d)\n", result); return 1; } AP4_Atom* atom = NULL; unsigned int track_id = 0; for (;!Options.init_only;) { // process the next atom result = AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(*input, atom); if (AP4_FAILED(result)) break; if (atom->GetType() == AP4_ATOM_TYPE_MOOF) { AP4_ContainerAtom* moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); unsigned int traf_count = 0; AP4_ContainerAtom* traf = NULL; do { traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof->GetChild(AP4_ATOM_TYPE_TRAF, traf_count)); if (traf == NULL) break; AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD)); if (tfhd == NULL) { fprintf(stderr, "ERROR: invalid media format\n"); return 1; } track_id = tfhd->GetTrackId(); traf_count++; } while (traf); // check if this fragment has more than one traf if (traf_count > 1) { if (Options.audio_only) { fprintf(stderr, "ERROR: --audio option incompatible with multi-track fragments"); return 1; } if (Options.video_only) { fprintf(stderr, "ERROR: --video option incompatible with multi-track fragments"); return 1; } track_id = 0; } // open a new file for this fragment if (output) { output->Release(); output = NULL; } char segment_name[4096]; if (Options.track_filter == 0 || Options.track_filter == track_id) { AP4_UI64 p[2] = {0,0}; unsigned int params_len = strlen(Options.pattern_params); for (unsigned int i=0; i<params_len; i++) { if (Options.pattern_params[i] == 'I') { p[i] = track_id; } else if (Options.pattern_params[i] == 'N') { p[i] = NextFragmentIndex(track_id)+Options.start_number; } } switch (params_len) { case 1: sprintf(segment_name, Options.media_segment_name, p[0]); break; case 2: sprintf(segment_name, Options.media_segment_name, p[0], p[1]); break; default: segment_name[0] = 0; break; } result = AP4_FileByteStream::Create(segment_name, AP4_FileByteStream::STREAM_MODE_WRITE, output); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open output file (%d)\n", result); return 1; } } } // write the atom if (output && atom->GetType() != AP4_ATOM_TYPE_MFRA) { atom->Write(*output); } delete atom; } // cleanup delete file; if (input) input->Release(); if (output) output->Release(); return 0; }
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { AP4_Result result; if (argc < 2) { PrintUsageAndExit(); } // open the input AP4_ByteStream* input; try { input = new AP4_FileByteStream(argv[1], AP4_FileByteStream::STREAM_MODE_READ); } catch (AP4_Exception&) { AP4_Debug("ERROR: cannot open input (%s)\n", argv[1]); return 1; } // open the output AP4_ByteStream* output = new AP4_FileByteStream( argv[2], AP4_FileByteStream::STREAM_MODE_WRITE); // create a sample table AP4_SyntheticSampleTable* sample_table = new AP4_SyntheticSampleTable(); // create an ADTS parser AP4_AdtsParser parser; bool initialized = false; unsigned int sample_description_index = 0; // read from the input, feed, and get AAC frames AP4_UI32 sample_rate = 0; AP4_Cardinal sample_count = 0; bool eos = false; for(;;) { // try to get a frame AP4_AacFrame frame; result = parser.FindFrame(frame); if (AP4_SUCCEEDED(result)) { AP4_Debug("AAC frame [%06d]: size = %d, %d kHz, %d ch\n", sample_count, frame.m_Info.m_FrameLength, frame.m_Info.m_SamplingFrequency, frame.m_Info.m_ChannelConfiguration); if (!initialized) { initialized = true; // create a sample description for our samples AP4_DataBuffer dsi; unsigned char aac_dsi[2] = {0x12, 0x10}; dsi.SetData(aac_dsi, 2); AP4_MpegAudioSampleDescription* sample_description = new AP4_MpegAudioSampleDescription( AP4_MPEG4_AUDIO_OTI, // object type frame.m_Info.m_SamplingFrequency, 16, // sample size frame.m_Info.m_ChannelConfiguration, &dsi, // decoder info 6144, // buffer size 128000, // max bitrate 128000); // average bitrate sample_description_index = sample_table->AddSampleDescription(sample_description); sample_rate = frame.m_Info.m_SamplingFrequency; } AP4_MemoryByteStream* sample_data = new AP4_MemoryByteStream(frame.m_Info.m_FrameLength); frame.m_Source->ReadBytes(sample_data->GetBuffer(), frame.m_Info.m_FrameLength); printf("%02x %02x %02x %02x\n", sample_data->GetBuffer()[0], sample_data->GetBuffer()[1], sample_data->GetBuffer()[2], sample_data->GetBuffer()[3]); sample_table->AddSample(*sample_data, 0, frame.m_Info.m_FrameLength, sample_description_index); sample_data->Release(); sample_count++; } else { if (eos) break; } // read some data and feed the parser AP4_UI08 input_buffer[4096]; AP4_Size bytes_read = 0; AP4_Size to_read = parser.GetBytesFree(); if (to_read) { if (to_read > sizeof(input_buffer)) to_read = sizeof(input_buffer); result = input->Read(input_buffer, to_read, &bytes_read); if (AP4_SUCCEEDED(result)) { AP4_Size to_feed = bytes_read; result = parser.Feed(input_buffer, &to_feed); if (AP4_FAILED(result)) { AP4_Debug("ERROR: parser.Feed() failed (%d)\n", result); return 1; } } else { if (result == AP4_ERROR_EOS) { eos = true; } } } } // create an audio track AP4_Track* track = new AP4_Track(AP4_Track::TYPE_AUDIO, sample_table, 0, // track id sample_rate, // movie time scale sample_rate, // track time scale sample_count*1024, // track duration "eng", // language 0, 0); // width, height // create a movie AP4_Movie* movie = new AP4_Movie(); // add the track to the movie movie->AddTrack(track); // create a multimedia file AP4_File* file = new AP4_File(movie); // create a writer to write the file AP4_FileWriter* writer = new AP4_FileWriter(*file); // write the file to the output writer->Write(*output); delete writer; delete file; delete output; return 0; }