/*----------------------------------------------------------------------
|   AP4_FragmentSampleTable::AP4_FragmentSampleTable
+---------------------------------------------------------------------*/
AP4_FragmentSampleTable::AP4_FragmentSampleTable(AP4_ContainerAtom* traf,
        AP4_TrexAtom*      trex,
        AP4_Cardinal       internal_track_id,
        AP4_ByteStream*    sample_stream,
        AP4_Position       moof_offset,
        AP4_Position       mdat_payload_offset,
        AP4_UI64           dts_origin)
    :m_InternalTrackId(internal_track_id)
{
    AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
    if (tfhd == NULL) return;

    // count all the samples and reserve space for them
    unsigned int sample_count = 0;
    for (AP4_List<AP4_Atom>::Item* item = traf->GetChildren().FirstItem();
            item;
            item = item->GetNext()) {
        AP4_Atom* atom = item->GetData();
        if (atom->GetType() == AP4_ATOM_TYPE_TRUN) {
            AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom);
            if (trun) sample_count += trun->GetEntries().ItemCount();
        }
    }
    m_Samples.EnsureCapacity(sample_count);

    // check if we have a timecode base
    AP4_TfdtAtom* tfdt = AP4_DYNAMIC_CAST(AP4_TfdtAtom, traf->GetChild(AP4_ATOM_TYPE_TFDT));
    if (tfdt) {
        dts_origin = tfdt->GetBaseMediaDecodeTime();
    }

    // process all the trun atoms
    for (AP4_List<AP4_Atom>::Item* item = traf->GetChildren().FirstItem();
            item;
            item = item->GetNext()) {
        AP4_Atom* atom = item->GetData();
        if (atom->GetType() == AP4_ATOM_TYPE_TRUN) {
            AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, atom);
            if (trun) {
                AP4_Result result = AddTrun(trun,
                                            tfhd,
                                            trex,
                                            sample_stream,
                                            moof_offset,
                                            mdat_payload_offset,
                                            dts_origin);
                if (AP4_FAILED(result)) return;
            }
        }
    }
}
/*----------------------------------------------------------------------
|   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_Processor::ProcessFragments
+---------------------------------------------------------------------*/
AP4_Result
AP4_Processor::ProcessFragment( AP4_ContainerAtom*		    moof,
                                AP4_SidxAtom*			    sidx,
                                AP4_Position			    sidx_position,
                                AP4_ByteStream&			    output,
								AP4_Array<AP4_Position>&    moof_positions,
								AP4_Array<AP4_Position>&    mdat_positions)
{
	unsigned int fragment_index = 0;
    
    //AP4_UI64           mdat_payload_offset = atom_offset+atom->GetSize()+AP4_ATOM_HEADER_SIZE;
    AP4_Sample         sample;
    AP4_DataBuffer     sample_data_in;
    AP4_DataBuffer     sample_data_out;
    AP4_Result         result = AP4_SUCCESS;
        
    // parse the moof
    //AP4_MovieFragment* fragment = new AP4_MovieFragment(moof);

    // process all the traf atoms
    AP4_Array<AP4_Processor::FragmentHandler*> handlers;
    AP4_Array<AP4_FragmentSampleTable*> sample_tables;

	for (; AP4_Atom* child = moof->GetChild(AP4_ATOM_TYPE_TRAF, handlers.ItemCount());) {
		AP4_TrafAtom* traf = AP4_DYNAMIC_CAST(AP4_TrafAtom, child);

		PERTRACK &track_data(m_TrackData[traf->GetInternalTrackId()]);
		AP4_TrakAtom* trak = track_data.track_handler->GetTrakAtom();
		AP4_TrexAtom* trex = track_data.track_handler->GetTrexAtom();

		// create the handler for this traf
		AP4_Processor::FragmentHandler* handler = CreateFragmentHandler(trak, trex, traf,
			*(m_StreamData[track_data.streamId].stream),
			moof_positions[track_data.streamId]);
		if (handler) {
			result = handler->ProcessFragment();
			if (AP4_FAILED(result)) return result;
		}
		handlers.Append(handler);

		// create a sample table object so we can read the sample data
		AP4_FragmentSampleTable* sample_table = new AP4_FragmentSampleTable(
			traf,
			trex,
      traf->GetInternalTrackId(),
			m_StreamData[track_data.streamId].stream,
			moof_positions[traf->GetInternalTrackId()],
			mdat_positions[traf->GetInternalTrackId()],
			0);
		sample_tables.Append(sample_table);

		// let the handler look at the samples before we process them
		if (handler)
			result = handler->PrepareForSamples(sample_table);
		if (AP4_FAILED(result))
			return result;
	}
             
	output.Buffer();
		
	// write the moof
    AP4_UI64 moof_out_start = 0;
    output.Tell(moof_out_start);
    moof->Write(output);
        
    // remember the location of this fragment
	FragmentMapEntry map_entry = { moof_positions[0], moof_out_start };
    fragment_map_.Append(map_entry);

    // write an mdat header
    AP4_Position mdat_out_start;
    AP4_UI64 mdat_size = AP4_ATOM_HEADER_SIZE;
    output.Tell(mdat_out_start);
    output.WriteUI32(0);
    output.WriteUI32(AP4_ATOM_TYPE_MDAT);

    // process all track runs
    for (unsigned int i=0; i<handlers.ItemCount(); i++) {
        AP4_Processor::FragmentHandler* handler = handlers[i];

        // get the track ID
        AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof->GetChild(AP4_ATOM_TYPE_TRAF, i));
        if (traf == NULL) continue;
        AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
            
        // compute the base data offset
        AP4_UI64 base_data_offset;
        if (tfhd->GetFlags() & AP4_TFHD_FLAG_BASE_DATA_OFFSET_PRESENT) {
            base_data_offset = mdat_out_start+AP4_ATOM_HEADER_SIZE;
        } else {
            base_data_offset = moof_out_start;
        }
            
        // build a list of all trun atoms
        AP4_Array<AP4_TrunAtom*> truns;
        for (AP4_List<AP4_Atom>::Item* child_item = traf->GetChildren().FirstItem();
                                        child_item;
                                        child_item = child_item->GetNext()) {
            AP4_Atom* child_atom = child_item->GetData();
            if (child_atom->GetType() == AP4_ATOM_TYPE_TRUN) {
                AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, child_atom);
                truns.Append(trun);
            }
        }    
        AP4_Ordinal   trun_index        = 0;
        AP4_Ordinal   trun_sample_index = 0;
        AP4_TrunAtom* trun = truns[0];
        trun->SetDataOffset((AP4_SI32)((mdat_out_start+mdat_size)-base_data_offset));
            
        // write the mdat
        for (unsigned int j=0; j<sample_tables[i]->GetSampleCount(); j++, trun_sample_index++) {
            // advance the trun index if necessary
            if (trun_sample_index >= trun->GetEntries().ItemCount()) {
                trun = truns[++trun_index];
                trun->SetDataOffset((AP4_SI32)((mdat_out_start+mdat_size)-base_data_offset));
                trun_sample_index = 0;
            }
                
            // get the next sample
            result = sample_tables[i]->GetSample(j, sample);
            if (AP4_FAILED(result)) return result;
            sample.ReadData(sample_data_in);
                
            m_TrackData[sample_tables[i]->GetInteralTrackId()].dts = sample.GetDts();
            
            // process the sample data
            if (handler) {
                result = handler->ProcessSample(sample_data_in, sample_data_out);
                if (AP4_FAILED(result)) return result;

                // write the sample data
                result = output.Write(sample_data_out.GetData(), sample_data_out.GetDataSize());
                if (AP4_FAILED(result)) return result;

                // update the mdat size
                mdat_size += sample_data_out.GetDataSize();
                    
                // update the trun entry
                trun->UseEntries()[trun_sample_index].sample_size = sample_data_out.GetDataSize();
            } else {
                // write the sample data (unmodified)
                result = output.Write(sample_data_in.GetData(), sample_data_in.GetDataSize());
                if (AP4_FAILED(result)) return result;

                // update the mdat size
                mdat_size += sample_data_in.GetDataSize();
            }
        }

        if (handler) {
            // update the tfhd header
            if (tfhd->GetFlags() & AP4_TFHD_FLAG_BASE_DATA_OFFSET_PRESENT) {
                tfhd->SetBaseDataOffset(mdat_out_start+AP4_ATOM_HEADER_SIZE);
            }
            if (tfhd->GetFlags() & AP4_TFHD_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT) {
                tfhd->SetDefaultSampleSize(trun->GetEntries()[0].sample_size);
            }
                
            // give the handler a chance to update the atoms
            handler->FinishFragment();
        }
    }

    // update the mdat header
    AP4_Position mdat_out_end;
    output.Tell(mdat_out_end);
