Esempio n. 1
0
/*----------------------------------------------------------------------
|   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_CompactingProcessor::TrackHandler::ProcessTrack
+---------------------------------------------------------------------*/
AP4_Result
AP4_CompactingProcessor::TrackHandler::ProcessTrack()
{
    // find the stsz atom
    AP4_ContainerAtom* stbl = AP4_DYNAMIC_CAST(AP4_ContainerAtom, m_TrakAtom->FindChild("mdia/minf/stbl"));
    if (stbl == NULL) return AP4_SUCCESS;
    AP4_StszAtom* stsz = AP4_DYNAMIC_CAST(AP4_StszAtom, stbl->GetChild(AP4_ATOM_TYPE_STSZ));
    if (stsz == NULL) return AP4_SUCCESS;
    
    // check if we can reduce the size of stsz by changing it to stz2
    AP4_UI32 max_size = 0;
    for (unsigned int i=1; i<=stsz->GetSampleCount(); i++) {
        AP4_Size sample_size;
        stsz->GetSampleSize(i, sample_size);
        if (sample_size > max_size) {
            max_size = sample_size;
        }
    }
    AP4_UI08 field_size = 0;
    if (max_size <= 0xFF) {
        field_size = 1;
    } else if (max_size <= 0xFFFF) {
        field_size = 2;
    }
    if (m_Outer.m_Verbose) printf("Track %d: ", m_TrakAtom->GetId());
    if (field_size == 0) {
        if (m_Outer.m_Verbose) {
            printf("no stz2 reduction possible\n");
        }
        return AP4_SUCCESS;
    } else {
        if (m_Outer.m_Verbose) {
            unsigned int reduction = (4-field_size)*stsz->GetSampleCount();
            printf("stz2 reduction = %d bytes\n", reduction);
            m_Outer.m_SizeReduction += reduction;
        }
    }
    
    // detach the original stsz atom so we can destroy it later
    m_StszAtom = stsz;
    stsz->Detach();
    
    // create an stz2 atom and populate its entries
    AP4_Stz2Atom* stz2 = new AP4_Stz2Atom(field_size);
    for (unsigned int i=1; i<=m_StszAtom->GetSampleCount(); i++) {
        AP4_Size sample_size;
        m_StszAtom->GetSampleSize(i, sample_size);
        stz2->AddEntry(sample_size);
    }
    stbl->AddChild(stz2);
    
    return AP4_SUCCESS;
}
Esempio n. 3
0
/*----------------------------------------------------------------------
|   AP4_OmaDcfAtomDecrypter::CreateDecryptingStream
+---------------------------------------------------------------------*/
AP4_Result
AP4_OmaDcfAtomDecrypter::CreateDecryptingStream(
    AP4_ContainerAtom&      odrm,
    const AP4_UI08*         key,
    AP4_Size                key_size,
    AP4_BlockCipherFactory* block_cipher_factory,
    AP4_ByteStream*&        stream)
{
    // default return values
    stream = NULL;

    AP4_OdheAtom* odhe = AP4_DYNAMIC_CAST(AP4_OdheAtom, odrm.GetChild(AP4_ATOM_TYPE_ODHE));
    if (odhe == NULL) return AP4_ERROR_INVALID_FORMAT;
    AP4_OddaAtom* odda = AP4_DYNAMIC_CAST(AP4_OddaAtom, odrm.GetChild(AP4_ATOM_TYPE_ODDA));;
    if (odda == NULL) return AP4_ERROR_INVALID_FORMAT;
    AP4_OhdrAtom* ohdr = AP4_DYNAMIC_CAST(AP4_OhdrAtom, odhe->GetChild(AP4_ATOM_TYPE_OHDR));
    if (ohdr == NULL) return AP4_ERROR_INVALID_FORMAT;

    // default cipher factory
    if (block_cipher_factory == NULL) {
        block_cipher_factory = &AP4_DefaultBlockCipherFactory::Instance;
    }

    // shortcut for non-encrypted files
    if (ohdr->GetEncryptionMethod() == AP4_OMA_DCF_ENCRYPTION_METHOD_NULL) {
        stream = &odda->GetEncryptedPayload();
        stream->AddReference();
        return AP4_SUCCESS;
    }

    // if this is part of a group, use the group key to obtain the content
    // key (note that the field called GroupKey in the spec is actually not
    // the group key but the content key encrypted with the group key...
    AP4_GrpiAtom* grpi = AP4_DYNAMIC_CAST(AP4_GrpiAtom, ohdr->GetChild(AP4_ATOM_TYPE_GRPI));
    AP4_UI08*     key_buffer = NULL;
    if (grpi) {
        // sanity check on the encrypted key size
        if (grpi->GetGroupKey().GetDataSize() < 32) {
            return AP4_ERROR_INVALID_FORMAT;
        }

        // create a block cipher to decrypt the content key
        AP4_BlockCipher*  block_cipher =  NULL;
        AP4_Result        result;

        // create a stream cipher from the block cipher
        AP4_StreamCipher* stream_cipher = NULL;
        switch (ohdr->GetEncryptionMethod()) {
        case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC:
            result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
                     AP4_BlockCipher::DECRYPT,
                     AP4_BlockCipher::CBC,
                     NULL,
                     key,
                     key_size,
                     block_cipher);
            if (AP4_FAILED(result)) return result;
            stream_cipher = new AP4_CbcStreamCipher(block_cipher);
            break;

        case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR: {
            AP4_BlockCipher::CtrParams ctr_params;
            ctr_params.counter_size = 16;
            result = block_cipher_factory->CreateCipher(AP4_BlockCipher::AES_128,
                     AP4_BlockCipher::DECRYPT,
                     AP4_BlockCipher::CTR,
                     &ctr_params,
                     key,
                     key_size,
                     block_cipher);
            if (AP4_FAILED(result)) return result;
            stream_cipher = new AP4_CtrStreamCipher(block_cipher, 16);
            break;
        }

        default:
            return AP4_ERROR_NOT_SUPPORTED;
        }

        // set the IV
        stream_cipher->SetIV(grpi->GetGroupKey().GetData());

        // decrypt the content key
        AP4_Size key_buffer_size = grpi->GetGroupKey().GetDataSize(); // worst case
        key_buffer = new AP4_UI08[key_buffer_size];
        result = stream_cipher->ProcessBuffer(grpi->GetGroupKey().GetData()+16,
                                              grpi->GetGroupKey().GetDataSize()-16,
                                              key_buffer,
                                              &key_buffer_size,
                                              true);
        delete stream_cipher; // this will also delete the block cipher
        if (AP4_FAILED(result)) {
            delete[] key_buffer;
            return result;
        }

        // point to the new key value
        key = key_buffer;
        key_size = key_buffer_size;
    }

    AP4_OmaDcfCipherMode mode;
    switch (ohdr->GetEncryptionMethod()) {
    case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC:
        mode = AP4_OMA_DCF_CIPHER_MODE_CBC;
        break;
    case AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR:
        mode = AP4_OMA_DCF_CIPHER_MODE_CTR;
        break;
    default:
        return AP4_ERROR_NOT_SUPPORTED;
    }

    AP4_Result result;
    result = CreateDecryptingStream(mode,
                                    odda->GetEncryptedPayload(),
                                    ohdr->GetPlaintextLength(),
                                    key, key_size,
                                    block_cipher_factory,
                                    stream);

    // cleanup
    delete[] key_buffer;

    return result;
}
/*----------------------------------------------------------------------
|   DcfParser_ParseV2Header
+---------------------------------------------------------------------*/
static BLT_Result
DcfParser_ParseV2Header(DcfParser* self, ATX_InputStream* stream)
{
    /* rewind the byte stream */
    ATX_InputStream_Seek(stream, 0);

    /* parse the atoms from the stream */
    AP4_ByteStream* mp4_stream = new ATX_InputStream_To_AP4_ByteStream_Adapter(stream);
    AP4_AtomParent  atoms;
    AP4_Result result = AP4_DefaultAtomFactory::Instance.CreateAtomsFromStream(*mp4_stream, atoms);
    mp4_stream->Release();
    
    AP4_ByteStream* decrypting_stream = NULL;
    AP4_ContainerAtom* odrm = dynamic_cast<AP4_ContainerAtom*>(atoms.GetChild(AP4_ATOM_TYPE_ODRM));
    if (odrm) {
        AP4_OdheAtom* odhe = dynamic_cast<AP4_OdheAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODHE));
        AP4_OddaAtom* odda = dynamic_cast<AP4_OddaAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODDA));
        if (odhe && odda) {
            const char* content_id = "";

            /* get the content ID */
            AP4_OhdrAtom* ohdr = dynamic_cast<AP4_OhdrAtom*>(odhe->GetChild(AP4_ATOM_TYPE_OHDR));
            if (ohdr) {
                content_id = ohdr->GetContentId().GetChars();
            }

            /* get the content key */
            NPT_DataBuffer key;
            result = DcfParser_GetContentKey(self, content_id, key);
            if (BLT_FAILED(result)) {
                ATX_LOG_FINE_2("GetKeyForContent(%s) returned %d", 
                               content_id, 
                               result);
                return BLT_ERROR_NO_MEDIA_KEY;
            }

            /* create the decrypting stream */
            result = AP4_OmaDcfAtomDecrypter::CreateDecryptingStream(*odrm,
                                                                     key.GetData(),
                                                                     key.GetDataSize(),
                                                                     self->cipher_factory,
                                                                     decrypting_stream);
            if (AP4_SUCCEEDED(result)) {
                /* update the content type */
                ATX_CopyStringN(self->input.content_type,
                                odhe->GetContentType().GetChars(),
                                sizeof(self->input.content_type));
                    
                /* update the encrypted size */
                self->input.encrypted_size = odda->GetEncryptedDataLength();
            }
        }
    }

    /* check that we have found what we needed in the atoms */
    if (decrypting_stream == NULL) return BLT_ERROR_INVALID_MEDIA_FORMAT;

    /* update the output size */
    AP4_LargeSize plaintext_size = 0;
    if (AP4_SUCCEEDED(decrypting_stream->GetSize(plaintext_size))) {
        self->output.size = plaintext_size;
    } else {
        self->output.size = self->input.encrypted_size;
    }
    
    /* create a reverse adapter */
    result = AP4_ByteStream_To_ATX_InputStream_Adapter_Create(decrypting_stream, &self->output.stream);
    decrypting_stream->Release();

    return BLT_SUCCESS;
}
Esempio n. 5
0
/*----------------------------------------------------------------------
|   AP4_MarlinIpmpDecryptingProcessor:CreateTrackHandler
+---------------------------------------------------------------------*/
AP4_Processor::TrackHandler* 
AP4_MarlinIpmpDecryptingProcessor::CreateTrackHandler(AP4_TrakAtom* trak)
{
    // look for this track in the list of entries
    AP4_MarlinIpmpParser::SinfEntry* sinf_entry = NULL;
    for (AP4_List<AP4_MarlinIpmpParser::SinfEntry>::Item* sinf_entry_item = m_SinfEntries.FirstItem();
                                                          sinf_entry_item;
                                                          sinf_entry_item = sinf_entry_item->GetNext()) {
        sinf_entry = sinf_entry_item->GetData();
        if (sinf_entry->m_TrackId == trak->GetId()) {
            break; // match
        } else {
            sinf_entry = NULL; // no match
        }
    }
    if (sinf_entry == NULL) return NULL; // no matching entry
    AP4_ContainerAtom* sinf = sinf_entry->m_Sinf;
    
    // check the scheme
    bool use_group_key;
    AP4_SchmAtom* schm = AP4_DYNAMIC_CAST(AP4_SchmAtom, sinf->GetChild(AP4_ATOM_TYPE_SCHM));
    if (schm == NULL) return NULL; // no schm
    if (schm->GetSchemeType()    == AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACBC && 
        schm->GetSchemeVersion() == 0x0100) {
        use_group_key = false;
    } else if (schm->GetSchemeType()    == AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACGK &&
               schm->GetSchemeVersion() == 0x0100) {
        use_group_key = true;
    } else {
        // unsupported scheme
        return NULL;
    }

    // find the key
    const AP4_DataBuffer* key = NULL;
    AP4_DataBuffer        unwrapped_key;
    if (use_group_key) {
        const AP4_DataBuffer* group_key = m_KeyMap.GetKey(0);
        if (group_key == NULL) return NULL; // no group key
        AP4_ContainerAtom* schi = AP4_DYNAMIC_CAST(AP4_ContainerAtom, sinf->GetChild(AP4_ATOM_TYPE_SCHI));
        if (schi == NULL) return NULL; // no schi
        AP4_Atom* gkey = schi->GetChild(AP4_ATOM_TYPE_GKEY);
        if (gkey == NULL) return NULL; // no gkey
        AP4_MemoryByteStream* gkey_data = new AP4_MemoryByteStream();
        gkey->WriteFields(*gkey_data);
        AP4_AesKeyUnwrap(group_key->GetData(), gkey_data->GetData(), gkey_data->GetDataSize(), unwrapped_key);
        key = &unwrapped_key;
        gkey_data->Release();        
    } else {
        key = m_KeyMap.GetKey(sinf_entry->m_TrackId);
    }
    if (key == NULL) return NULL;

    // create the decrypter
    AP4_MarlinIpmpTrackDecrypter* decrypter = NULL;
    AP4_Result result = AP4_MarlinIpmpTrackDecrypter::Create(*m_BlockCipherFactory,
                                                             key->GetData(), 
                                                             key->GetDataSize(),
                                                             decrypter);
    if (AP4_FAILED(result)) return NULL;
    
    return decrypter;
}
/*----------------------------------------------------------------------
|   AP4_Processor::MuxStream
+---------------------------------------------------------------------*/
AP4_Result
AP4_Processor::MuxStream(
	AP4_Array<AP4_ByteStream *> &input,
	AP4_ByteStream& output,
	AP4_UI08		partitions,
	AP4_AtomFactory& atom_factory)
{
	AP4_Result		result;
	AP4_UI64		stream_offset = 0;

	if (partitions & 1)
	{
		// read all atoms.
		// keep all atoms except [mdat]
		// keep a ref to [moov]
		// put [moof] atoms in a separate list
		AP4_AtomParent              top_level;
		AP4_Array<AP4_MoovAtom*>	moov;
		AP4_Size					track_count(0);

		for(AP4_Size streamid(0); streamid < input.ItemCount(); ++streamid)
		{
			for (AP4_Atom* atom = NULL; AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(*input[streamid], atom)); input[streamid]->Tell(stream_offset))
			{
				if (atom->GetType() == AP4_ATOM_TYPE_MFRA) {
					delete atom;
					continue;
				}
				else if (atom->GetType() == AP4_ATOM_TYPE_SIDX) {
					delete atom;
					continue;
				}
				else if (atom->GetType() == AP4_ATOM_TYPE_SSIX) {
					delete atom;
					continue;
				}
				if (streamid == 0)
					top_level.AddChild(atom);
				else if (atom->GetType() != AP4_ATOM_TYPE_MOOV)
					delete atom;
				if (atom->GetType() == AP4_ATOM_TYPE_MOOV)
				{
					moov.Append(AP4_DYNAMIC_CAST(AP4_MoovAtom,atom));
					break;
				}
			}
			if (moov.ItemCount() == streamid)
				return AP4_ERROR_INVALID_FORMAT;
			
			while (AP4_SUCCEEDED(moov[streamid]->DeleteChild(AP4_ATOM_TYPE_PSSH, 0)));

			// Remove tracks we cannot handle
			for (AP4_List<AP4_TrakAtom>::Item *item(moov[streamid]->GetTrakAtoms().FirstItem()); item;)
				if (!item->GetData()->FindChild("mdia/minf/stbl"))
					moov[streamid]->GetTrakAtoms().Remove(item);
				else
					item = item->GetNext();
			track_count += moov[streamid]->GetTrakAtoms().ItemCount();
		}

		// initialize the processor
		if (AP4_FAILED(result = Initialize(top_level, *input[0]))) 
			return result;

		// process the tracks if we have a moov atom
		m_TrackData.SetItemCount(track_count);
		m_StreamData.SetItemCount(input.ItemCount());
	
		//NormalizeTREX(mvex, 0, m_TrackCounts[0], m_TrackCounts[1]);
		AP4_Cardinal internal_index(0);
		AP4_ContainerAtom *mvex_base(0);
		AP4_List<AP4_TrakAtom>::Item *item = NULL;

		for (AP4_Size streamid(0); streamid < input.ItemCount(); ++streamid)
		{
			m_StreamData[streamid].trackStart = internal_index;
			m_StreamData[streamid].stream = input[streamid];
			if (streamid)
				moov[0]->AddTrakAtoms(moov[streamid]->GetTrakAtoms(), item);
			else
				item = moov[streamid]->GetTrakAtoms().FirstItem();

			for (; item; item = item->GetNext())
			{
				PERTRACK &track_data(m_TrackData[internal_index]);
				track_data.original_id = item->GetData()->GetId();
				item->GetData()->SetId(track_data.new_id = internal_index + 1);

        if (AP4_MdhdAtom* mdhd = AP4_DYNAMIC_CAST(AP4_MdhdAtom, item->GetData()->FindChild("mdia/mdhd")))
          track_data.timescale = mdhd->GetTimeScale();
        else
          track_data.timescale = 1;
        
        AP4_ContainerAtom *mvex = AP4_DYNAMIC_CAST(AP4_ContainerAtom, moov[streamid]->GetChild(AP4_ATOM_TYPE_MVEX, 0));
				if (!mvex)
					return AP4_ERROR_INVALID_FORMAT;
				
				if (!item->GetData()->GetDuration())
				{
					AP4_MehdAtom *mehd(AP4_DYNAMIC_CAST(AP4_MehdAtom, mvex->GetChild(AP4_ATOM_TYPE_MEHD, 0)));
					item->GetData()->SetDuration(mehd? mehd->GetDuration():0);
				}
				
				AP4_TrexAtom *trex(NULL);
				unsigned int index(0);
				for (; !trex && (trex = AP4_DYNAMIC_CAST(AP4_TrexAtom, mvex->GetChild(AP4_ATOM_TYPE_TREX, index++)));)
					if(trex->GetTrackId() != track_data.original_id)
						trex = NULL;
				if (!trex)
					return AP4_ERROR_INVALID_FORMAT;

				if (mvex_base)
				{
					trex = AP4_DYNAMIC_CAST(AP4_TrexAtom, trex->Clone());
					mvex_base->AddChild(trex);
				}
				else
					mvex_base = mvex;
				trex->SetTrackId(track_data.new_id);

				track_data.track_handler = CreateTrackHandler(item->GetData(), trex);
				track_data.track_handler->ProcessTrack();
				track_data.streamId = streamid;
				++m_StreamData[streamid].trackCount;
				++internal_index;
			}
		}
		// We don't need the other moovs anymore.....
		moov.SetItemCount(1);

		AP4_MvhdAtom *mvhd(AP4_DYNAMIC_CAST(AP4_MvhdAtom, moov[0]->GetChild(AP4_ATOM_TYPE_MVHD, 0)));
		if (!mvhd->GetDuration())
		{
			AP4_MehdAtom *mehd(AP4_DYNAMIC_CAST(AP4_MehdAtom, mvex_base->GetChild(AP4_ATOM_TYPE_MEHD, 0)));
			mvhd->SetDuration(mehd ? mehd->GetDuration() : 0);
		}

		// finalize the processor
		Finalize(top_level);

		// calculate the size of all atoms combined
		AP4_UI64 atoms_size = 0;
		top_level.GetChildren().Apply(AP4_AtomSizeAdder(atoms_size));

		// write all atoms
		top_level.GetChildren().Apply(AP4_AtomListWriter(output));
		m_MoovAtom = moov[0];
		m_MoovAtom->Detach();
	}

	if (partitions & 2)
	{
		// process the fragments, if any
		result = AP4_SUCCESS;
		AP4_Array<AP4_UI64> moof_positions, mdat_positions;
		moof_positions.SetItemCount(input.ItemCount());
		mdat_positions.SetItemCount(input.ItemCount());

		for (;;)
		{
			AP4_ContainerAtom *moof = NULL;
			AP4_UI32 track_index(0);
			
#if 0			
      for (AP4_Cardinal streamid(0); streamid < input.ItemCount(); ++streamid)
			{
				AP4_Atom* atom = NULL;
				if (AP4_SUCCEEDED(input[streamid]->Tell(stream_offset)) && AP4_SUCCEEDED(atom_factory.CreateAtomFromStream(*input[streamid], atom)))
				{
					if (atom->GetType() != AP4_ATOM_TYPE_MOOF)
						return AP4_ERROR_INVALID_FORMAT;
					
					moof_positions[streamid] = stream_offset;
					mdat_positions[streamid] = stream_offset + atom->GetSize() + +AP4_ATOM_HEADER_SIZE;

					if (moof)
					{
						int index(0);
						for (; AP4_Atom* child = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom)->GetChild(AP4_ATOM_TYPE_TRAF, index++);)
							moof->AddChild(child->Clone());
						delete atom;
					}
					else
						moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
					NormalizeTRAF(AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof), m_StreamData[streamid].trackStart,
						m_StreamData[streamid].trackStart + m_StreamData[streamid].trackCount, track_index);
				}
				else
					delete atom;
			}
