/*---------------------------------------------------------------------- | AP4_StscAtom::AP4_StscAtom +---------------------------------------------------------------------*/ AP4_StscAtom::AP4_StscAtom(AP4_UI32 size, AP4_UI32 version, AP4_UI32 flags, AP4_ByteStream& stream) : AP4_Atom(AP4_ATOM_TYPE_STSC, size, version, flags), m_CachedChunkGroup(0) { AP4_UI32 first_sample = 1; AP4_UI32 entry_count; stream.ReadUI32(entry_count); m_Entries.SetItemCount(entry_count); unsigned char* buffer = new unsigned char[entry_count*12]; AP4_Result result = stream.Read(buffer, entry_count*12); if (AP4_FAILED(result)) { delete[] buffer; return; } for (unsigned int i=0; i<entry_count; i++) { AP4_UI32 first_chunk = AP4_BytesToUInt32BE(&buffer[i*12 ]); AP4_UI32 samples_per_chunk = AP4_BytesToUInt32BE(&buffer[i*12+4]); AP4_UI32 sample_description_index = AP4_BytesToUInt32BE(&buffer[i*12+8]); if (i) { AP4_Ordinal prev = i-1; m_Entries[prev].m_ChunkCount = first_chunk-m_Entries[prev].m_FirstChunk; first_sample += m_Entries[prev].m_ChunkCount * m_Entries[prev].m_SamplesPerChunk; } m_Entries[i].m_ChunkCount = 0; // not known yet m_Entries[i].m_FirstChunk = first_chunk; m_Entries[i].m_FirstSample = first_sample; m_Entries[i].m_SamplesPerChunk = samples_per_chunk; m_Entries[i].m_SampleDescriptionIndex = sample_description_index; } delete[] buffer; }
/*---------------------------------------------------------------------- | AP4_StssAtom::AP4_StssAtom +---------------------------------------------------------------------*/ AP4_StssAtom::AP4_StssAtom(AP4_UI32 size, AP4_UI08 version, AP4_UI32 flags, AP4_ByteStream& stream) : AP4_Atom(AP4_ATOM_TYPE_STSS, size, version, flags), m_LookupCache(0) { AP4_UI32 entry_count; stream.ReadUI32(entry_count); // check for bogus values if (entry_count*4 > size) return; // read the table into a local array for conversion unsigned char* buffer = new unsigned char[entry_count*4]; AP4_Result result = stream.Read(buffer, entry_count*4); if (AP4_FAILED(result)) { delete[] buffer; return; } m_Entries.SetItemCount(entry_count); for (unsigned int i=0; i<entry_count; i++) { m_Entries[i] = AP4_BytesToUInt32BE(&buffer[i*4]); } delete[] buffer; }
/*---------------------------------------------------------------------- | IsIFrame +---------------------------------------------------------------------*/ static bool IsIFrame(AP4_Sample& sample, AP4_AvcSampleDescription* avc_desc) { AP4_DataBuffer sample_data; if (AP4_FAILED(sample.ReadData(sample_data))) { return false; } const unsigned char* data = sample_data.GetData(); AP4_Size size = sample_data.GetDataSize(); while (size >= avc_desc->GetNaluLengthSize()) { unsigned int nalu_length = 0; if (avc_desc->GetNaluLengthSize() == 1) { nalu_length = *data++; --size; } else if (avc_desc->GetNaluLengthSize() == 2) { nalu_length = AP4_BytesToUInt16BE(data); data += 2; size -= 2; } else if (avc_desc->GetNaluLengthSize() == 4) { nalu_length = AP4_BytesToUInt32BE(data); data += 4; size -= 4; } else { return false; } if (nalu_length <= size) { size -= nalu_length; } else { size = 0; } switch (*data & 0x1F) { case 1: { AP4_BitStream bits; bits.WriteBytes(data+1, 8); ReadGolomb(bits); unsigned int slice_type = ReadGolomb(bits); if (slice_type == 2 || slice_type == 7) { return true; } else { return false; // only show first slice type } } case 5: return true; } data += nalu_length; } return false; }
/*---------------------------------------------------------------------- | AP4_CttsAtom::AP4_CttsAtom +---------------------------------------------------------------------*/ AP4_CttsAtom::AP4_CttsAtom(AP4_UI32 size, AP4_UI08 version, AP4_UI32 flags, AP4_ByteStream& stream) : AP4_Atom(AP4_ATOM_TYPE_CTTS, size, version, flags) { m_LookupCache.sample = 0; m_LookupCache.entry_index = 0; AP4_UI32 entry_count; stream.ReadUI32(entry_count); m_Entries.SetItemCount(entry_count); unsigned char* buffer = new unsigned char[entry_count*8]; AP4_Result result = stream.Read(buffer, entry_count*8); if (AP4_FAILED(result)) { delete[] buffer; return; } //bool use_quicktime_format = false; //AP4_SI32 quicktime_min_offset = 0; for (unsigned i=0; i<entry_count; i++) { m_Entries[i].m_SampleCount = AP4_BytesToUInt32BE(&buffer[i*8 ]); AP4_UI32 offset = AP4_BytesToUInt32BE(&buffer[i*8+4]); //if (offset & 0x80000000) { // use_quicktime_format = true; // AP4_SI32 noffset = (AP4_SI32)offset; // if (noffset < quicktime_min_offset) quicktime_min_offset = noffset; //} m_Entries[i].m_SampleOffset = offset; } delete[] buffer; // in the quicktime format, the offsets can be positive or negative, so // we need to adjust for them here //if (use_quicktime_format) { // for (unsigned i=0; i<entry_count; i++) { // m_Entries[i].m_SampleOffset -= quicktime_min_offset; // } //} }
/*---------------------------------------------------------------------- | AP4_ByteStream::ReadUI32 +---------------------------------------------------------------------*/ AP4_Result AP4_ByteStream::ReadUI32(AP4_UI32& value) { unsigned char buffer[4]; // read bytes from the stream AP4_Result result; result = Read((void*)buffer, 4); if (AP4_FAILED(result)) { value = 0; return result; } // convert bytes to value value = AP4_BytesToUInt32BE(buffer); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_StcoAtom::AP4_StcoAtom +---------------------------------------------------------------------*/ AP4_StcoAtom::AP4_StcoAtom(AP4_UI32 size, AP4_UI32 version, AP4_UI32 flags, AP4_ByteStream& stream) : AP4_Atom(AP4_ATOM_TYPE_STCO, size, version, flags) { stream.ReadUI32(m_EntryCount); if (m_EntryCount > (size-AP4_FULL_ATOM_HEADER_SIZE-4)/4) { m_EntryCount = (size-AP4_FULL_ATOM_HEADER_SIZE-4)/4; } m_Entries = new AP4_UI32[m_EntryCount]; unsigned char* buffer = new unsigned char[m_EntryCount*4]; AP4_Result result = stream.Read(buffer, m_EntryCount*4); if (AP4_FAILED(result)) { delete[] buffer; return; } for (AP4_Ordinal i=0; i<m_EntryCount; i++) { m_Entries[i] = AP4_BytesToUInt32BE(&buffer[i*4]); } delete[] buffer; }
/*---------------------------------------------------------------------- | AP4_LinearReader::SeekTo +---------------------------------------------------------------------*/ AP4_Result AP4_LinearReader::SeekTo(AP4_UI32 time_ms, AP4_UI32* actual_time_ms) { if (actual_time_ms) *actual_time_ms = time_ms; // default // we only support fragmented sources for now if (!m_HasFragments) return AP4_ERROR_NOT_SUPPORTED; // look for a fragment index if (m_Mfra == NULL) { if (m_FragmentStream) { // get the size of the stream (needed) AP4_LargeSize stream_size = 0; m_FragmentStream->GetSize(stream_size); if (stream_size > 12) { // remember where we are AP4_Position here; m_FragmentStream->Tell(here); // read the last 12 bytes unsigned char mfro[12]; AP4_Result result = m_FragmentStream->Seek(stream_size-12); if (AP4_SUCCEEDED(result)) { result = m_FragmentStream->Read(mfro, 12); } if (AP4_SUCCEEDED(result) && mfro[0] == 'm' && mfro[1] == 'f' && mfro[2] == 'r' && mfro[3] == 'o') { AP4_UI32 mfra_size = AP4_BytesToUInt32BE(&mfro[8]); if ((AP4_LargeSize)mfra_size < stream_size) { result = m_FragmentStream->Seek(stream_size-mfra_size); if (AP4_SUCCEEDED(result)) { AP4_Atom* mfra = NULL; AP4_LargeSize available = mfra_size; AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(*m_FragmentStream, available, mfra); m_Mfra = AP4_DYNAMIC_CAST(AP4_ContainerAtom, mfra); } } } if (AP4_SUCCEEDED(result)) { result = m_FragmentStream->Seek(here); } } } } // return now if we have not found an index if (m_Mfra == NULL) { return AP4_ERROR_NOT_SUPPORTED; } // look for the earliest fragment referenced by an entry with the largest timestamp that's // before or equal to the requested time int best_entry = -1; for (unsigned t=0; t<m_Trackers.ItemCount(); t++) { // find the tfra index for this track AP4_TfraAtom* tfra = NULL; for (AP4_List<AP4_Atom>::Item* item = m_Mfra->GetChildren().FirstItem(); item; item = item->GetNext()) { if (item->GetData()->GetType() == AP4_ATOM_TYPE_TFRA) { AP4_TfraAtom* tfra_ = (AP4_TfraAtom*)item->GetData(); if (tfra_->GetTrackId() == m_Trackers[t]->m_Track->GetId()) { tfra = tfra_; break; } } } if (tfra == NULL) { return AP4_ERROR_NOT_SUPPORTED; } AP4_Array<AP4_TfraAtom::Entry>& entries = tfra->GetEntries(); AP4_UI64 media_time = AP4_ConvertTime(time_ms, 1000, m_Trackers[t]->m_Track->GetMediaTimeScale()); int entry = -1; for (int i=0; i<(int)entries.ItemCount(); i++) { if (entries[i].m_Time > media_time) break; entry = i; } if (entry >= 0) { if (best_entry == -1) { best_entry = entry; } else if (entries[entry].m_MoofOffset < entries[best_entry].m_MoofOffset) { best_entry = entry; } // update our position if (best_entry >= 0) { if (actual_time_ms) { // report the actual time we found (in milliseconds) *actual_time_ms = (AP4_UI32)AP4_ConvertTime(entries[best_entry].m_Time, m_Trackers[t]->m_Track->GetMediaTimeScale(), 1000); } m_NextFragmentPosition = entries[best_entry].m_MoofOffset; } } } // check that we found something if (best_entry == -1) { return AP4_FAILURE; } // flush any queued samples FlushQueues(); // reset tracker states for (unsigned int i=0; i<m_Trackers.ItemCount(); i++) { delete m_Trackers[i]->m_SampleTable; delete m_Trackers[i]->m_NextSample; m_Trackers[i]->m_SampleTable = NULL; m_Trackers[i]->m_NextSample = NULL; m_Trackers[i]->m_NextSampleIndex = 0; m_Trackers[i]->m_Eos = false; } return AP4_SUCCESS; }