/*---------------------------------------------------------------------- | AP4_OmaDcfCtrSampleEncrypter::EncryptSampleData +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfCtrSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out, AP4_UI64 counter, bool /*skip_encryption*/) { // setup the buffers const unsigned char* in = data_in.GetData(); AP4_CHECK(data_out.SetDataSize(data_in.GetDataSize()+AP4_CIPHER_BLOCK_SIZE+1)); unsigned char* out = data_out.UseData(); // selective encryption flag *out++ = 0x80; // IV on 16 bytes: [SSSSSSSSXXXXXXXX] // where SSSSSSSS is the 64-bit salt and // XXXXXXXX is the 64-bit base counter AP4_CopyMemory(out, m_Salt, 8); AP4_BytesFromUInt64BE(&out[8], counter); // encrypt the payload AP4_Size data_size = data_in.GetDataSize(); m_Cipher->SetIV(out); m_Cipher->ProcessBuffer(in, data_size, out+AP4_CIPHER_BLOCK_SIZE); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_SgpdAtom::InspectFields +---------------------------------------------------------------------*/ AP4_Result AP4_SgpdAtom::InspectFields(AP4_AtomInspector& inspector) { char fourcc[5]; AP4_FormatFourChars(fourcc, m_GroupingType); inspector.AddField("grouping_type", fourcc); if (m_Version >= 1) { inspector.AddField("default_length", m_DefaultLength); } inspector.AddField("entry_count", m_Entries.ItemCount()); // inspect entries char header[32]; unsigned int i=0; for (AP4_List<AP4_DataBuffer>::Item* item = m_Entries.FirstItem(); item; item = item->GetNext()) { AP4_DataBuffer* entry = item->GetData(); AP4_FormatString(header, sizeof(header), "entry %02d", i); ++i; inspector.AddField(header, entry->GetData(), entry->GetDataSize()); } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_SgpdAtom::WriteFields +---------------------------------------------------------------------*/ AP4_Result AP4_SgpdAtom::WriteFields(AP4_ByteStream& stream) { AP4_Result result; result = stream.WriteUI32(m_GroupingType); if (AP4_FAILED(result)) return result; if (m_Version >= 1) { result = stream.WriteUI32(m_DefaultLength); if (AP4_FAILED(result)) return result; } // write the children result = stream.WriteUI32(m_Entries.ItemCount()); if (AP4_FAILED(result)) return result; for (AP4_List<AP4_DataBuffer>::Item* item = m_Entries.FirstItem(); item; item = item->GetNext()) { AP4_DataBuffer* entry = item->GetData(); if (m_Version >= 1) { if (m_DefaultLength == 0) { stream.WriteUI32((AP4_UI32)entry->GetDataSize()); } } result = stream.Write(entry->GetData(), entry->GetDataSize()); if (AP4_FAILED(result)) { return result; } } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_DecoderSpecificInfoDescriptor::AP4_DecoderSpecificInfoDescriptor +---------------------------------------------------------------------*/ AP4_DecoderSpecificInfoDescriptor::AP4_DecoderSpecificInfoDescriptor( const AP4_DataBuffer& data) : AP4_Descriptor(AP4_DESCRIPTOR_TAG_DECODER_SPECIFIC_INFO, MinHeaderSize(data.GetDataSize()), data.GetDataSize()), m_Info(data) { }
/*---------------------------------------------------------------------- | AP4_DefaultFragmentHandler::ProcessSample +---------------------------------------------------------------------*/ AP4_Result AP4_DefaultFragmentHandler::ProcessSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out) { if (m_TrackHandler == NULL) { data_out.SetData(data_in.GetData(), data_in.GetDataSize()); return AP4_SUCCESS; } return m_TrackHandler->ProcessSample(data_in, data_out); }
/*---------------------------------------------------------------------- | AP4_IsmaCipher::DecryptSample +---------------------------------------------------------------------*/ AP4_Result AP4_IsmaCipher::DecryptSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out) { bool is_encrypted = true; const unsigned char* in = data_in.GetData(); if (m_SelectiveEncryption) { is_encrypted = ((in[0]&1)==1); in++; } // get the IV (this implementation only supports un to 32 bits of IV) // so we skip anything beyond the last 4 bytes unsigned int to_read = m_IvLength; if (to_read > 16 || to_read == 0) return AP4_ERROR_INVALID_FORMAT; while (to_read > 4) { to_read--; in++; } AP4_UI32 iv = 0; while (to_read--) { iv = (iv<<8) | *in++; } // get the key indicator (we only support up to 32 bits as well) to_read = m_KeyIndicatorLength; if (to_read > 4 ) return AP4_ERROR_INVALID_FORMAT; while (to_read > 4) { to_read--; in++; } AP4_UI32 key_indicator = 0; while (to_read--) { key_indicator = (key_indicator<<8) | *in++; } // we only support key indicator = 0 for now... (TODO) if (key_indicator != 0) { return AP4_FAILURE; } // process the sample data unsigned int header_size = in-data_in.GetData(); unsigned int payload_size = data_in.GetDataSize()-header_size; data_out.SetDataSize(payload_size); unsigned char* out = data_out.UseData(); if (is_encrypted) { m_Cipher->SetStreamOffset(iv); m_Cipher->ProcessBuffer(in, out, payload_size); } else { memcpy(out, in, payload_size); } return AP4_SUCCESS; }
virtual void UpdatePPSId(AP4_DataBuffer const &buffer) override { //Search the Slice header NALU const AP4_UI08 *data(buffer.GetData()); unsigned int data_size(buffer.GetDataSize()); for (; data_size;) { // sanity check if (data_size < naluLengthSize) break; // get the next NAL unit AP4_UI32 nalu_size; switch (naluLengthSize) { case 1:nalu_size = *data++; data_size--; break; case 2:nalu_size = AP4_BytesToInt16BE(data); data += 2; data_size -= 2; break; case 4:nalu_size = AP4_BytesToInt32BE(data); data += 4; data_size -= 4; break; default: data_size = 0; nalu_size = 1; break; } if (nalu_size > data_size) break; // Stop further NALU processing if (countPictureSetIds < 2) naluLengthSize = 0; unsigned int nal_unit_type = *data & 0x1F; if ( //nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_NON_IDR_PICTURE || nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_OF_IDR_PICTURE //|| //nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_A || //nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_B || //nal_unit_type == AP4_AVC_NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_C ) { AP4_DataBuffer unescaped(data, data_size); AP4_NalParser::Unescape(unescaped); AP4_BitReader bits(unescaped.GetData(), unescaped.GetDataSize()); bits.SkipBits(8); // NAL Unit Type AP4_AvcFrameParser::ReadGolomb(bits); // first_mb_in_slice AP4_AvcFrameParser::ReadGolomb(bits); // slice_type pictureId = AP4_AvcFrameParser::ReadGolomb(bits); //picture_set_id } // move to the next NAL unit data += nalu_size; data_size -= nalu_size; } }
/*---------------------------------------------------------------------- | AP4_UnknownSampleEntry::AP4_UnknownSampleEntry +---------------------------------------------------------------------*/ AP4_UnknownSampleEntry::AP4_UnknownSampleEntry(AP4_Atom::Type type, AP4_DataBuffer& payload) : AP4_SampleEntry(type), m_Payload(payload) { m_Size32 += payload.GetDataSize(); }
/*---------------------------------------------------------------------- | AP4_OmaDcfCbcSampleEncrypter::EncryptSampleData +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfCbcSampleEncrypter::EncryptSampleData(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out, AP4_UI64 counter, bool /*skip_encryption*/) { // make sure there is enough space in the output buffer data_out.Reserve(data_in.GetDataSize()+2*AP4_CIPHER_BLOCK_SIZE+1); // setup the buffers AP4_Size out_size = data_in.GetDataSize()+AP4_CIPHER_BLOCK_SIZE; unsigned char* out = data_out.UseData(); // selective encryption flag *out++ = 0x80; // IV on 16 bytes: [SSSSSSSSXXXXXXXX] // where SSSSSSSS is the 64-bit salt and // XXXXXXXX is the 64-bit base counter AP4_CopyMemory(out, m_Salt, 8); AP4_BytesFromUInt64BE(&out[8], counter); // encrypt the payload m_Cipher->SetIV(out); m_Cipher->ProcessBuffer(data_in.GetData(), data_in.GetDataSize(), out+AP4_CIPHER_BLOCK_SIZE, &out_size, true); AP4_CHECK(data_out.SetDataSize(out_size+AP4_CIPHER_BLOCK_SIZE+1)); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_IsmaTrackEncrypter::ProcessSample +---------------------------------------------------------------------*/ AP4_Result AP4_IsmaTrackEncrypter::ProcessSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out) { AP4_Result result = m_Cipher->EncryptSample(data_in, data_out, m_ByteOffset, false); if (AP4_FAILED(result)) return result; m_ByteOffset += data_in.GetDataSize(); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_HintTrackReader::BuildRtpPacket +---------------------------------------------------------------------*/ AP4_Result AP4_HintTrackReader::BuildRtpPacket(AP4_RtpPacket* packet, AP4_DataBuffer& packet_data) { // set the data size AP4_Result result = packet_data.SetDataSize(packet->GetConstructedDataSize()); if (AP4_FAILED(result)) return result; // now write AP4_ByteStream* stream = new AP4_MemoryByteStream(packet_data); // header + ssrc stream->WriteUI08(0x80 | (packet->GetPBit() << 5) | (packet->GetXBit() << 4)); stream->WriteUI08((packet->GetMBit() << 7) | packet->GetPayloadType()); stream->WriteUI16(m_RtpSequenceStart + packet->GetSequenceSeed()); stream->WriteUI32(m_RtpTimeStampStart + (AP4_UI32)m_CurrentHintSample.GetCts() + packet->GetTimeStampOffset()); stream->WriteUI32(m_Ssrc); AP4_List<AP4_RtpConstructor>::Item* constructors_it = packet->GetConstructors().FirstItem(); while (constructors_it != NULL) { AP4_RtpConstructor* constructor = constructors_it->GetData(); // add data to the packet according to the constructor switch (constructor->GetType()) { case AP4_RTP_CONSTRUCTOR_TYPE_NOOP: // nothing to do here break; case AP4_RTP_CONSTRUCTOR_TYPE_IMMEDIATE: result = WriteImmediateRtpData( static_cast<AP4_ImmediateRtpConstructor*>(constructor), stream); if (AP4_FAILED(result)) return result; break; case AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE: result = WriteSampleRtpData( static_cast<AP4_SampleRtpConstructor*>(constructor), stream); if (AP4_FAILED(result)) return result; break; case AP4_RTP_CONSTRUCTOR_TYPE_SAMPLE_DESC: return AP4_ERROR_NOT_SUPPORTED; default: // unknown constructor type return AP4_FAILURE; } // iterate constructors_it = constructors_it->GetNext(); } // release the stream stream->Release(); return result; }
/*---------------------------------------------------------------------- | AP4_OmaDcfTrackEncrypter::ProcessTrack +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfTrackEncrypter::ProcessTrack() { // original format AP4_FrmaAtom* frma = new AP4_FrmaAtom(m_SampleEntry->GetType()); // scheme info AP4_OdafAtom* odaf = new AP4_OdafAtom(true, 0, AP4_CIPHER_BLOCK_SIZE); AP4_OhdrAtom* ohdr = new AP4_OhdrAtom(m_CipherMode, m_CipherPadding, 0, m_ContentId.GetChars(), m_RightsIssuerUrl.GetChars(), m_TextualHeaders.GetData(), m_TextualHeaders.GetDataSize()); AP4_SchmAtom* schm = new AP4_SchmAtom(AP4_PROTECTION_SCHEME_TYPE_OMA, AP4_PROTECTION_SCHEME_VERSION_OMA_20); // populate the odkm container AP4_ContainerAtom* odkm = new AP4_ContainerAtom(AP4_ATOM_TYPE_ODKM, (AP4_UI32)0, (AP4_UI32)0); odkm->AddChild(odaf); odkm->AddChild(ohdr); // populate the schi container AP4_ContainerAtom* schi = new AP4_ContainerAtom(AP4_ATOM_TYPE_SCHI); schi->AddChild(odkm); // populate the sinf container AP4_ContainerAtom* sinf = new AP4_ContainerAtom(AP4_ATOM_TYPE_SINF); sinf->AddChild(frma); sinf->AddChild(schm); sinf->AddChild(schi); // add the sinf atom to the sample description m_SampleEntry->AddChild(sinf); // change the atom type of the sample description m_SampleEntry->SetType(m_Format); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_SgpdAtom::AP4_SgpdAtom +---------------------------------------------------------------------*/ AP4_SgpdAtom::AP4_SgpdAtom(AP4_UI32 size, AP4_UI08 version, AP4_UI32 flags, AP4_ByteStream& stream) : AP4_Atom(AP4_ATOM_TYPE_SGPD, size, version, flags), m_GroupingType(0), m_DefaultLength(0) { AP4_Size bytes_available = size-AP4_FULL_ATOM_HEADER_SIZE; stream.ReadUI32(m_GroupingType); bytes_available -= 4; if (version >= 1) { stream.ReadUI32(m_DefaultLength); bytes_available -= 4; } // read the number of entries AP4_UI32 entry_count = 0; AP4_Result result = stream.ReadUI32(entry_count); if (AP4_FAILED(result)) return; bytes_available -= 4; // read all entries for (unsigned int i=0; i<entry_count; i++) { AP4_UI32 description_length = m_DefaultLength; if (m_Version == 0) { // entry size unknown, read the whole thing description_length = bytes_available; } else { if (m_DefaultLength == 0) { description_length = stream.ReadUI32(description_length); } } if (description_length <= bytes_available) { AP4_DataBuffer* payload = new AP4_DataBuffer(description_length); payload->SetDataSize(description_length); stream.Read(payload->UseData(), description_length); m_Entries.Add(payload); } } }
/*---------------------------------------------------------------------- | AP4_IsmaCipher::EncryptSample +---------------------------------------------------------------------*/ AP4_Result AP4_IsmaCipher::EncryptSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out, AP4_Offset iv, bool skip_encryption) { // setup the buffers const unsigned char* in = data_in.GetData(); data_out.SetDataSize(data_in.GetDataSize()+4); unsigned char* out = data_out.UseData(); // IV on 4 bytes AP4_BytesFromUInt32BE(out, iv); out += 4; // encrypt the payload m_Cipher->SetStreamOffset(iv); m_Cipher->ProcessBuffer(in, out, data_in.GetDataSize()); return AP4_FAILURE; }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpTrackDecrypter:ProcessSample +---------------------------------------------------------------------*/ AP4_Result AP4_MarlinIpmpTrackDecrypter::ProcessSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out) { AP4_Result result; const AP4_UI08* in = data_in.GetData(); AP4_Size in_size = data_in.GetDataSize(); // default to 0 output data_out.SetDataSize(0); // check that we have at least the minimum size if (in_size < 2*AP4_AES_BLOCK_SIZE) return AP4_ERROR_INVALID_FORMAT; // process the sample data AP4_Size out_size = in_size-AP4_AES_BLOCK_SIZE; // worst case data_out.SetDataSize(out_size); AP4_UI08* out = data_out.UseData(); // decrypt the data m_Cipher->SetIV(in); result = m_Cipher->ProcessBuffer(in+AP4_AES_BLOCK_SIZE, in_size-AP4_AES_BLOCK_SIZE, out, &out_size, true); if (AP4_FAILED(result)) return result; // update the payload size data_out.SetDataSize(out_size); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpTrackDecrypter:GetProcessedSampleSize +---------------------------------------------------------------------*/ AP4_Size AP4_MarlinIpmpTrackDecrypter::GetProcessedSampleSize(AP4_Sample& sample) { // with CBC, we need to decrypt the last block to know what the padding was AP4_Size encrypted_size = sample.GetSize()-AP4_AES_BLOCK_SIZE; AP4_DataBuffer encrypted; AP4_DataBuffer decrypted; AP4_Size decrypted_size = AP4_CIPHER_BLOCK_SIZE; if (sample.GetSize() < 2*AP4_CIPHER_BLOCK_SIZE) { return 0; } AP4_Size offset = sample.GetSize()-2*AP4_CIPHER_BLOCK_SIZE; if (AP4_FAILED(sample.ReadData(encrypted, 2*AP4_CIPHER_BLOCK_SIZE, offset))) { return 0; } decrypted.Reserve(decrypted_size); m_Cipher->SetIV(encrypted.GetData()); if (AP4_FAILED(m_Cipher->ProcessBuffer(encrypted.GetData()+AP4_CIPHER_BLOCK_SIZE, AP4_CIPHER_BLOCK_SIZE, decrypted.UseData(), &decrypted_size, true))) { return 0; } unsigned int padding_size = AP4_CIPHER_BLOCK_SIZE-decrypted_size; return encrypted_size-padding_size; }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpTrackEncrypter:ProcessSample +---------------------------------------------------------------------*/ AP4_Result AP4_MarlinIpmpTrackEncrypter::ProcessSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out) { AP4_Result result; const AP4_UI08* in = data_in.GetData(); AP4_Size in_size = data_in.GetDataSize(); // default to 0 output data_out.SetDataSize(0); // process the sample data AP4_Size out_size = AP4_CIPHER_BLOCK_SIZE*(2+(in_size/AP4_CIPHER_BLOCK_SIZE)); data_out.SetDataSize(out_size); AP4_UI08* out = data_out.UseData(); // write the IV AP4_CopyMemory(out, m_IV, AP4_CIPHER_BLOCK_SIZE); out_size -= AP4_CIPHER_BLOCK_SIZE; // encrypt the data m_Cipher->SetIV(m_IV); result = m_Cipher->ProcessBuffer(in, in_size, out+AP4_AES_BLOCK_SIZE, &out_size, true); if (AP4_FAILED(result)) return result; // update the payload size data_out.SetDataSize(out_size+AP4_AES_BLOCK_SIZE); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | ShowSample +---------------------------------------------------------------------*/ static void ShowSample(AP4_Sample& sample, unsigned int index, AP4_SampleDecrypter* sample_decrypter) { printf("[%06d] size=%6d duration=%6d", index, (int)sample.GetSize(), (int)sample.GetDuration()); printf(" offset=%10lld dts=%10lld cts=%10lld ", sample.GetOffset(), sample.GetDts(), sample.GetCts()); if (sample.IsSync()) { printf(" [S] "); } else { printf(" "); } AP4_DataBuffer sample_data; sample.ReadData(sample_data); AP4_DataBuffer* data = &sample_data; AP4_DataBuffer decrypted_sample_data; if (sample_decrypter) { sample_decrypter->DecryptSampleData(sample_data, decrypted_sample_data); data = & decrypted_sample_data; } unsigned int show = data->GetDataSize(); if (show > 12) show = 12; // max first 12 chars for (unsigned int i=0; i<show; i++) { printf("%02x", data->GetData()[i]); } if (show == data->GetDataSize()) { printf("\n"); } else { printf("...\n"); } }
/*---------------------------------------------------------------------- | AP4_OmaDcfTrackEncrypter::ProcessSample +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfTrackEncrypter::ProcessSample(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out) { AP4_Result result = m_Cipher->EncryptSampleData(data_in, data_out, m_Counter, false); if (AP4_FAILED(result)) return result; m_Counter += (data_in.GetDataSize()+AP4_CIPHER_BLOCK_SIZE-1)/AP4_CIPHER_BLOCK_SIZE; return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | WV_CencSingleSampleDecrypter::DecryptSampleData +---------------------------------------------------------------------*/ AP4_Result WV_CencSingleSampleDecrypter::DecryptSampleData( AP4_DataBuffer& data_in, AP4_DataBuffer& data_out, const AP4_UI08* iv, unsigned int subsample_count, const AP4_UI16* bytes_of_cleartext_data, const AP4_UI32* bytes_of_encrypted_data) { // the output has the same size as the input data_out.SetDataSize(data_in.GetDataSize()); if (!wv_adapter) { data_out.SetData(data_in.GetData(), data_in.GetDataSize()); return AP4_SUCCESS; } // check input parameters if (iv == NULL) return AP4_ERROR_INVALID_PARAMETERS; if (subsample_count) { if (bytes_of_cleartext_data == NULL || bytes_of_encrypted_data == NULL) { return AP4_ERROR_INVALID_PARAMETERS; } } // transform ap4 format into cmd format cdm::InputBuffer cdm_in; if (subsample_count > max_subsample_count_) { subsample_buffer_ = (cdm::SubsampleEntry*)realloc(subsample_buffer_, subsample_count*sizeof(cdm::SubsampleEntry)); max_subsample_count_ = subsample_count; } for (cdm::SubsampleEntry *b(subsample_buffer_), *e(subsample_buffer_ + subsample_count); b != e; ++b, ++bytes_of_cleartext_data, ++bytes_of_encrypted_data) { b->clear_bytes = *bytes_of_cleartext_data; b->cipher_bytes = *bytes_of_encrypted_data; } cdm_in.data = data_in.GetData(); cdm_in.data_size = data_in.GetDataSize(); cdm_in.iv = iv; cdm_in.iv_size = 16; //Always 16, see AP4_CencSingleSampleDecrypter declaration. cdm_in.key_id = wv_adapter->GetKeyId(); cdm_in.key_id_size = wv_adapter->GetKeyIdSize(); cdm_in.num_subsamples = subsample_count; cdm_in.subsamples = subsample_buffer_; CdmBuffer buf(&data_out); CdmDecryptedBlock cdm_out; cdm_out.SetDecryptedBuffer(&buf); cdm::Status ret = wv_adapter->Decrypt(cdm_in, &cdm_out); return (ret == cdm::Status::kSuccess) ? AP4_SUCCESS : AP4_ERROR_INVALID_PARAMETERS; }
/*---------------------------------------------------------------------- | 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_OmaDcfCbcSampleDecrypter::GetDecryptedSampleSize +---------------------------------------------------------------------*/ AP4_Size AP4_OmaDcfCbcSampleDecrypter::GetDecryptedSampleSize(AP4_Sample& sample) { if (m_Cipher == NULL) return 0; // decide if this sample is encrypted or not bool is_encrypted; if (m_SelectiveEncryption) { // read the first byte to see if the sample is encrypted or not AP4_Byte h; AP4_DataBuffer peek_buffer; peek_buffer.SetBuffer(&h, 1); sample.ReadData(peek_buffer, 1); is_encrypted = ((h&0x80)!=0); } else { is_encrypted = true; } if (is_encrypted) { // with CBC, we need to decrypt the last block to know what the padding was AP4_Size crypto_header_size = (m_SelectiveEncryption?1:0)+m_IvLength; AP4_Size encrypted_size = sample.GetSize()-crypto_header_size; AP4_DataBuffer encrypted; AP4_DataBuffer decrypted; AP4_Size decrypted_size = AP4_CIPHER_BLOCK_SIZE; if (sample.GetSize() < crypto_header_size+AP4_CIPHER_BLOCK_SIZE) { return 0; } AP4_Size offset = sample.GetSize()-2*AP4_CIPHER_BLOCK_SIZE; if (AP4_FAILED(sample.ReadData(encrypted, 2*AP4_CIPHER_BLOCK_SIZE, offset))) { return 0; } decrypted.Reserve(decrypted_size); m_Cipher->SetIV(encrypted.GetData()); if (AP4_FAILED(m_Cipher->ProcessBuffer(encrypted.GetData()+AP4_CIPHER_BLOCK_SIZE, AP4_CIPHER_BLOCK_SIZE, decrypted.UseData(), &decrypted_size, true))) { return 0; } unsigned int padding_size = AP4_CIPHER_BLOCK_SIZE-decrypted_size; return encrypted_size-padding_size; } else { return sample.GetSize()-(m_SelectiveEncryption?1:0); } }
/*---------------------------------------------------------------------- | AP4_OmaDcfCtrSampleDecrypter::DecryptSampleData +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfCtrSampleDecrypter::DecryptSampleData(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out, const AP4_UI08* /*iv*/) { bool is_encrypted = true; const unsigned char* in = data_in.GetData(); AP4_Size in_size = data_in.GetDataSize(); // default to 0 output AP4_CHECK(data_out.SetDataSize(0)); // check the selective encryption flag if (m_SelectiveEncryption) { if (in_size < 1) return AP4_ERROR_INVALID_FORMAT; is_encrypted = ((in[0]&0x80)!=0); in++; } // check the size unsigned int header_size = (m_SelectiveEncryption?1:0)+(is_encrypted?m_IvLength:0); if (header_size > in_size) return AP4_ERROR_INVALID_FORMAT; // process the sample data AP4_Size payload_size = in_size-header_size; AP4_CHECK(data_out.Reserve(payload_size)); unsigned char* out = data_out.UseData(); if (is_encrypted) { // set the IV if (m_IvLength == 16) { m_Cipher->SetIV(in); } else { AP4_UI08 iv[16]; AP4_SetMemory(iv, 0, 16); AP4_CopyMemory(iv+16-m_IvLength, in, m_IvLength); m_Cipher->SetIV(iv); } AP4_CHECK(m_Cipher->ProcessBuffer(in+m_IvLength, payload_size, out)); } else { AP4_CopyMemory(out, in, payload_size); } AP4_CHECK(data_out.SetDataSize(payload_size)); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_OmaDbcCbcSampleDecrypter::DecryptSampleData +---------------------------------------------------------------------*/ AP4_Result AP4_OmaDcfCbcSampleDecrypter::DecryptSampleData(AP4_DataBuffer& data_in, AP4_DataBuffer& data_out, const AP4_UI08* /*iv*/) { bool is_encrypted = true; const unsigned char* in = data_in.GetData(); AP4_Size in_size = data_in.GetDataSize(); AP4_Size out_size; // default to 0 output AP4_CHECK(data_out.SetDataSize(0)); // check the selective encryption flag if (m_SelectiveEncryption) { if (in_size < 1) return AP4_ERROR_INVALID_FORMAT; is_encrypted = ((in[0]&0x80)!=0); in++; } // check the size unsigned int header_size = (m_SelectiveEncryption?1:0)+(is_encrypted?m_IvLength:0); if (header_size > in_size) return AP4_ERROR_INVALID_FORMAT; // process the sample data unsigned int payload_size = in_size-header_size; data_out.Reserve(payload_size); unsigned char* out = data_out.UseData(); if (is_encrypted) { // get the IV const AP4_UI08* iv = (const AP4_UI08*)in; in += AP4_CIPHER_BLOCK_SIZE; m_Cipher->SetIV(iv); out_size = payload_size; AP4_CHECK(m_Cipher->ProcessBuffer(in, payload_size, out, &out_size, true)); } else { AP4_CopyMemory(out, in, payload_size); out_size = payload_size; } AP4_CHECK(data_out.SetDataSize(out_size)); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | MakeFramePrefix +---------------------------------------------------------------------*/ static AP4_Result MakeFramePrefix(AP4_SampleDescription* sdesc, AP4_DataBuffer& prefix, unsigned int& nalu_length_size) { AP4_AvcSampleDescription* avc_desc = AP4_DYNAMIC_CAST(AP4_AvcSampleDescription, sdesc); if (avc_desc == NULL) { fprintf(stderr, "ERROR: track does not contain an AVC stream\n"); return AP4_FAILURE; } if (sdesc->GetFormat() == AP4_SAMPLE_FORMAT_AVC3 || sdesc->GetFormat() == AP4_SAMPLE_FORMAT_AVC4) { // no need for a prefix, SPS/PPS NALs should be in the elementary stream already return AP4_SUCCESS; } // make the SPS/PPS prefix nalu_length_size = avc_desc->GetNaluLengthSize(); for (unsigned int i=0; i<avc_desc->GetSequenceParameters().ItemCount(); i++) { AP4_DataBuffer& buffer = avc_desc->GetSequenceParameters()[i]; unsigned int prefix_size = prefix.GetDataSize(); prefix.SetDataSize(prefix_size+4+buffer.GetDataSize()); unsigned char* p = prefix.UseData()+prefix_size; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize()); } for (unsigned int i=0; i<avc_desc->GetPictureParameters().ItemCount(); i++) { AP4_DataBuffer& buffer = avc_desc->GetPictureParameters()[i]; unsigned int prefix_size = prefix.GetDataSize(); prefix.SetDataSize(prefix_size+4+buffer.GetDataSize()); unsigned char* p = prefix.UseData()+prefix_size; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize()); } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | WriteSample +---------------------------------------------------------------------*/ static void WriteSample(const AP4_DataBuffer& sample_data, AP4_DataBuffer& prefix, unsigned int nalu_length_size, AP4_ByteStream* output) { const unsigned char* data = sample_data.GetData(); unsigned int data_size = sample_data.GetDataSize(); // allocate a buffer for the PES packet AP4_DataBuffer frame_data; unsigned char* frame_buffer = NULL; // add a delimiter if we don't already have one bool have_access_unit_delimiter = (data_size > nalu_length_size) && ((data[nalu_length_size] & 0x1F) == AP4_AVC_NAL_UNIT_TYPE_ACCESS_UNIT_DELIMITER); if (!have_access_unit_delimiter) { AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+6); frame_buffer = frame_data.UseData()+frame_data_size; // start of access unit frame_buffer[0] = 0; frame_buffer[1] = 0; frame_buffer[2] = 0; frame_buffer[3] = 1; frame_buffer[4] = 9; // NAL type = Access Unit Delimiter; frame_buffer[5] = 0xE0; // Slice types = ANY } // write the NAL units bool prefix_added = false; while (data_size) { // sanity check if (data_size < nalu_length_size) break; // get the next NAL unit AP4_UI32 nalu_size; if (nalu_length_size == 1) { nalu_size = *data++; data_size--; } else if (nalu_length_size == 2) { nalu_size = AP4_BytesToInt16BE(data); data += 2; data_size -= 2; } else if (nalu_length_size == 4) { nalu_size = AP4_BytesToInt32BE(data); data += 4; data_size -= 4; } else { break; } if (nalu_size > data_size) break; // add the prefix if needed if (prefix.GetDataSize() && !prefix_added && !have_access_unit_delimiter) { AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+prefix.GetDataSize()); frame_buffer = frame_data.UseData()+frame_data_size; AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize()); prefix_added = true; } // add a start code before the NAL unit AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+3+nalu_size); frame_buffer = frame_data.UseData()+frame_data_size; frame_buffer[0] = 0; frame_buffer[1] = 0; frame_buffer[2] = 1; AP4_CopyMemory(frame_buffer+3, data, nalu_size); // add the prefix if needed if (prefix.GetDataSize() && !prefix_added) { AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+prefix.GetDataSize()); frame_buffer = frame_data.UseData()+frame_data_size; AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize()); prefix_added = true; } // move to the next NAL unit data += nalu_size; data_size -= nalu_size; } output->Write(frame_data.GetData(), frame_data.GetDataSize()); }
/*---------------------------------------------------------------------- | WriteSample +---------------------------------------------------------------------*/ static void WriteSample(const AP4_DataBuffer& sample_data, AP4_DataBuffer& prefix, unsigned int nalu_length_size, AP4_ByteStream* output) { const unsigned char* data = sample_data.GetData(); unsigned int data_size = sample_data.GetDataSize(); // detect if we have VPS/SPS/PPS and/or AUD NAL units already bool have_param_sets = false; bool have_access_unit_delimiter = false; while (data_size) { // sanity check if (data_size < nalu_length_size) break; // get the next NAL unit AP4_UI32 nalu_size; if (nalu_length_size == 1) { nalu_size = *data++; data_size--; } else if (nalu_length_size == 2) { nalu_size = AP4_BytesToInt16BE(data); data += 2; data_size -= 2; } else if (nalu_length_size == 4) { nalu_size = AP4_BytesToInt32BE(data); data += 4; data_size -= 4; } else { break; } if (nalu_size > data_size) break; unsigned int nal_unit_type = (data[0]>>1)&0x3F; if (nal_unit_type == AP4_HEVC_NALU_TYPE_AUD_NUT) { have_access_unit_delimiter = true; } if (nal_unit_type == AP4_HEVC_NALU_TYPE_VPS_NUT || nal_unit_type == AP4_HEVC_NALU_TYPE_SPS_NUT || nal_unit_type == AP4_HEVC_NALU_TYPE_PPS_NUT) { have_param_sets = true; break; } // move to the next NAL unit data += nalu_size; data_size -= nalu_size; } data = sample_data.GetData(); data_size = sample_data.GetDataSize(); // allocate a buffer for the frame data AP4_DataBuffer frame_data; unsigned char* frame_buffer = NULL; // add a delimiter if we don't already have one if (data_size && !have_access_unit_delimiter) { AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+7); frame_buffer = frame_data.UseData()+frame_data_size; // start of access unit frame_buffer[0] = 0; frame_buffer[1] = 0; frame_buffer[2] = 0; frame_buffer[3] = 1; frame_buffer[4] = AP4_HEVC_NALU_TYPE_AUD_NUT<<1; frame_buffer[5] = 1; frame_buffer[6] = 0x40; // pic_type = 2 (B,P,I) } // write the NAL units bool prefix_added = false; while (data_size) { // sanity check if (data_size < nalu_length_size) break; // get the next NAL unit AP4_UI32 nalu_size; if (nalu_length_size == 1) { nalu_size = *data++; data_size--; } else if (nalu_length_size == 2) { nalu_size = AP4_BytesToInt16BE(data); data += 2; data_size -= 2; } else if (nalu_length_size == 4) { nalu_size = AP4_BytesToInt32BE(data); data += 4; data_size -= 4; } else { break; } if (nalu_size > data_size) break; // add the prefix if needed if (!have_param_sets && !prefix_added && !have_access_unit_delimiter) { AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+prefix.GetDataSize()); frame_buffer = frame_data.UseData()+frame_data_size; AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize()); prefix_added = true; } // add a start code before the NAL unit AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+3+nalu_size); frame_buffer = frame_data.UseData()+frame_data_size; frame_buffer[0] = 0; frame_buffer[1] = 0; frame_buffer[2] = 1; AP4_CopyMemory(frame_buffer+3, data, nalu_size); // add the prefix if needed if (!have_param_sets && !prefix_added) { AP4_Size frame_data_size = frame_data.GetDataSize(); frame_data.SetDataSize(frame_data_size+prefix.GetDataSize()); frame_buffer = frame_data.UseData()+frame_data_size; AP4_CopyMemory(frame_buffer, prefix.GetData(), prefix.GetDataSize()); prefix_added = true; } // move to the next NAL unit data += nalu_size; data_size -= nalu_size; } output->Write(frame_data.GetData(), frame_data.GetDataSize()); }
/*---------------------------------------------------------------------- | MakeFramePrefix +---------------------------------------------------------------------*/ static AP4_Result MakeFramePrefix(AP4_SampleDescription* sdesc, AP4_DataBuffer& prefix, unsigned int& nalu_length_size) { AP4_HevcSampleDescription* hevc_desc = AP4_DYNAMIC_CAST(AP4_HevcSampleDescription, sdesc); if (hevc_desc == NULL) { fprintf(stderr, "ERROR: track does not contain an HEVC stream\n"); return AP4_FAILURE; } // extract the nalu length size nalu_length_size = hevc_desc->GetNaluLengthSize(); // make the VPS/SPS/PPS prefix for (unsigned int i=0; i<hevc_desc->GetSequences().ItemCount(); i++) { const AP4_HvccAtom::Sequence& seq = hevc_desc->GetSequences()[i]; if (seq.m_NaluType == AP4_HEVC_NALU_TYPE_VPS_NUT) { for (unsigned int j=0; j<seq.m_Nalus.ItemCount(); j++) { const AP4_DataBuffer& buffer = seq.m_Nalus[j]; unsigned int prefix_size = prefix.GetDataSize(); prefix.SetDataSize(prefix_size+4+buffer.GetDataSize()); unsigned char* p = prefix.UseData()+prefix_size; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize()); } } } for (unsigned int i=0; i<hevc_desc->GetSequences().ItemCount(); i++) { const AP4_HvccAtom::Sequence& seq = hevc_desc->GetSequences()[i]; if (seq.m_NaluType == AP4_HEVC_NALU_TYPE_SPS_NUT) { for (unsigned int j=0; j<seq.m_Nalus.ItemCount(); j++) { const AP4_DataBuffer& buffer = seq.m_Nalus[j]; unsigned int prefix_size = prefix.GetDataSize(); prefix.SetDataSize(prefix_size+4+buffer.GetDataSize()); unsigned char* p = prefix.UseData()+prefix_size; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize()); } } } for (unsigned int i=0; i<hevc_desc->GetSequences().ItemCount(); i++) { const AP4_HvccAtom::Sequence& seq = hevc_desc->GetSequences()[i]; if (seq.m_NaluType == AP4_HEVC_NALU_TYPE_PPS_NUT) { for (unsigned int j=0; j<seq.m_Nalus.ItemCount(); j++) { const AP4_DataBuffer& buffer = seq.m_Nalus[j]; unsigned int prefix_size = prefix.GetDataSize(); prefix.SetDataSize(prefix_size+4+buffer.GetDataSize()); unsigned char* p = prefix.UseData()+prefix_size; *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; AP4_CopyMemory(p, buffer.GetData(), buffer.GetDataSize()); } } } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | main +---------------------------------------------------------------------*/ int main(int argc, char** argv) { if (argc == 1) PrintUsageAndExit(); // parse options const char* kms_uri = NULL; enum Method method = METHOD_NONE; const char* input_filename = NULL; const char* output_filename = NULL; const char* fragments_info_filename = NULL; AP4_ProtectionKeyMap key_map; AP4_TrackPropertyMap property_map; bool show_progress = false; bool strict = false; AP4_Array<AP4_PsshAtom*> pssh_atoms; AP4_DataBuffer kids; unsigned int kid_count = 0; AP4_Result result; // parse the command line arguments char* arg; while ((arg = *++argv)) { if (!strcmp(arg, "--method")) { arg = *++argv; if (arg == NULL) { fprintf(stderr, "ERROR: missing argument for --method option\n"); return 1; } if (!strcmp(arg, "OMA-PDCF-CBC")) { method = METHOD_OMA_PDCF_CBC; } else if (!strcmp(arg, "OMA-PDCF-CTR")) { method = METHOD_OMA_PDCF_CTR; } else if (!strcmp(arg, "MARLIN-IPMP-ACBC")) { method = METHOD_MARLIN_IPMP_ACBC; } else if (!strcmp(arg, "MARLIN-IPMP-ACGK")) { method = METHOD_MARLIN_IPMP_ACGK; } else if (!strcmp(arg, "PIFF-CBC")) { method = METHOD_PIFF_CBC; } else if (!strcmp(arg, "PIFF-CTR")) { method = METHOD_PIFF_CTR; } else if (!strcmp(arg, "MPEG-CENC")) { method = METHOD_MPEG_CENC; } else if (!strcmp(arg, "ISMA-IAEC")) { method = METHOD_ISMA_AES; } else { fprintf(stderr, "ERROR: invalid value for --method argument\n"); return 1; } } else if (!strcmp(arg, "--fragments-info")) { arg = *++argv; if (arg == NULL) { fprintf(stderr, "ERROR: missing argument for --fragments-info option\n"); return 1; } fragments_info_filename = arg; } else if (!strcmp(arg, "--pssh") || !strcmp(arg, "--pssh-v1")) { bool v1 = (strcmp(arg, "--pssh-v1") == 0); arg = *++argv; if (arg == NULL) { fprintf(stderr, "ERROR: missing argument for --pssh\n"); return 1; } if (AP4_StringLength(arg) < 32+1 || arg[32] != ':') { fprintf(stderr, "ERROR: invalid argument syntax for --pssh\n"); return 1; } unsigned char system_id[16]; arg[32] = '\0'; result = AP4_ParseHex(arg, system_id, 16); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: invalid argument syntax for --pssh\n"); return 1; } const char* pssh_filename = arg+33; // load the pssh payload AP4_DataBuffer pssh_payload; if (pssh_filename[0]) { AP4_ByteStream* pssh_input = NULL; result = AP4_FileByteStream::Create(pssh_filename, AP4_FileByteStream::STREAM_MODE_READ, pssh_input); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open pssh payload file (%d)\n", result); return 1; } AP4_LargeSize pssh_payload_size = 0; pssh_input->GetSize(pssh_payload_size); pssh_payload.SetDataSize((AP4_Size)pssh_payload_size); result = pssh_input->Read(pssh_payload.UseData(), (AP4_Size)pssh_payload_size); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot read pssh payload from file (%d)\n", result); return 1; } } AP4_PsshAtom* pssh; if (v1) { if (kid_count) { pssh = new AP4_PsshAtom(system_id, kids.GetData(), kid_count); } else { pssh = new AP4_PsshAtom(system_id); } } else { pssh = new AP4_PsshAtom(system_id); } if (pssh_payload.GetDataSize()) { pssh->SetData(pssh_payload.GetData(), pssh_payload.GetDataSize()); } pssh_atoms.Append(pssh); } else if (!strcmp(arg, "--kms-uri")) { arg = *++argv; if (arg == NULL) { fprintf(stderr, "ERROR: missing argument for --kms-uri option\n"); return 1; } if (method != METHOD_ISMA_AES) { fprintf(stderr, "ERROR: --kms-uri only applies to method ISMA-IAEC\n"); return 1; } kms_uri = arg; } else if (!strcmp(arg, "--show-progress")) { show_progress = true; } else if (!strcmp(arg, "--strict")) { strict = true; } else if (!strcmp(arg, "--key")) { if (method == METHOD_NONE) { fprintf(stderr, "ERROR: --method argument must appear before --key\n"); return 1; } arg = *++argv; if (arg == NULL) { fprintf(stderr, "ERROR: missing argument for --key option\n"); return 1; } char* track_ascii = NULL; char* key_ascii = NULL; char* iv_ascii = NULL; if (AP4_FAILED(AP4_SplitArgs(arg, track_ascii, key_ascii, iv_ascii))) { fprintf(stderr, "ERROR: invalid argument for --key option\n"); return 1; } unsigned int track = strtoul(track_ascii, NULL, 10); // parse the key value unsigned char key[16]; AP4_SetMemory(key, 0, sizeof(key)); if (AP4_CompareStrings(key_ascii, "random") == 0) { result = AP4_System_GenerateRandomBytes(key, 16); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: failed to generate random key (%d)\n", result); return 1; } char key_hex[32+1]; key_hex[32] = '\0'; AP4_FormatHex(key, 16, key_hex); printf("KEY.%d=%s\n", track, key_hex); } else { if (AP4_ParseHex(key_ascii, key, 16)) { fprintf(stderr, "ERROR: invalid hex format for key\n"); return 1; } } // parse the iv unsigned char iv[16]; AP4_SetMemory(iv, 0, sizeof(iv)); if (AP4_CompareStrings(iv_ascii, "random") == 0) { result = AP4_System_GenerateRandomBytes(iv, 16); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: failed to generate random key (%d)\n", result); return 1; } iv[0] &= 0x7F; // always set the MSB to 0 so we don't have wraparounds } else { unsigned int iv_size = (unsigned int)AP4_StringLength(iv_ascii)/2; if (AP4_ParseHex(iv_ascii, iv, iv_size)) { fprintf(stderr, "ERROR: invalid hex format for iv\n"); return 1; } } switch (method) { case METHOD_OMA_PDCF_CTR: case METHOD_ISMA_AES: case METHOD_PIFF_CTR: case METHOD_MPEG_CENC: // truncate the IV AP4_SetMemory(&iv[8], 0, 8); break; default: break; } // check that the key is not already there if (key_map.GetKey(track)) { fprintf(stderr, "ERROR: key already set for track %d\n", track); return 1; } // set the key in the map key_map.SetKey(track, key, 16, iv, 16); } else if (!strcmp(arg, "--property")) { char* track_ascii = NULL; char* name = NULL; char* value = NULL; if (method != METHOD_OMA_PDCF_CBC && method != METHOD_OMA_PDCF_CTR && method != METHOD_MARLIN_IPMP_ACBC && method != METHOD_MARLIN_IPMP_ACGK && method != METHOD_PIFF_CBC && method != METHOD_PIFF_CTR && method != METHOD_MPEG_CENC) { fprintf(stderr, "ERROR: this method does not use properties\n"); return 1; } arg = *++argv; if (arg == NULL) { fprintf(stderr, "ERROR: missing argument for --property option\n"); return 1; } if (AP4_FAILED(AP4_SplitArgs(arg, track_ascii, name, value))) { fprintf(stderr, "ERROR: invalid argument for --property option\n"); return 1; } unsigned int track = strtoul(track_ascii, NULL, 10); // check that the property is not already set if (property_map.GetProperty(track, name)) { fprintf(stderr, "ERROR: property %s already set for track %d\n", name, track); return 1; } // set the property in the map property_map.SetProperty(track, name, value); // special treatment for KID properties if (!strcmp(name, "KID")) { if (AP4_StringLength(value) != 32) { fprintf(stderr, "ERROR: invalid size for KID property (must be 32 hex chars)\n"); return 1; } AP4_UI08 kid[16]; if (AP4_FAILED(AP4_ParseHex(value, kid, 16))) { fprintf(stderr, "ERROR: invalid syntax for KID property (must be 32 hex chars)\n"); return 1; } // check if we already have this KID bool kid_already_present = false; for (unsigned int i=0; i<kid_count; i++) { if (AP4_CompareMemory(kids.GetData()+(i*16), kid, 16) == 0) { kid_already_present = true; break; } } if (!kid_already_present) { ++kid_count; kids.AppendData(kid, 16); } } } else if (!strcmp(arg, "--global-option")) { arg = *++argv; char* name = NULL; char* value = NULL; if (arg == NULL) { fprintf(stderr, "ERROR: missing argument for --global-option option\n"); return 1; } if (AP4_FAILED(AP4_SplitArgs(arg, name, value))) { fprintf(stderr, "ERROR: invalid argument for --global-option option\n"); return 1; } AP4_GlobalOptions::SetString(name, value); } else if (input_filename == NULL) { input_filename = arg; } else if (output_filename == NULL) { output_filename = arg; } else { fprintf(stderr, "ERROR: unexpected argument (%s)\n", arg); return 1; } } // check the arguments if (method == METHOD_NONE) { fprintf(stderr, "ERROR: missing --method argument\n"); return 1; } if (input_filename == NULL) { fprintf(stderr, "ERROR: missing input filename\n"); return 1; } if (output_filename == NULL) { fprintf(stderr, "ERROR: missing output filename\n"); return 1; } // create an encrypting processor AP4_Processor* processor = NULL; if (method == METHOD_ISMA_AES) { if (kms_uri == NULL) { fprintf(stderr, "ERROR: method ISMA-IAEC requires --kms-uri\n"); return 1; } AP4_IsmaEncryptingProcessor* isma_processor = new AP4_IsmaEncryptingProcessor(kms_uri); isma_processor->GetKeyMap().SetKeys(key_map); processor = isma_processor; } else if (method == METHOD_MARLIN_IPMP_ACBC || method == METHOD_MARLIN_IPMP_ACGK) { bool use_group_key = (method == METHOD_MARLIN_IPMP_ACGK); if (use_group_key) { // check that the group key is set if (key_map.GetKey(0) == NULL) { fprintf(stderr, "ERROR: method MARLIN-IPMP-ACGK requires a group key\n"); return 1; } } AP4_MarlinIpmpEncryptingProcessor* marlin_processor = new AP4_MarlinIpmpEncryptingProcessor(use_group_key); marlin_processor->GetKeyMap().SetKeys(key_map); marlin_processor->GetPropertyMap().SetProperties(property_map); processor = marlin_processor; } else if (method == METHOD_OMA_PDCF_CTR || method == METHOD_OMA_PDCF_CBC) { AP4_OmaDcfEncryptingProcessor* oma_processor = new AP4_OmaDcfEncryptingProcessor(method == METHOD_OMA_PDCF_CTR? AP4_OMA_DCF_CIPHER_MODE_CTR : AP4_OMA_DCF_CIPHER_MODE_CBC); oma_processor->GetKeyMap().SetKeys(key_map); oma_processor->GetPropertyMap().SetProperties(property_map); processor = oma_processor; } else if (method == METHOD_PIFF_CTR || method == METHOD_PIFF_CBC || method == METHOD_MPEG_CENC) { AP4_CencVariant variant = AP4_CENC_VARIANT_MPEG; switch (method) { case METHOD_PIFF_CBC: variant = AP4_CENC_VARIANT_PIFF_CBC; break; case METHOD_PIFF_CTR: variant = AP4_CENC_VARIANT_PIFF_CTR; break; case METHOD_MPEG_CENC: variant = AP4_CENC_VARIANT_MPEG; break; default: break; } AP4_CencEncryptingProcessor* cenc_processor = new AP4_CencEncryptingProcessor(variant); cenc_processor->GetKeyMap().SetKeys(key_map); cenc_processor->GetPropertyMap().SetProperties(property_map); for (unsigned int i=0; i<pssh_atoms.ItemCount(); i++) { cenc_processor->GetPsshAtoms().Append(pssh_atoms[i]); } processor = cenc_processor; } // create the input stream AP4_ByteStream* input = NULL; 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; } // create the output stream AP4_ByteStream* output = NULL; result = AP4_FileByteStream::Create(output_filename, AP4_FileByteStream::STREAM_MODE_WRITE, output); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open output file (%s)\n", output_filename); return 1; } // create the fragments info stream if needed AP4_ByteStream* fragments_info = NULL; if (fragments_info_filename) { result = AP4_FileByteStream::Create(fragments_info_filename, AP4_FileByteStream::STREAM_MODE_READ, fragments_info); if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: cannot open fragments info file (%s)\n", fragments_info_filename); return 1; } } // process/decrypt the file ProgressListener listener; if (fragments_info) { bool check = CheckWarning(*fragments_info, key_map, method); if (strict && check) return 1; result = processor->Process(*input, *output, *fragments_info, show_progress?&listener:NULL); } else { bool check = CheckWarning(*input, key_map, method); if (strict && check) return 1; result = processor->Process(*input, *output, show_progress?&listener:NULL); } if (AP4_FAILED(result)) { fprintf(stderr, "ERROR: failed to process the file (%d)\n", result); } // cleanup delete processor; input->Release(); output->Release(); if (fragments_info) fragments_info->Release(); for (unsigned int i=0; i<pssh_atoms.ItemCount(); i++) { delete pssh_atoms[i]; } return 0; }
static prMALError SDKImportAudio7( imStdParms *stdParms, imFileRef SDKfileRef, imImportAudioRec7 *audioRec7) { prMALError result = malNoError; // privateData ImporterLocalRec8H ldataH = reinterpret_cast<ImporterLocalRec8H>(audioRec7->privateData); stdParms->piSuites->memFuncs->lockHandle(reinterpret_cast<char**>(ldataH)); ImporterLocalRec8Ptr localRecP = reinterpret_cast<ImporterLocalRec8Ptr>( *ldataH ); if(localRecP && localRecP->audio_track && localRecP->alac) { assert(localRecP->reader != NULL); assert(localRecP->file != NULL && localRecP->file->GetMovie() != NULL); assert(audioRec7->position >= 0); // Do they really want contiguous samples? assert(audioRec7->position < localRecP->duration); if(audioRec7->size > localRecP->duration - audioRec7->position) { // this does happen, we get asked for audio data past the duration // let's make sure there's no garbage there and re-set audioRec7->size for(int c=0; c < localRecP->numChannels; c++) { memset(audioRec7->buffer[c], 0, sizeof(float) * audioRec7->size); } audioRec7->size = localRecP->duration - audioRec7->position; } const AP4_UI32 timestamp_ms = audioRec7->position * 1000 / localRecP->audioSampleRate; const size_t bytes_per_sample = (localRecP->alac->mConfig.bitDepth <= 16 ? 2 : 4); const size_t alac_buf_size = localRecP->alac->mConfig.frameLength * localRecP->alac->mConfig.numChannels * bytes_per_sample + kALACMaxEscapeHeaderBytes; uint8_t *alac_buffer = (uint8_t *)malloc(alac_buf_size); AP4_Ordinal sample_index = 0; AP4_Result ap4_result = localRecP->audio_track->GetSampleIndexForTimeStampMs(timestamp_ms, sample_index); if(ap4_result == AP4_SUCCESS) { // for surround channels // Premiere uses Left, Right, Left Rear, Right Rear, Center, LFE // ALAC uses Center, Left, Right, Left Rear, Right Rear, LFE // http://alac.macosforge.org/trac/browser/trunk/ReadMe.txt static const int surround_swizzle[] = {4, 0, 1, 2, 3, 5}; static const int stereo_swizzle[] = {0, 1, 2, 3, 4, 5}; // no swizzle, actually const int *swizzle = localRecP->numChannels > 2 ? surround_swizzle : stereo_swizzle; csSDK_uint32 samples_needed = audioRec7->size; PrAudioSample pos = 0; AP4_DataBuffer dataBuffer; while(samples_needed > 0 && ap4_result == AP4_SUCCESS && result == malNoError) { AP4_Sample sample; ap4_result = localRecP->audio_track->ReadSample(sample_index, sample, dataBuffer); if(ap4_result == AP4_SUCCESS) { const PrAudioSample sample_pos = sample.GetDts() * localRecP->audioSampleRate / localRecP->audio_track->GetMediaTimeScale(); const PrAudioSample sample_len = sample.GetDuration() * localRecP->audioSampleRate / localRecP->audio_track->GetMediaTimeScale(); const PrAudioSample skip_samples = (audioRec7->position > sample_pos) ? (audioRec7->position - sample_pos) : 0; long samples_to_read = sample_len - skip_samples; if(samples_to_read > samples_needed) samples_to_read = samples_needed; else if(samples_to_read < 0) samples_to_read = 0; if(samples_to_read > 0) { BitBuffer bits; BitBufferInit(&bits, dataBuffer.UseData(), dataBuffer.GetDataSize()); uint32_t outSamples = 0; int32_t alac_result = localRecP->alac->Decode(&bits, alac_buffer, localRecP->alac->mConfig.frameLength, localRecP->numChannels, &outSamples); if(alac_result == 0) { bool eos = false; if(samples_to_read > outSamples) { samples_to_read = outSamples; eos = true; } if(localRecP->alac->mConfig.bitDepth == 16) { CopySamples<int16_t>((const int16_t *)alac_buffer, audioRec7->buffer, localRecP->numChannels, swizzle, samples_to_read, pos, skip_samples); } else if(localRecP->alac->mConfig.bitDepth == 32) { CopySamples<int32_t>((const int32_t *)alac_buffer, audioRec7->buffer, localRecP->numChannels, swizzle, samples_to_read, pos, skip_samples); } else { assert(localRecP->alac->mConfig.bitDepth == 20 || localRecP->alac->mConfig.bitDepth == 24); CopySamples24(alac_buffer, audioRec7->buffer, localRecP->numChannels, swizzle, samples_to_read, pos, skip_samples, localRecP->alac->mConfig.bitDepth); } if(eos) { // end of the stream break; } } else assert(false); } samples_needed -= samples_to_read; pos += samples_to_read; sample_index++; } else assert(false); } assert(ap4_result == AP4_SUCCESS); if(ap4_result != AP4_SUCCESS && ap4_result != AP4_ERROR_EOS && ap4_result != AP4_ERROR_OUT_OF_RANGE) { result = imFileReadFailed; } } else { assert(ap4_result == AP4_ERROR_EOS); } free(alac_buffer); } stdParms->piSuites->memFuncs->unlockHandle(reinterpret_cast<char**>(ldataH)); assert(result == malNoError); return result; }