#else
      double mindts(9999999999.0);
      AP4_Cardinal nextStream(~0);
      for (AP4_Cardinal track(0); track < m_TrackData.ItemCount(); ++track)
        if ((double)m_TrackData[track].dts / m_TrackData[track].timescale  < mindts)
        {
          mindts = (double)m_TrackData[track].dts / m_TrackData[track].timescale;
          nextStream = m_TrackData[track].streamId;
        }
      
      AP4_Atom* atom = NULL;
      if (AP4_SUCCEEDED(result = input[nextStream]->Tell(stream_offset)) && AP4_SUCCEEDED(result = atom_factory.CreateAtomFromStream(*input[nextStream], atom)))
      {
        if (atom->GetType() != AP4_ATOM_TYPE_MOOF)
          return AP4_ERROR_INVALID_FORMAT;
			} else if (atom)
				return result;

      moof_positions[nextStream] = stream_offset;
      mdat_positions[nextStream] = stream_offset + atom->GetSize() + +AP4_ATOM_HEADER_SIZE;

      moof = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
      NormalizeTRAF(AP4_DYNAMIC_CAST(AP4_ContainerAtom, moof), m_StreamData[nextStream].trackStart,
        m_StreamData[nextStream].trackStart + m_StreamData[nextStream].trackCount, track_index);
