/*---------------------------------------------------------------------- | AP4_CbcStreamCipher::SetIV +---------------------------------------------------------------------*/ AP4_Result AP4_CbcStreamCipher::SetIV(const AP4_UI08* iv) { AP4_CopyMemory(m_Iv, iv, AP4_CIPHER_BLOCK_SIZE); m_StreamOffset = 0; m_Eos = false; AP4_CopyMemory(m_ChainBlock, m_Iv, AP4_CIPHER_BLOCK_SIZE); m_ChainBlockFullness = AP4_CIPHER_BLOCK_SIZE; m_InBlockFullness = 0; m_OutputSkip = 0; return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | 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_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_MemoryByteStream::ReadPartial +---------------------------------------------------------------------*/ AP4_Result AP4_MemoryByteStream::ReadPartial(void* buffer, AP4_Size bytes_to_read, AP4_Size& bytes_read) { // default values bytes_read = 0; // shortcut if (bytes_to_read == 0) { return AP4_SUCCESS; } // clamp to range if (m_Position+bytes_to_read > m_Buffer->GetDataSize()) { bytes_to_read = (AP4_Size)(m_Buffer->GetDataSize() - m_Position); } // check for end of stream if (bytes_to_read == 0) { return AP4_ERROR_EOS; } // read from the memory AP4_CopyMemory(buffer, m_Buffer->GetData()+m_Position, bytes_to_read); m_Position += bytes_to_read; bytes_read = bytes_to_read; return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_CbcStreamCipher::SetStreamOffset +---------------------------------------------------------------------*/ AP4_Result AP4_CbcStreamCipher::SetStreamOffset(AP4_UI64 offset, AP4_Cardinal* preroll) { // does not make sense for encryption if (m_BlockCipher->GetDirection() == AP4_BlockCipher::ENCRYPT) { return AP4_ERROR_NOT_SUPPORTED; } // check params if (preroll == NULL) return AP4_ERROR_INVALID_PARAMETERS; // reset the end of stream flag m_Eos = false; // invalidate the chain block m_ChainBlockFullness = 0; // flush cached data m_InBlockFullness = 0; // compute the preroll if (offset < AP4_CIPHER_BLOCK_SIZE) { AP4_CopyMemory(m_ChainBlock, m_Iv, AP4_CIPHER_BLOCK_SIZE); m_ChainBlockFullness = AP4_CIPHER_BLOCK_SIZE; *preroll = (AP4_Cardinal)offset; } else { *preroll = (AP4_Cardinal) ((offset%AP4_CIPHER_BLOCK_SIZE) + AP4_CIPHER_BLOCK_SIZE); } m_StreamOffset = offset-*preroll; m_OutputSkip = (AP4_Size)(offset%AP4_CIPHER_BLOCK_SIZE); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | AP4_MarlinIpmpTrackEncrypter::AP4_MarlinIpmpTrackEncrypter +---------------------------------------------------------------------*/ AP4_MarlinIpmpTrackEncrypter::AP4_MarlinIpmpTrackEncrypter(AP4_StreamCipher* cipher, const AP4_UI08* iv) : m_Cipher(cipher) { // copy the IV AP4_CopyMemory(m_IV, iv, AP4_AES_BLOCK_SIZE); }
/*---------------------------------------------------------------------- | 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_BlocAtom::SetPurchaseLocation +---------------------------------------------------------------------*/ void AP4_BlocAtom::SetPurchaseLocation(const char* purchase_location) { unsigned int len = (unsigned int)AP4_StringLength(purchase_location); if (len > 256) len = 256; AP4_CopyMemory(m_PurchaseLocation, purchase_location, len); AP4_SetMemory(&m_PurchaseLocation[len], 0, 256-len+1); }
/*---------------------------------------------------------------------- | AP4_StcoAtom::AP4_StcoAtom +---------------------------------------------------------------------*/ AP4_StcoAtom::AP4_StcoAtom(AP4_UI32* entries, AP4_UI32 entry_count) : AP4_Atom(AP4_ATOM_TYPE_STCO, AP4_FULL_ATOM_HEADER_SIZE+4+entry_count*4, 0, 0), m_Entries(new AP4_UI32[entry_count]), m_EntryCount(entry_count) { AP4_CopyMemory(m_Entries, entries, m_EntryCount*4); }
SampleFileStorage::SampleFileStorage(const char *basename) { m_Stream = NULL; AP4_Size name_length = (AP4_Size)AP4_StringLength(basename); char* filename = new char[name_length+2]; AP4_CopyMemory(filename, basename, name_length); filename[name_length] = '_'; filename[name_length+1] = '\0'; m_Filename = filename; delete[] filename; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | AP4_CtrStreamCipher::SetIV +---------------------------------------------------------------------*/ AP4_Result AP4_CtrStreamCipher::SetIV(const AP4_UI08* iv) { if (iv) { AP4_CopyMemory(m_IV, iv, AP4_CIPHER_BLOCK_SIZE); } else { AP4_SetMemory(m_IV, 0, AP4_CIPHER_BLOCK_SIZE); } // for the stream offset back to 0 m_CacheValid = false; return SetStreamOffset(0); }
/*---------------------------------------------------------------------- | AP4_AvccAtom::UpdateRawBytes +---------------------------------------------------------------------*/ void AP4_AvccAtom::UpdateRawBytes() { // compute the payload size unsigned int payload_size = 6; for (unsigned int i=0; i<m_SequenceParameters.ItemCount(); i++) { payload_size += 2+m_SequenceParameters[i].GetDataSize(); } ++payload_size; for (unsigned int i=0; i<m_PictureParameters.ItemCount(); i++) { payload_size += 2+m_PictureParameters[i].GetDataSize(); } m_RawBytes.SetDataSize(payload_size); AP4_UI08* payload = m_RawBytes.UseData(); payload[0] = m_ConfigurationVersion; payload[1] = m_Profile; payload[2] = m_ProfileCompatibility; payload[3] = m_Level; payload[4] = 0xFC | (m_NaluLengthSize-1); payload[5] = 0xE0 | m_SequenceParameters.ItemCount(); unsigned int cursor = 6; for (unsigned int i=0; i<m_SequenceParameters.ItemCount(); i++) { AP4_UI16 param_length = (AP4_UI16)m_SequenceParameters[i].GetDataSize(); AP4_BytesFromUInt16BE(&payload[cursor], param_length); cursor += 2; AP4_CopyMemory(&payload[cursor], m_SequenceParameters[i].GetData(), param_length); cursor += param_length; } payload[cursor++] = m_PictureParameters.ItemCount(); for (unsigned int i=0; i<m_PictureParameters.ItemCount(); i++) { AP4_UI16 param_length = (AP4_UI16)m_PictureParameters[i].GetDataSize(); AP4_BytesFromUInt16BE(&payload[cursor], param_length); cursor += 2; AP4_CopyMemory(&payload[cursor], m_PictureParameters[i].GetData(), param_length); cursor += param_length; } }
/*---------------------------------------------------------------------- | AP4_MkidAtom::AddEntry +---------------------------------------------------------------------*/ AP4_Result AP4_MkidAtom::AddEntry(const AP4_UI08* kid, const char* content_id) { unsigned int content_id_size = (unsigned int)AP4_StringLength(content_id); unsigned int entry_count = m_Entries.ItemCount(); // add the entry m_Entries.SetItemCount(entry_count+1); AP4_CopyMemory(m_Entries[entry_count].m_KID, kid, 16); m_Entries[entry_count].m_ContentId.Assign(content_id, content_id_size); // update the size m_Size32 += 4+16+content_id_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; }
/*---------------------------------------------------------------------- | AP4_MemoryByteStream::WritePartial +---------------------------------------------------------------------*/ AP4_Result AP4_MemoryByteStream::WritePartial(const void* buffer, AP4_Size bytes_to_write, AP4_Size& bytes_written) { // default values bytes_written = 0; // shortcut if (bytes_to_write == 0) { return AP4_SUCCESS; } // check that we don't exceed the max if (m_Position+bytes_to_write > (AP4_Position)AP4_MEMORY_BYTE_STREAM_MAX_SIZE) { return AP4_ERROR_OUT_OF_RANGE; } // reserve space in the buffer AP4_Result result = m_Buffer->Reserve((AP4_Size)(m_Position+bytes_to_write)); if (AP4_SUCCEEDED(result)) { m_Buffer->SetDataSize((AP4_Size)(m_Position+bytes_to_write)); } else { // failed to reserve, most likely caused by a buffer that has // external storage if (m_Position+bytes_to_write > m_Buffer->GetDataSize()) { bytes_to_write = (AP4_Size)(m_Buffer->GetDataSize() - m_Position); } } // check for en of stream if (bytes_to_write == 0) { return AP4_ERROR_EOS; } // write to memory AP4_CopyMemory((void*)(m_Buffer->UseData()+m_Position), buffer, bytes_to_write); m_Position += bytes_to_write; bytes_written = bytes_to_write; return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_BufferedInputStream::ReadPartial +---------------------------------------------------------------------*/ AP4_Result AP4_BufferedInputStream::ReadPartial(void* buffer, AP4_Size bytes_to_read, AP4_Size& bytes_read) { // check for shortcut if (bytes_to_read == 0) { bytes_read = 0; return AP4_SUCCESS; } // compute how much data is available in the buffer assert(m_BufferPosition <= m_Buffer.GetDataSize()); AP4_Size available = m_Buffer.GetDataSize()-m_BufferPosition; // refill the buffer if it is empty if (available == 0) { AP4_Result result = Refill(); if (AP4_FAILED(result)) { bytes_read = 0; return result; } assert(m_BufferPosition == 0); assert(m_Buffer.GetDataSize() != 0); available = m_Buffer.GetDataSize()-m_BufferPosition; } // clamp the number of bytes to read to what's available if (bytes_to_read > available) bytes_to_read = available; bytes_read = bytes_to_read; // copy the buffered data AP4_CopyMemory(buffer, m_Buffer.GetData()+m_BufferPosition, bytes_to_read); m_BufferPosition += bytes_to_read; assert(m_BufferPosition <= m_Buffer.GetDataSize()); return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_UuidAtom::AP4_UuidAtom +---------------------------------------------------------------------*/ AP4_UuidAtom::AP4_UuidAtom(AP4_UI64 size, const AP4_UI08* uuid) : AP4_Atom(AP4_ATOM_TYPE_UUID, size) { AP4_CopyMemory(m_Uuid, uuid, 16); }
/*---------------------------------------------------------------------- | AP4_CbcStreamCipher::DecryptBuffer +---------------------------------------------------------------------*/ AP4_Result AP4_CbcStreamCipher::DecryptBuffer(const AP4_UI08* in, AP4_Size in_size, AP4_UI08* out, AP4_Size* out_size, bool is_last_buffer) { // we don't do much checking here because this method is only called // from ProcessBuffer(), which does all the parameter checking // deal chain-block buffering if (m_ChainBlockFullness != AP4_CIPHER_BLOCK_SIZE) { unsigned int needed = AP4_CIPHER_BLOCK_SIZE-m_ChainBlockFullness; unsigned int chunk = (in_size > needed) ? needed : in_size; AP4_CopyMemory(&m_ChainBlock[m_ChainBlockFullness], in, chunk); in_size -= chunk; in += chunk; m_ChainBlockFullness += chunk; m_StreamOffset += chunk; if (m_ChainBlockFullness != AP4_CIPHER_BLOCK_SIZE) { // not enough to continue *out_size = 0; return AP4_SUCCESS; } } AP4_ASSERT(m_ChainBlockFullness == AP4_CIPHER_BLOCK_SIZE); // compute how many blocks we span AP4_UI64 start_block = (m_StreamOffset-m_InBlockFullness)/AP4_CIPHER_BLOCK_SIZE; AP4_UI64 end_block = (m_StreamOffset+in_size)/AP4_CIPHER_BLOCK_SIZE; AP4_UI32 blocks_needed = (AP4_UI32)(end_block-start_block); // compute how many blocks we will need to produce if (*out_size < blocks_needed*AP4_CIPHER_BLOCK_SIZE) { *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE; return AP4_ERROR_BUFFER_TOO_SMALL; } *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE; if (blocks_needed && m_OutputSkip) *out_size -= m_OutputSkip; // shortcut if (in_size == 0) return AP4_SUCCESS; // deal with in-block buffering // NOTE: if we have bytes to skip on output, always use the in-block buffer for // the first block, even if we're aligned, this makes the code simpler AP4_ASSERT(m_InBlockFullness < AP4_CIPHER_BLOCK_SIZE); if (m_OutputSkip || m_InBlockFullness) { unsigned int needed = AP4_CIPHER_BLOCK_SIZE-m_InBlockFullness; unsigned int chunk = (in_size > needed) ? needed : in_size; AP4_CopyMemory(&m_InBlock[m_InBlockFullness], in, chunk); in_size -= chunk; in += chunk; m_InBlockFullness += chunk; m_StreamOffset += chunk; if (m_InBlockFullness != AP4_CIPHER_BLOCK_SIZE) { // not enough to continue *out_size = 0; return AP4_SUCCESS; } AP4_UI08 out_block[AP4_CIPHER_BLOCK_SIZE]; AP4_Result result = m_BlockCipher->Process(m_InBlock, AP4_CIPHER_BLOCK_SIZE, out_block, m_ChainBlock); m_InBlockFullness = 0; if (AP4_FAILED(result)) { *out_size = 0; return result; } AP4_CopyMemory(m_ChainBlock, m_InBlock, AP4_CIPHER_BLOCK_SIZE); if (m_OutputSkip) { AP4_ASSERT(m_OutputSkip < AP4_CIPHER_BLOCK_SIZE); AP4_CopyMemory(out, &out_block[m_OutputSkip], AP4_CIPHER_BLOCK_SIZE-m_OutputSkip); out += AP4_CIPHER_BLOCK_SIZE-m_OutputSkip; m_OutputSkip = 0; } else { AP4_CopyMemory(out, out_block, AP4_CIPHER_BLOCK_SIZE); out += AP4_CIPHER_BLOCK_SIZE; } } AP4_ASSERT(m_InBlockFullness == 0); AP4_ASSERT(m_OutputSkip == 0); // process full blocks unsigned int block_count = in_size/AP4_CIPHER_BLOCK_SIZE; if (block_count) { AP4_UI32 blocks_size = block_count*AP4_CIPHER_BLOCK_SIZE; AP4_Result result = m_BlockCipher->Process(in, blocks_size, out, m_ChainBlock); AP4_CopyMemory(m_ChainBlock, in+blocks_size-AP4_CIPHER_BLOCK_SIZE, AP4_CIPHER_BLOCK_SIZE); if (AP4_FAILED(result)) { *out_size = 0; return result; } in += blocks_size; out += blocks_size; in_size -= blocks_size; m_StreamOffset += blocks_size; } // buffer partial block leftovers if (in_size) { AP4_ASSERT(in_size < AP4_CIPHER_BLOCK_SIZE); AP4_CopyMemory(m_InBlock, in, in_size); m_InBlockFullness = in_size; m_StreamOffset += in_size; } // deal with padding if (is_last_buffer) { AP4_UI08 pad_byte = *(out-1); if (pad_byte > AP4_CIPHER_BLOCK_SIZE || *out_size < pad_byte) { *out_size = 0; return AP4_ERROR_INVALID_FORMAT; } *out_size -= pad_byte; } return AP4_SUCCESS; }
/*---------------------------------------------------------------------- | AP4_CbcStreamCipher::EncryptBuffer +---------------------------------------------------------------------*/ AP4_Result AP4_CbcStreamCipher::EncryptBuffer(const AP4_UI08* in, AP4_Size in_size, AP4_UI08* out, AP4_Size* out_size, bool is_last_buffer) { // we don't do much checking here because this method is only called // from ProcessBuffer(), which does all the parameter checking // compute how many blocks we span AP4_UI64 start_block = (m_StreamOffset-m_InBlockFullness)/AP4_CIPHER_BLOCK_SIZE; AP4_UI64 end_block = (m_StreamOffset+in_size)/AP4_CIPHER_BLOCK_SIZE; AP4_UI32 blocks_needed = (AP4_UI32)(end_block-start_block); // compute how many blocks we will need to produce if (is_last_buffer) { ++blocks_needed; } if (*out_size < blocks_needed*AP4_CIPHER_BLOCK_SIZE) { *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE; return AP4_ERROR_BUFFER_TOO_SMALL; } *out_size = blocks_needed*AP4_CIPHER_BLOCK_SIZE; // finish any incomplete block from a previous call unsigned int offset = (unsigned int)(m_StreamOffset%AP4_CIPHER_BLOCK_SIZE); AP4_ASSERT(m_InBlockFullness == offset); if (offset) { unsigned int chunk = AP4_CIPHER_BLOCK_SIZE-offset; if (chunk > in_size) chunk = in_size; for (unsigned int x=0; x<chunk; x++) { m_InBlock[x+offset] = in[x]; } in += chunk; in_size -= chunk; m_StreamOffset += chunk; m_InBlockFullness += chunk; if (offset+chunk == AP4_CIPHER_BLOCK_SIZE) { // we have filled the input block, encrypt it AP4_Result result = m_BlockCipher->Process(m_InBlock, AP4_CIPHER_BLOCK_SIZE, out, m_ChainBlock); AP4_CopyMemory(m_ChainBlock, out, AP4_CIPHER_BLOCK_SIZE); m_InBlockFullness = 0; if (AP4_FAILED(result)) { *out_size = 0; return result; } out += AP4_CIPHER_BLOCK_SIZE; } } // encrypt the whole blocks unsigned int block_count = in_size/AP4_CIPHER_BLOCK_SIZE; if (block_count) { AP4_ASSERT(m_InBlockFullness == 0); AP4_UI32 blocks_size = block_count*AP4_CIPHER_BLOCK_SIZE; AP4_Result result = m_BlockCipher->Process(in, blocks_size, out, m_ChainBlock); AP4_CopyMemory(m_ChainBlock, out+blocks_size-AP4_CIPHER_BLOCK_SIZE, AP4_CIPHER_BLOCK_SIZE); if (AP4_FAILED(result)) { *out_size = 0; return result; } in += blocks_size; out += blocks_size; in_size -= blocks_size; m_StreamOffset += blocks_size; } // deal with what's left if (in_size) { AP4_ASSERT(in_size < AP4_CIPHER_BLOCK_SIZE); for (unsigned int x=0; x<in_size; x++) { m_InBlock[x+m_InBlockFullness] = in[x]; } m_InBlockFullness += in_size; m_StreamOffset += in_size; } // pad if needed if (is_last_buffer) { AP4_ASSERT(m_InBlockFullness == m_StreamOffset%AP4_CIPHER_BLOCK_SIZE); AP4_UI08 pad_byte = AP4_CIPHER_BLOCK_SIZE-(AP4_UI08)(m_StreamOffset%AP4_CIPHER_BLOCK_SIZE); for (unsigned int x=AP4_CIPHER_BLOCK_SIZE-pad_byte; x<AP4_CIPHER_BLOCK_SIZE; x++) { m_InBlock[x] = pad_byte; } AP4_Result result = m_BlockCipher->Process(m_InBlock, AP4_CIPHER_BLOCK_SIZE, out, m_ChainBlock); AP4_CopyMemory(m_ChainBlock, out, AP4_CIPHER_BLOCK_SIZE); m_InBlockFullness = 0; if (AP4_FAILED(result)) { *out_size = 0; return result; } } 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(); // 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; }
/*---------------------------------------------------------------------- | 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()); }
/*---------------------------------------------------------------------- | AP4_UuidAtom::AP4_UuidAtom +---------------------------------------------------------------------*/ AP4_UuidAtom::AP4_UuidAtom(AP4_UI64 size, const AP4_UI08* uuid, AP4_UI08 version, AP4_UI32 flags) : AP4_Atom(AP4_ATOM_TYPE_UUID, size, false, version, flags) { AP4_CopyMemory(m_Uuid, uuid, 16); }