Exemple #1
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #2
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #3
0
/*----------------------------------------------------------------------
|   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);
}
Exemple #6
0
/*----------------------------------------------------------------------
|       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;
    }
  }
Exemple #8
0
/*----------------------------------------------------------------------
|   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();
}
Exemple #9
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #10
0
/*----------------------------------------------------------------------
|       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;
}
Exemple #12
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #13
0
/*----------------------------------------------------------------------
|   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);
        }
    }
}
Exemple #14
0
/*----------------------------------------------------------------------
|       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;
}
Exemple #15
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #16
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #17
0
/*----------------------------------------------------------------------
|   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");
    }
}
Exemple #19
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #21
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #22
0
/*----------------------------------------------------------------------
|   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);
    }
}
Exemple #23
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;
}
Exemple #24
0
/*----------------------------------------------------------------------
|   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;
}
Exemple #29
0
/*----------------------------------------------------------------------
|   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;
}