#endif
			if (!moof)
				break;
			
			if (AP4_FAILED(result = ProcessFragment(moof, NULL, 0, output, moof_positions, mdat_positions)))
				return result;
			
			delete moof;
			moof = NULL;
		}

		// cleanup
		m_TrackData.Clear();
		m_StreamData.Clear();
	}

	return AP4_SUCCESS;
}
/*----------------------------------------------------------------------
|   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;
}
/*----------------------------------------------------------------------
|   Mp4ParserOutput_SetSampleDescription
+---------------------------------------------------------------------*/
static BLT_Result
Mp4ParserOutput_SetSampleDescription(Mp4ParserOutput* self, 
                                     unsigned int     indx)
{
    // if we had a decrypter before, release it now
    delete self->sample_decrypter;
    self->sample_decrypter = NULL;
    
    // check that the audio track is of the right type
    AP4_SampleDescription* sample_desc = self->track->GetSampleDescription(indx);
    if (sample_desc == NULL) {
        ATX_LOG_FINE("no sample description for track");
        return BLT_ERROR_INVALID_MEDIA_FORMAT;
    }
    // handle encrypted tracks
    BLT_Result result = Mp4ParserOutput_ProcessCryptoInfo(self, sample_desc);
    if (BLT_FAILED(result)) return result;
    
    // update the generic part of the stream info
    BLT_StreamInfo stream_info;
    stream_info.id            = self->track->GetId();
    stream_info.duration      = self->track->GetDurationMs();
    stream_info.mask = BLT_STREAM_INFO_MASK_ID |
                       BLT_STREAM_INFO_MASK_DURATION;
    
    // deal with audio details, if this is an audio track
    AP4_AudioSampleDescription* audio_desc = dynamic_cast<AP4_AudioSampleDescription*>(sample_desc);
    if (audio_desc) {
        ATX_LOG_FINE("sample description is audio");
        stream_info.type          = BLT_STREAM_TYPE_AUDIO;
        stream_info.channel_count = audio_desc->GetChannelCount();
        stream_info.sample_rate   = audio_desc->GetSampleRate();
        stream_info.mask |= BLT_STREAM_INFO_MASK_TYPE          |
                            BLT_STREAM_INFO_MASK_CHANNEL_COUNT |
                            BLT_STREAM_INFO_MASK_SAMPLE_RATE;
    } else if (self == &self->parser->audio_output) {
        ATX_LOG_FINE("expected audio sample description, but did not get one");
        return BLT_ERROR_INVALID_MEDIA_FORMAT;
    }

    AP4_VideoSampleDescription* video_desc = dynamic_cast<AP4_VideoSampleDescription*>(sample_desc);
    if (video_desc) {
        ATX_LOG_FINE("sample description is video");
        stream_info.type     = BLT_STREAM_TYPE_VIDEO;
        stream_info.width    = video_desc->GetWidth();
        stream_info.height   = video_desc->GetHeight();
        stream_info.mask |= BLT_STREAM_INFO_MASK_TYPE     |
                            BLT_STREAM_INFO_MASK_WIDTH    |
                            BLT_STREAM_INFO_MASK_HEIGHT;
    } else if (self == &self->parser->video_output) {
        ATX_LOG_FINE("expected video sample descriton, but did not get one");
        return BLT_ERROR_INVALID_MEDIA_FORMAT;
    }
    
    AP4_MpegSampleDescription* mpeg_desc = NULL;
    if (sample_desc->GetType() == AP4_SampleDescription::TYPE_MPEG) {
        ATX_LOG_FINE("sample description is of type MPEG");
        mpeg_desc = dynamic_cast<AP4_MpegSampleDescription*>(sample_desc);
    }
    if (mpeg_desc) {
        stream_info.data_type       = mpeg_desc->GetObjectTypeString(mpeg_desc->GetObjectTypeId());
        stream_info.average_bitrate = mpeg_desc->GetAvgBitrate();
        stream_info.nominal_bitrate = mpeg_desc->GetAvgBitrate();
        stream_info.mask |= BLT_STREAM_INFO_MASK_AVERAGE_BITRATE |
                            BLT_STREAM_INFO_MASK_NOMINAL_BITRATE |
                            BLT_STREAM_INFO_MASK_DATA_TYPE;
    }
    
    // setup the output media type
    AP4_DataBuffer  decoder_info;
    BLT_MediaTypeId media_type_id = BLT_MEDIA_TYPE_ID_NONE;
    AP4_UI32        format_or_object_type_id = 0;
    if (mpeg_desc) {
        decoder_info.SetData(mpeg_desc->GetDecoderInfo().GetData(),
                             mpeg_desc->GetDecoderInfo().GetDataSize());
        media_type_id = self->mp4_es_type_id;
        format_or_object_type_id = mpeg_desc->GetObjectTypeId();
    } else {
        // here we have to be format-specific for the decoder info
        stream_info.data_type = AP4_GetFormatName(sample_desc->GetFormat());
        stream_info.mask |= BLT_STREAM_INFO_MASK_DATA_TYPE;
        format_or_object_type_id = sample_desc->GetFormat();
        if (sample_desc->GetFormat() == AP4_SAMPLE_FORMAT_AVC1) {
            // look for an 'avcC' atom
            AP4_AvccAtom* avcc = static_cast<AP4_AvccAtom*>(sample_desc->GetDetails().GetChild(AP4_ATOM_TYPE_AVCC));
            if (avcc) {
                // pass the avcc payload as the decoder info
                decoder_info.SetData(avcc->GetRawBytes().GetData(),
                                     avcc->GetRawBytes().GetDataSize());
            } 
        } else if (sample_desc->GetFormat() == AP4_SAMPLE_FORMAT_ALAC) {
            // look for an 'alac' atom (either top-level or inside a 'wave') 
            AP4_Atom* alac = sample_desc->GetDetails().GetChild(AP4_SAMPLE_FORMAT_ALAC);
            if (alac == NULL) {
                AP4_ContainerAtom* wave = dynamic_cast<AP4_ContainerAtom*>(sample_desc->GetDetails().GetChild(AP4_ATOM_TYPE_WAVE));
                if (wave) {
                    alac = wave->GetChild(AP4_SAMPLE_FORMAT_ALAC);
                }
            }
            if (alac) {
                // pass the alac payload as the decoder info
                AP4_MemoryByteStream* mbs = new AP4_MemoryByteStream((AP4_Size)alac->GetSize());
                alac->WriteFields(*mbs);
                decoder_info.SetData(mbs->GetData(), mbs->GetDataSize());                
                mbs->Release();
            } 
        }
        
        media_type_id = self->iso_base_es_type_id;
    }
    BLT_Mp4MediaType* media_type = NULL;
    unsigned int struct_size = decoder_info.GetDataSize()?decoder_info.GetDataSize()-1:0;
    if (audio_desc) {
        struct_size += sizeof(BLT_Mp4AudioMediaType);
        BLT_Mp4AudioMediaType* audio_type = (BLT_Mp4AudioMediaType*)ATX_AllocateZeroMemory(struct_size);;
        audio_type->base.stream_type    = BLT_MP4_STREAM_TYPE_AUDIO;
        audio_type->channel_count       = audio_desc->GetChannelCount();
        audio_type->sample_rate         = audio_desc->GetSampleRate();
        audio_type->decoder_info_length = decoder_info.GetDataSize();
        if (decoder_info.GetDataSize()) {
            ATX_CopyMemory(&audio_type->decoder_info[0], decoder_info.GetData(), decoder_info.GetDataSize());
        }
        media_type = &audio_type->base;
    } else {
        struct_size += sizeof(BLT_Mp4VideoMediaType);
        BLT_Mp4VideoMediaType* video_type = (BLT_Mp4VideoMediaType*)ATX_AllocateZeroMemory(struct_size);
        video_type->base.stream_type    = BLT_MP4_STREAM_TYPE_VIDEO;
        video_type->width               = video_desc->GetWidth();
        video_type->height              = video_desc->GetHeight();
        video_type->decoder_info_length = decoder_info.GetDataSize();
        if (decoder_info.GetDataSize()) {
            ATX_CopyMemory(&video_type->decoder_info[0], decoder_info.GetData(), decoder_info.GetDataSize());
        }
        media_type = &video_type->base;
    }
    media_type->base.id                  = media_type_id;
    media_type->base.extension_size      = struct_size-sizeof(BLT_MediaType); 
    media_type->format_or_object_type_id = format_or_object_type_id;
    self->media_type = &media_type->base;
    self->sample_description_index = indx;
    
    // final update to the stream info
    BLT_Stream_SetInfo(ATX_BASE(self->parser, BLT_BaseMediaNode).context, &stream_info);
    
    // enable the track in the linear reader if we have one
    if (self->parser->input.reader) {
        self->parser->input.reader->EnableTrack(self->track->GetId());
    }
    
    return BLT_SUCCESS;    
}
Esempio n. 9
0
/*----------------------------------------------------------------------
|   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;
}
Esempio n. 10
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);
								}
							}
						}
					}
				}
			}
		}
	}
}
Esempio n. 11
0
/*----------------------------------------------------------------------
|   AP4_Processor::ProcessFragments
+---------------------------------------------------------------------*/
AP4_Result
AP4_Processor::ProcessFragments(AP4_MoovAtom*              moov, 
                                AP4_List<AP4_MoofLocator>& moofs, 
                                AP4_ContainerAtom*         mfra,
                                AP4_ByteStream&            input, 
                                AP4_ByteStream&            output)
{
    // FIXME: this only works for non-changing moofs 
 
    for (AP4_List<AP4_MoofLocator>::Item* item = moofs.FirstItem();
                                          item;
                                          item = item->GetNext()) {
        AP4_MoofLocator*   locator     = item->GetData();
        AP4_ContainerAtom* moof        = locator->m_Moof;
        AP4_UI64           moof_offset = locator->m_Offset;
        AP4_UI64           mdat_payload_offset = moof_offset+moof->GetSize()+8;
        AP4_MovieFragment* fragment    = new AP4_MovieFragment(moof);
        AP4_Sample         sample;
        AP4_DataBuffer     sample_data_in;
        AP4_DataBuffer     sample_data_out;
        AP4_Result         result;
    
        // process all the traf atoms
        AP4_Array<AP4_Processor::FragmentHandler*> handlers;
        for (;AP4_Atom* atom = moof->GetChild(AP4_ATOM_TYPE_TRAF, handlers.ItemCount());) {
            AP4_ContainerAtom* traf = AP4_DYNAMIC_CAST(AP4_ContainerAtom, atom);
            AP4_Processor::FragmentHandler* handler = CreateFragmentHandler(traf);
            if (handler) result = handler->ProcessFragment();
            handlers.Append(handler);
        }
             
        // write the moof
        AP4_UI64 moof_out_start = 0;
        output.Tell(moof_out_start);
        bool moof_has_changed = false;
        moof->Write(output);
            
        // process all track runs
        for (unsigned int i=0; i<handlers.ItemCount(); i++) {
            AP4_FragmentSampleTable* sample_table = NULL;
            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));
            AP4_TfhdAtom* tfhd      = AP4_DYNAMIC_CAST(AP4_TfhdAtom, traf->GetChild(AP4_ATOM_TYPE_TFHD, i));
            
            // create a sample table object so we can read the sample data
            result = fragment->CreateSampleTable(moov, tfhd->GetTrackId(), &input, moof_offset, mdat_payload_offset, sample_table);
            if (AP4_FAILED(result)) return result;

            // compute the mdat size
            AP4_UI64 mdat_size = 0;
            for (unsigned int j=0; j<sample_table->GetSampleCount(); j++) {
                result = sample_table->GetSample(j, sample);
                if (AP4_FAILED(result)) return result;
                mdat_size += sample.GetSize();
            }
            
            // write an mdat header
            if (mdat_size > 0xFFFFFFFF-8) {
                // we don't support large mdat fragments
                return AP4_ERROR_OUT_OF_RANGE;
            }
            if (mdat_size) {
                output.WriteUI32((AP4_UI32)(8+mdat_size));
                output.WriteUI32(AP4_ATOM_TYPE_MDAT);
            }
            
#if defined(AP4_DEBUG)
            AP4_Position before;
            output.Tell(before);
#endif
            
            // write the mdat
            for (unsigned int j=0; j<sample_table->GetSampleCount(); j++) {
                result = sample_table->GetSample(j, sample);
                if (AP4_FAILED(result)) return result;
                sample.ReadData(sample_data_in);
                
                // 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;

                    // give the handler a chance to update the atoms
                    result = handler->FinishFragment();
                    if (AP4_SUCCEEDED(result)) moof_has_changed = true;
                } else {
                    // write the sample data (unmodified)
                    result = output.Write(sample_data_in.GetData(), sample_data_in.GetDataSize());
                    if (AP4_FAILED(result)) return result;
                }
            }
            
