bool TimeSeek(double pts, bool preceeding) { AP4_Ordinal sampleIndex; if (AP4_SUCCEEDED(SeekSample(m_Track->GetId(), static_cast<AP4_UI64>(pts*(double)m_Track->GetMediaTimeScale()), sampleIndex, preceeding))) { if (m_Decrypter) m_Decrypter->SetSampleIndex(sampleIndex); m_started = true; return AP4_SUCCEEDED(ReadSample()); } return false; };
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { if (argc != 2) { PrintUsageAndExit(); } const char* input_filename = argv[1]; // open the input AP4_ByteStream* input = NULL; AP4_Result result = AP4_FileByteStream::Create(input_filename, AP4_FileByteStream::STREAM_MODE_READ, input); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open input file (%s)\n", input_filename); return 1; } // get the movie AP4_File* file = new AP4_File(*input, AP4_DefaultAtomFactory::Instance, true); AP4_Movie* movie = file->GetMovie(); AP4_Atom* atom = NULL; do { // process the next atom result = AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(*input, atom); if (AP4_SUCCEEDED(result)) { printf("atom size=%lld\n", atom->GetSize()); if (atom->GetType() == AP4_ATOM_TYPE_MOOF) { AP4_ContainerAtom* moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); if (moof) { // remember where we are in the stream AP4_Position position = 0; input->Tell(position); // process the movie fragment ProcessMoof(movie, moof, input, position-atom->GetSize(), position+8); // go back to where we were before processing the fragment input->Seek(position); } } else { delete atom; } } } while (AP4_SUCCEEDED(result)); // cleanup delete file; input->Release(); return 0; }
/*---------------------------------------------------------------------- | AP4_AtomFactory::CreateAtomsFromStream +---------------------------------------------------------------------*/ AP4_Result AP4_AtomFactory::CreateAtomsFromStream(AP4_ByteStream& stream, AP4_AtomParent& atoms) { AP4_LargeSize stream_size = 0; AP4_Position stream_position = 0; AP4_LargeSize bytes_available = (AP4_LargeSize)(-1); if (AP4_SUCCEEDED(stream.GetSize(stream_size)) && stream_size != 0 && AP4_SUCCEEDED(stream.Tell(stream_position)) && stream_position <= stream_size) { bytes_available = stream_size-stream_position; } return CreateAtomsFromStream(stream, bytes_available, atoms); }
/*---------------------------------------------------------------------- | AP4_File::AP4_File +---------------------------------------------------------------------*/ AP4_File::AP4_File(AP4_ByteStream& stream, AP4_AtomFactory& atom_factory) : m_Movie(NULL) { // get all atoms AP4_Atom* atom; while (AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(stream, atom))) { switch (atom->GetType()) { case AP4_ATOM_TYPE_MOOV: m_Movie = new AP4_Movie(dynamic_cast<AP4_MoovAtom*>(atom), stream); break; case AP4_ATOM_TYPE_MOOF: if (m_Movie) { m_Movie->ProcessMoof(AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom), stream); } delete atom; break; case AP4_ATOM_TYPE_FTYP: //m_Movie = new AP4_Movie(dynamic_cast<AP4_FtypAtom*>(atom), stream); m_FileType = dynamic_cast<AP4_FtypAtom*>(atom); default: m_OtherAtoms.Add(atom); } } }
/*---------------------------------------------------------------------- | AP4_TrackSampleSource::ReadNextSample +---------------------------------------------------------------------*/ AP4_Result AP4_TrackSampleSource::ReadNextSample(AP4_Sample& sample, AP4_DataBuffer& buffer) { AP4_Result result = m_Track->ReadSample(m_SampleIndex, sample, buffer); if (AP4_SUCCEEDED(result)) ++m_SampleIndex; return result; }
/*---------------------------------------------------------------------- | AP4_StsdAtom::AP4_StsdAtom +---------------------------------------------------------------------*/ AP4_StsdAtom::AP4_StsdAtom(AP4_UI32 size, AP4_UI08 version, AP4_UI32 flags, AP4_ByteStream& stream, AP4_AtomFactory& atom_factory) : AP4_ContainerAtom(AP4_ATOM_TYPE_STSD, size, false, version, flags) { // read the number of entries AP4_UI32 entry_count; stream.ReadUI32(entry_count); // save and switch the factory's context atom_factory.PushContext(m_Type); // read all entries AP4_LargeSize bytes_available = size-AP4_FULL_ATOM_HEADER_SIZE-4; for (unsigned int i=0; i<entry_count; i++) { AP4_Atom* atom; if (AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(stream, bytes_available, atom))) { atom->SetParent(this); m_Children.Add(atom); } } // restore the saved context atom_factory.PopContext(); // initialize the sample description cache m_SampleDescriptions.EnsureCapacity(m_Children.ItemCount()); for (AP4_Ordinal i=0; i<m_Children.ItemCount(); i++) { m_SampleDescriptions.Append(NULL); } }
/*---------------------------------------------------------------------- | AP4_StsdAtom::AP4_StsdAtom +---------------------------------------------------------------------*/ AP4_StsdAtom::AP4_StsdAtom(AP4_Size size, AP4_ByteStream& stream, AP4_AtomFactory& atom_factory) : AP4_ContainerAtom(AP4_ATOM_TYPE_STSD, size, true, stream) { // read the number of entries AP4_UI32 entry_count; stream.ReadUI32(entry_count); // read all entries AP4_Size bytes_available = size-AP4_FULL_ATOM_HEADER_SIZE-4; m_Data.SetDataSize(bytes_available); stream.Read(m_Data.UseData(), m_Data.GetDataSize()); AP4_ByteStream* s = DNew AP4_MemoryByteStream(m_Data.UseData(), m_Data.GetDataSize()); for (unsigned int i=0; i<entry_count; i++) { AP4_Atom* atom; if (AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(*s, bytes_available, atom, this))) { atom->SetParent(this); m_Children.Add(atom); } } s->Release(); // initialize the sample description cache m_SampleDescriptions.EnsureCapacity(m_Children.ItemCount()); for (AP4_Ordinal i=0; i<m_Children.ItemCount(); i++) { m_SampleDescriptions.Append(NULL); } }
/*---------------------------------------------------------------------- | AP4_IsmaDecryptingProcessor:CreateTrackHandler +---------------------------------------------------------------------*/ AP4_Processor::TrackHandler* AP4_IsmaDecryptingProcessor::CreateTrackHandler(AP4_TrakAtom* trak) { // find the stsd atom AP4_StsdAtom* stsd = dynamic_cast<AP4_StsdAtom*>( trak->FindChild("mdia/minf/stbl/stsd")); // avoid tracks with no stsd atom (should not happen) if (stsd == NULL) return NULL; // we only look at the first sample description AP4_SampleDescription* desc = stsd->GetSampleDescription(0); AP4_SampleEntry* entry = stsd->GetSampleEntry(0); if (desc == NULL || entry == NULL) return NULL; if (desc->GetType() == AP4_SampleDescription::TYPE_ISMACRYP) { // create a handler for this track AP4_IsmaCrypSampleDescription* ismacryp_desc = static_cast<AP4_IsmaCrypSampleDescription*>(desc); if (ismacryp_desc->GetSchemeType() == AP4_ISMACRYP_SCHEME_TYPE_IAEC) { const AP4_UI08* key; const AP4_UI08* salt; if (AP4_SUCCEEDED(m_KeyMap.GetKey(trak->GetId(), key, salt))) { return new AP4_IsmaTrackDecrypter(key, salt, ismacryp_desc, entry); } } } return NULL; }
/*---------------------------------------------------------------------- | AP4_AtomFactory::CreateAtomsFromStream +---------------------------------------------------------------------*/ AP4_Result AP4_AtomFactory::CreateAtomsFromStream(AP4_ByteStream& stream, AP4_LargeSize bytes_available, AP4_AtomParent& atoms) { AP4_Result result; do { AP4_Atom* atom = NULL; result = CreateAtomFromStream(stream, bytes_available, atom); if (AP4_SUCCEEDED(result) && atom != NULL) { atoms.AddChild(atom); } } while (AP4_SUCCEEDED(result)); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_SubStream::WritePartial +---------------------------------------------------------------------*/ AP4_Result AP4_SubStream::WritePartial(const void* buffer, AP4_Size bytes_to_write, AP4_Size& bytes_written) { // default values bytes_written = 0; // shortcut if (bytes_to_write == 0) { return AP4_SUCCESS; } // clamp to range if (m_Position+bytes_to_write > m_Size) { bytes_to_write = (AP4_Size)(m_Size - m_Position); } // check for en of substream if (bytes_to_write == 0) { return AP4_ERROR_EOS; } // seek inside container AP4_Result result; result = m_Container.Seek(m_Offset+m_Position); if (AP4_FAILED(result)) return result; // write to container result = m_Container.WritePartial(buffer, bytes_to_write, bytes_written); if (AP4_SUCCEEDED(result)) { m_Position += bytes_written; } return result; }
/*---------------------------------------------------------------------- | DumpRtpPackets +---------------------------------------------------------------------*/ static AP4_Result DumpRtpPackets(AP4_HintTrackReader& reader, const char* file_name) { // create the output stream AP4_ByteStream* output; try { output = new AP4_FileByteStream(file_name, AP4_FileByteStream::STREAM_MODE_WRITE); } catch (AP4_Exception) { fprintf(stderr, "ERROR: cannot open output file (%s)\n", file_name); return AP4_FAILURE; } // read the packets from the reader and write them in the output stream AP4_DataBuffer data(1500); AP4_TimeStamp ts; while(AP4_SUCCEEDED(reader.GetNextPacket(data, ts))) { output->Write(data.GetData(), data.GetDataSize()); AP4_Debug("#########\n\tpacket contains %d bytes\n", data.GetDataSize()); AP4_Debug("\tsent at time stamp %d\n\n", ts); } output->Release(); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_DefaultAtomFactory::Initialize +---------------------------------------------------------------------*/ AP4_Result AP4_DefaultAtomFactory::Initialize() { // register built-in type handlers AP4_Result result = AddTypeHandler(new AP4_MetaDataAtomTypeHandler(this)); if (AP4_SUCCEEDED(result)) m_Initialized = true; return result; }
virtual AP4_Result GetSample(AP4_Ordinal index, AP4_Sample& sample) { AP4_Result result = m_Track->GetSample(index, sample); if (AP4_SUCCEEDED(result)) { if (m_ForcedSync[index]) { sample.SetSync(true); } } return result; }
/*---------------------------------------------------------------------- | AP4_TrefTypeAtom::AddTrackId +---------------------------------------------------------------------*/ AP4_Result AP4_TrefTypeAtom::AddTrackId(AP4_UI32 track_id) { AP4_Result result = m_TrackIds.Append(track_id); if (AP4_SUCCEEDED(result)) { m_Size32 += 4; } return result; }
/*---------------------------------------------------------------------- | AP4_OmaDcfAtomDecrypter::DecryptAtoms +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfAtomDecrypter::DecryptAtoms(AP4_AtomParent& atoms, AP4_Processor::ProgressListener* /*listener*/, AP4_BlockCipherFactory* block_cipher_factory, AP4_ProtectionKeyMap& key_map) { // default factory if (block_cipher_factory == NULL) { block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance; } unsigned int index = 1; for (AP4_List<AP4_Atom>::Item* item = atoms.GetChildren().FirstItem(); item; item = item->GetNext()) { AP4_Atom* atom = item->GetData(); if (atom->GetType() != AP4_ATOM_TYPE_ODRM) continue; // check that we have the key const AP4_DataBuffer* key = key_map.GetKey(index++); if (key == NULL) return AP4_ERROR_INVALID_PARAMETERS; // check that we have all the atoms we need AP4_ContainerAtom* odrm = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); if (odrm == NULL) continue; // not enough info AP4_OdheAtom* odhe = AP4_DYNAMIC_CAST(AP4_OdheAtom, odrm->GetChild(AP4_ATOM_TYPE_ODHE)); if (odhe == NULL) continue; // not enough info AP4_OddaAtom* odda = AP4_DYNAMIC_CAST(AP4_OddaAtom, odrm->GetChild(AP4_ATOM_TYPE_ODDA));; if (odda == NULL) continue; // not enough info AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, odhe->GetChild(AP4_ATOM_TYPE_OHDR)); if (ohdr == NULL) continue; // not enough info // do nothing if the atom is not encrypted if (ohdr->GetEncryptionMethod() == AP4_OMA_DCF_ENCRYPTION_METHOD_NULL) { continue; } // create the byte stream AP4_ByteStream* cipher_stream = NULL; AP4_Result result = CreateDecryptingStream(*odrm, key->GetData(), key->GetDataSize(), block_cipher_factory, cipher_stream); if (AP4_SUCCEEDED(result)) { // replace the odda atom's payload with the decrypted stream odda->SetEncryptedPayload(*cipher_stream, ohdr->GetPlaintextLength()); cipher_stream->Release(); // the atom will now be in the clear ohdr->SetEncryptionMethod(AP4_OMA_DCF_ENCRYPTION_METHOD_NULL); ohdr->SetPaddingScheme(AP4_OMA_DCF_PADDING_SCHEME_NONE); } } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_KodiFileByteStream::Flush +---------------------------------------------------------------------*/ AP4_Result AP4_KodiFileByteStream::Flush() { int ret_val = 0; xbmc->FlushFile(m_File); AP4_Result result((ret_val > 0) ? AP4_FAILURE : AP4_SUCCESS); if (AP4_SUCCEEDED(result) && GetObserver()) return GetObserver()->OnFlush(this); return result; }
/*---------------------------------------------------------------------- | AP4_Movie::GetTrack +---------------------------------------------------------------------*/ AP4_Track* AP4_Movie::GetTrack(AP4_Track::Type track_type, AP4_Ordinal index) { AP4_Track* track = NULL; if (AP4_SUCCEEDED(m_Tracks.Find(AP4_TrackFinderByType(track_type, index), track))) { return track; } else { return NULL; } }
/*---------------------------------------------------------------------- | AP4_Movie::GetTrack +---------------------------------------------------------------------*/ AP4_Track* AP4_Movie::GetTrack(AP4_UI32 track_id) { AP4_Track* track = NULL; if (AP4_SUCCEEDED(m_Tracks.Find(AP4_TrackFinderById(track_id), track))) { return track; } else { return NULL; } }
/*---------------------------------------------------------------------- | AP4_SyntheticSampleTable::GetSampleDescription +---------------------------------------------------------------------*/ AP4_SampleDescription* AP4_SyntheticSampleTable::GetSampleDescription(AP4_Ordinal index) { SampleDescriptionHolder* holder; if (AP4_SUCCEEDED(m_SampleDescriptions.Get(index, holder))) { return holder->m_SampleDescription; } else { return NULL; } }
/*---------------------------------------------------------------------- | AP4_SyntheticSampleTable::GetSampleDescription +---------------------------------------------------------------------*/ AP4_SampleDescription* AP4_SyntheticSampleTable::GetSampleDescription(AP4_Ordinal index) { AP4_SampleDescription* description; if (AP4_SUCCEEDED(m_SampleDescriptions.Get(index, description))) { return description; } else { return NULL; } }
/*---------------------------------------------------------------------- | AP4_SubStream::Seek +---------------------------------------------------------------------*/ AP4_Result AP4_SubStream::Seek(AP4_Offset offset) { if (offset > m_Size) return AP4_FAILURE; AP4_Result result; result = m_Container.Seek(m_Offset+offset); if (AP4_SUCCEEDED(result)) { m_Position = offset; } return result; }
/*---------------------------------------------------------------------- | AP4_TrackSampleSource::ReadNextSample +---------------------------------------------------------------------*/ AP4_Result AP4_TrackSampleSource::ReadNextSample(AP4_Sample& sample, AP4_DataBuffer& buffer, AP4_UI32& track_id) { AP4_Result result = m_Track->ReadSample(m_SampleIndex, sample, buffer); if (AP4_SUCCEEDED(result)) { ++m_SampleIndex; track_id = m_Track->GetId(); } else { track_id = 0; } return result; }
/*---------------------------------------------------------------------- | AP4_ContainerAtom::ReadChildren +---------------------------------------------------------------------*/ void AP4_ContainerAtom::ReadChildren(AP4_AtomFactory& atom_factory, AP4_ByteStream& stream, AP4_Size size) { AP4_Atom* atom; AP4_Size bytes_available = size; while (AP4_SUCCEEDED( atom_factory.CreateAtomFromStream(stream, bytes_available, atom, this))) { atom->SetParent(this); m_Children.Add(atom); } }
/*---------------------------------------------------------------------- | AutoDetectAudioFragmentDuration +---------------------------------------------------------------------*/ static unsigned int AutoDetectAudioFragmentDuration(AP4_ByteStream& stream, TrackCursor* cursor) { // remember where we are in the stream AP4_Position where = 0; stream.Tell(where); AP4_LargeSize stream_size = 0; stream.GetSize(stream_size); AP4_LargeSize bytes_available = stream_size-where; AP4_UI64 fragment_count = 0; AP4_UI32 last_fragment_size = 0; AP4_Atom* atom = NULL; while (AP4_SUCCEEDED(AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(stream, bytes_available, atom))) { if (atom && atom->GetType() == AP4_ATOM_TYPE_MOOF) { AP4_ContainerAtom* moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom); AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, moof->FindChild("traf/tfhd")); if (tfhd && tfhd->GetTrackId() == cursor->m_Track->GetId()) { ++fragment_count; AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, moof->FindChild("traf/trun")); if (trun) { last_fragment_size = trun->GetEntries().ItemCount(); } } } delete atom; atom = NULL; } // restore the stream to its original position stream.Seek(where); // decide if we can infer an fragment size if (fragment_count == 0 || cursor->m_Samples->GetSampleCount() == 0) { return 0; } // don't count the last fragment if we have more than one if (fragment_count > 1 && last_fragment_size) { --fragment_count; } if (fragment_count <= 1 || cursor->m_Samples->GetSampleCount() < last_fragment_size) { last_fragment_size = 0; } AP4_Sample sample; AP4_UI64 total_duration = 0; for (unsigned int i=0; i<cursor->m_Samples->GetSampleCount()-last_fragment_size; i++) { cursor->m_Samples->GetSample(i, sample); total_duration += sample.GetDuration(); } return (unsigned int)AP4_ConvertTime(total_duration/fragment_count, cursor->m_Track->GetMediaTimeScale(), 1000); }
AP4_ByteStream* AP4_FileByteStream_Create(const char* name, int mode, AP4_Result* result) { AP4_Result local_result; AP4_ByteStream* stream; local_result = AP4_FileByteStream::Create(name, (AP4_FileByteStream::Mode)mode, stream); if (result) *result = local_result; if (AP4_SUCCEEDED(local_result)) { return stream; } else { return NULL; } }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpEncryptingProcessor::CreateTrackHandler +---------------------------------------------------------------------*/ AP4_Processor::TrackHandler* AP4_MarlinIpmpEncryptingProcessor::CreateTrackHandler(AP4_TrakAtom* trak) { // create a handler for this track if we have a key for it const AP4_UI08* key; const AP4_UI08* iv; if (AP4_SUCCEEDED(m_KeyMap.GetKeyAndIv(trak->GetId(), key, iv))) { // create the track handler AP4_MarlinIpmpTrackEncrypter* handler = NULL; AP4_Result result = AP4_MarlinIpmpTrackEncrypter::Create(*m_BlockCipherFactory, key, iv, handler); if (AP4_FAILED(result)) return NULL; return handler; } // not encrypted return NULL; }
/*---------------------------------------------------------------------- | AP4_Track::Clone +---------------------------------------------------------------------*/ AP4_Track* AP4_Track::Clone(AP4_Result* result) { AP4_SyntheticSampleTable* sample_table = new AP4_SyntheticSampleTable(); // default return value if (result) *result = AP4_SUCCESS; // add clones of the sample descriptions to the new sample table for (unsigned int i=0; ;i++) { AP4_SampleDescription* sample_description = GetSampleDescription(i); if (sample_description == NULL) break; sample_table->AddSampleDescription(sample_description->Clone()); } AP4_Sample sample; AP4_Ordinal index = 0; while (AP4_SUCCEEDED(GetSample(index, sample))) { AP4_ByteStream* data_stream; data_stream = sample.GetDataStream(); sample_table->AddSample(*data_stream, sample.GetOffset(), sample.GetSize(), sample.GetDuration(), sample.GetDescriptionIndex(), sample.GetDts(), sample.GetCtsDelta(), sample.IsSync()); AP4_RELEASE(data_stream); // release our ref, the table has kept its own ref. index++; } // create the cloned track AP4_Track* clone = new AP4_Track(GetType(), sample_table, GetId(), GetMovieTimeScale(), GetDuration(), GetMediaTimeScale(), GetMediaDuration(), GetTrackLanguage(), GetWidth(), GetHeight()); return clone; }
/*---------------------------------------------------------------------- | ProcessMoof +---------------------------------------------------------------------*/ static int ProcessMoof(AP4_Movie* movie, AP4_ContainerAtom* moof, AP4_ByteStream* sample_stream, AP4_Position moof_offset, AP4_Position mdat_payload_offset) { AP4_Result result; AP4_MovieFragment* fragment = new AP4_MovieFragment(moof); printf("fragment sequence number=%d\n", fragment->GetSequenceNumber()); AP4_FragmentSampleTable* sample_table = NULL; // get all track IDs in this fragment AP4_Array<AP4_UI32> ids; fragment->GetTrackIds(ids); printf("Found %d tracks in fragment: ", ids.ItemCount()); for (unsigned int i=0; i<ids.ItemCount(); i++) { printf("%d ", ids[i]); } printf("\n"); for (unsigned int i=0; i<ids.ItemCount(); i++) { AP4_Track* track = NULL; if (movie) { track = movie->GetTrack(ids[i]); } AP4_ContainerAtom* traf = NULL; fragment->GetTrafAtom(ids[i], traf); printf("processing moof for track id %d\n", ids[i]); result = fragment->CreateSampleTable(movie, ids[i], sample_stream, moof_offset, mdat_payload_offset, sample_table); CHECK(result == AP4_SUCCESS || result == AP4_ERROR_NO_SUCH_ITEM); if (AP4_SUCCEEDED(result) ) { ProcessSamples(track, traf, sample_table); delete sample_table; } else { printf("no sample table for this track\n"); } } delete fragment; return 0; }
/*---------------------------------------------------------------------- | AP4_LinearReader::PopSample +---------------------------------------------------------------------*/ bool AP4_LinearReader::PopSample(Tracker* tracker, AP4_Sample& sample, AP4_DataBuffer& sample_data) { SampleBuffer* head = NULL; if (AP4_SUCCEEDED(tracker->m_Samples.PopHead(head))) { assert(head->m_Sample); sample = *head->m_Sample; sample_data.SetData(head->m_Data.GetData(), head->m_Data.GetDataSize()); assert(m_BufferFullness >= sample.GetSize()); m_BufferFullness -= sample.GetSize(); delete head; return true; } return false; }
/*---------------------------------------------------------------------- | AP4_MemoryByteStream::WritePartial +---------------------------------------------------------------------*/ AP4_Result AP4_MemoryByteStream::WritePartial(const void* buffer, AP4_Size bytes_to_write, AP4_Size& bytes_written) { // default values bytes_written = 0; // shortcut if (bytes_to_write == 0) { return AP4_SUCCESS; } // check that we don't exceed the max if (m_Position+bytes_to_write > (AP4_Position)AP4_MEMORY_BYTE_STREAM_MAX_SIZE) { return AP4_ERROR_OUT_OF_RANGE; } // reserve space in the buffer AP4_Result result = m_Buffer->Reserve((AP4_Size)(m_Position+bytes_to_write)); if (AP4_SUCCEEDED(result)) { m_Buffer->SetDataSize((AP4_Size)(m_Position+bytes_to_write)); } else { // failed to reserve, most likely caused by a buffer that has // external storage if (m_Position+bytes_to_write > m_Buffer->GetDataSize()) { bytes_to_write = (AP4_Size)(m_Buffer->GetDataSize() - m_Position); } } // check for en of stream if (bytes_to_write == 0) { return AP4_ERROR_EOS; } // write to memory AP4_CopyMemory((void*)(m_Buffer->UseData()+m_Position), buffer, bytes_to_write); m_Position += bytes_to_write; bytes_written = bytes_to_write; return AP4_SUCCESS; }