#if defined(AP4_DEBUG)
    AP4_ASSERT(mdat_out_end-mdat_out_start == mdat_size);
#endif
	if (AP4_FAILED(result = output.Seek(mdat_out_start)))
		return result;
    output.WriteUI32((AP4_UI32)mdat_size);
    output.Seek(mdat_out_end);
        
    // update the moof if needed
	if (AP4_FAILED(result = output.Seek(moof_out_start)))
		return result;
    moof->Write(output);
    output.Seek(mdat_out_end);
        
    // update the sidx if we have one
    if (sidx && fragment_index < sidx->GetReferences().ItemCount()) {
        if (fragment_index == 0) {
            sidx->SetFirstOffset(moof_out_start-(sidx_position+sidx->GetSize()));
        }
        AP4_LargeSize fragment_size = mdat_out_end-moof_out_start;
        AP4_SidxAtom::Reference& sidx_ref = sidx->UseReferences()[fragment_index];
        sidx_ref.m_ReferencedSize = (AP4_UI32)fragment_size;
    }
        
    // cleanup
    //delete fragment;
        
    for (unsigned int i=0; i<handlers.ItemCount(); i++) {
        delete handlers[i];
    }
    for (unsigned int i=0; i<sample_tables.ItemCount(); i++) {
        delete sample_tables[i];
    }
	if (AP4_FAILED(result = output.Flush()))
		return result;
    
    return AP4_SUCCESS;
}
Beispiel #4
0
/*----------------------------------------------------------------------
|   AP4_Movie::ProcessMoof
+---------------------------------------------------------------------*/
void
AP4_Movie::ProcessMoof(AP4_ContainerAtom* moof, AP4_ByteStream& stream)
{
	if (moof) {
		AP4_Offset offset = 0;
		stream.Tell(offset);
		AP4_Offset moof_offset = offset - moof->GetSize();
		AP4_Offset mdat_payload_offset = offset + 8;

		AP4_MfhdAtom* mfhd = AP4_DYNAMIC_CAST(AP4_MfhdAtom, moof->GetChild(AP4_ATOM_TYPE_MFHD));
		if (mfhd) {
			for (AP4_List<AP4_Atom>::Item* item = moof->GetChildren().FirstItem();
										   item;
										   item = item->GetNext()) {
				AP4_Atom* atom = item->GetData();
				if (atom->GetType() == AP4_ATOM_TYPE_TRAF) {
					AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
					if (traf) {
						AP4_TfhdAtom* tfhd = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD));
						if (!tfhd) {
							continue;
						}
						AP4_Track* track = GetTrack(tfhd->GetTrackId());
						if (!track) {
							continue;
						}

						AP4_TfdtAtom* tfdt = AP4_DYNAMIC_CAST(AP4_TfdtAtom, traf->GetChild(AP4_ATOM_TYPE_TFDT));

						AP4_TrexAtom*      trex = NULL;
						AP4_ContainerAtom* mvex = AP4_DYNAMIC_CAST(AP4_ContainerAtom, m_MoovAtom->GetChild(AP4_ATOM_TYPE_MVEX));
						if (mvex) {
							for (AP4_List<AP4_Atom>::Item* child_item = mvex->GetChildren().FirstItem();
														   child_item;
														   child_item = child_item->GetNext()) {
								AP4_Atom* child_atom = child_item->GetData();
								if (child_atom->GetType() == AP4_ATOM_TYPE_TREX) {
									trex = AP4_DYNAMIC_CAST(AP4_TrexAtom, child_atom);
									if (trex && trex->GetTrackId() == tfhd->GetTrackId()) break;
									trex = NULL;
								}
							}
						}

						AP4_FragmentSampleTable* sampleTable = track->GetFragmentSampleTable();

						AP4_Cardinal sample_count = 0;
						for (AP4_List<AP4_Atom>::Item* child_item = traf->GetChildren().FirstItem();
													   child_item;
													   child_item = child_item->GetNext()) {
							AP4_Atom* child_atom = child_item->GetData();
							if (child_atom->GetType() == AP4_ATOM_TYPE_TRUN) {
								AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, child_atom);
								if (trun) {
									sample_count += trun->GetEntries().ItemCount();
								}
							}
						}

						if (!sample_count) {
							return;
						}

						if (sampleTable->GetSampleCount() == 0) {
							track->CreateFragmentFromStdSamples();
						}

						if (AP4_FAILED(sampleTable->EnsureCapacity(sample_count + sampleTable->GetSampleCount()))) {
							return;
						}

						AP4_UI64 dts_origin = tfdt ? tfdt->GetBaseMediaDecodeTime() : 0;
						for (AP4_List<AP4_Atom>::Item* child_item = traf->GetChildren().FirstItem();
													   child_item;
													   child_item = child_item->GetNext()) {
							AP4_Atom* child_atom = child_item->GetData();
							if (child_atom->GetType() == AP4_ATOM_TYPE_TRUN) {
								AP4_TrunAtom* trun = AP4_DYNAMIC_CAST(AP4_TrunAtom, child_atom);
								if (trun) {
									sampleTable->AddTrun(trun, tfhd, trex, stream, dts_origin, moof_offset, mdat_payload_offset);
								}
							}
						}
					}
				}
			}
		}
	}
}