#if defined(AP4_DEBUG)
            AP4_Position after;
            output.Tell(after);
            AP4_ASSERT(after-before == mdat_size);
#endif
            delete sample_table;
        }
        
        // update the moof if needed
        AP4_UI64 mdat_out_end = 0;
        output.Tell(mdat_out_end);
        if (moof_has_changed) {
            output.Seek(moof_out_start);
            moof->Write(output);
            output.Seek(mdat_out_end);
        }
                
        // update the mfra if we have one
        if (mfra) {
            for (AP4_List<AP4_Atom>::Item* mfra_item = mfra->GetChildren().FirstItem();
                                           mfra_item;
                                           mfra_item = mfra_item->GetNext()) {
                if (mfra_item->GetData()->GetType() != AP4_ATOM_TYPE_TFRA) continue;
                AP4_TfraAtom* tfra = AP4_DYNAMIC_CAST(AP4_TfraAtom, mfra_item->GetData());
                if (tfra == NULL) continue;
                AP4_Array<AP4_TfraAtom::Entry>& entries     = tfra->GetEntries();
                AP4_Cardinal                    entry_count = entries.ItemCount();
                for (unsigned int i=0; i<entry_count; i++) {
                    if (entries[i].m_MoofOffset == locator->m_Offset) {
                        entries[i].m_MoofOffset = moof_out_start;
                    }
                }
            }
        }

        delete fragment;
    }
        
    return AP4_SUCCESS;
}