Exemplo n.º 1
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;
    AP4_ProtectionKeyMap key_map;
    AP4_TrackPropertyMap property_map;
    bool                 show_progress = false;

    // parse the command line arguments
    char* arg;
    while ((arg = *++argv)) {
        if (!strcmp(arg, "--method")) {
            arg = *++argv;
            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")) {
                method = METHOD_MARLIN_IPMP;
            } 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, "--kms-uri")) {
            if (method != METHOD_ISMA_AES) {
                fprintf(stderr, "ERROR: --kms-uri only applies to method ISMA-IAEC\n");
                return 1;
            }
            kms_uri = *++argv;
        } else if (!strcmp(arg, "--show-progress")) {
            show_progress = 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 char key[16];
            unsigned char iv[16];
            unsigned int track = strtoul(track_ascii, NULL, 10);
            AP4_SetMemory(key, 0, sizeof(key));
            AP4_SetMemory(iv, 0, sizeof(iv));
            if (AP4_ParseHex(key_ascii, key, 16)) {
                fprintf(stderr, "ERROR: invalid hex format for key\n");
            }
            if (AP4_ParseHex(iv_ascii, iv, 8)) {
                fprintf(stderr, "ERROR: invalid hex format for iv\n");
                return 1;
            }
            // 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, iv);
        } 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) {
                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);
        } 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;
    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) {
        AP4_MarlinIpmpEncryptingProcessor* marlin_processor = 
            new AP4_MarlinIpmpEncryptingProcessor();
        marlin_processor->GetKeyMap().SetKeys(key_map);
        marlin_processor->GetPropertyMap().SetProperties(property_map);
        processor = marlin_processor;
    } else {
        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;
    }

    // create the input stream
    AP4_ByteStream* input;
    try{
        input = new AP4_FileByteStream(input_filename,
            AP4_FileByteStream::STREAM_MODE_READ);
    } catch (AP4_Exception) {
        fprintf(stderr, "ERROR: cannot open input file (%s)\n", input_filename);
        return 1;
    }

    // create the output stream
    AP4_ByteStream* output;
    try {
        output = new AP4_FileByteStream(output_filename,
            AP4_FileByteStream::STREAM_MODE_WRITE);
    } catch (AP4_Exception) {
        fprintf(stderr, "ERROR: cannot open output file (%s)\n", output_filename);
        return 1;
    }

    // process/decrypt the file
    ProgressListener listener;
    AP4_Result 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();

    return 0;
}
Exemplo n.º 2
0
/*----------------------------------------------------------------------
|   main
+---------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    if (argc < 2) {
        PrintUsageAndExit();
    }

    // init the variables
    AP4_ByteStream*         input       = NULL;
    const char*             filename    = NULL;
    AP4_ProtectionKeyMap    key_map;
    AP4_Array<AP4_Ordinal>  tracks_to_dump;
    AP4_Ordinal             verbosity   = 0;
    bool                    json_format = false;

    // parse the command line
    argv++;
    char* arg;
    while ((arg = *argv++)) {
        if (!strcmp(arg, "--track")) {
            arg = *argv++;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument after --track option\n");
                return 1;
            }
            char* track_ascii = arg;
            char* key_ascii = NULL;
            char* delimiter = strchr(arg, ':');
            if (delimiter != NULL) {
                *delimiter = '\0';
                key_ascii = delimiter+1;  
            }

            // this track will be dumped
            AP4_Ordinal track_id = (AP4_Ordinal) strtoul(track_ascii, NULL, 10); 
            tracks_to_dump.Append(track_id);

            // see if we have a key for this track
            if (key_ascii != NULL) {
                unsigned char key[16];
                if (AP4_ParseHex(key_ascii, key, 16)) {
                    fprintf(stderr, "ERROR: invalid hex format for key\n");
                    return 1;
                }
                // set the key in the map
                key_map.SetKey(track_id, key, 16);
            }
        } else if (!strcmp(arg, "--verbosity")) {
            arg = *argv++;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument after --verbosity option\n");
                return 1;
            }
            verbosity = strtoul(arg, NULL, 10);
        } else if (!strcmp(arg, "--format")) {
            arg = *argv++;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument after --format option\n");
                return 1;
            }
            if (strcmp(arg, "json") == 0) {
                json_format = true;
            } else if (strcmp(arg, "text")) {
                fprintf(stderr, "ERROR: unknown output format\n");
                return 1;
            }
        } else {
#ifdef __EMSCRIPTEN__
            // Emscripten has to "mount" the filesystem to a virtual directory.
            EM_ASM(FS.mkdir('/working'));
            EM_ASM(FS.mount(NODEFS, { root: '.' }, '/working'));
            const char* const mount_name = "/working/%s";
            const int filename_size = snprintf(NULL, 0, mount_name, arg);
            char* mounted_filename = (char*) malloc(filename_size + 1);
            sprintf(mounted_filename, mount_name, arg);
            filename = mounted_filename;
#else
            filename = arg;
#endif
            AP4_Result result = AP4_FileByteStream::Create(filename, AP4_FileByteStream::STREAM_MODE_READ, input);

#ifdef __EMSCRIPTEN__
            free(mounted_filename);
            mounted_filename = NULL;
#endif

            if (AP4_FAILED(result)) {
                fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
                return 1;
            }
        }
    }
Exemplo n.º 3
0
/*----------------------------------------------------------------------
|       main
+---------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    if (argc < 5) {
        PrintUsageAndExit();
    }

    // parse options
    if (strcmp(*++argv, "--kms-uri")) {
        fprintf(stderr, "ERROR: the first option must be --kms-uri\n");
        return 1;
    }
    const char* kms_uri = *++argv;

    // create an encrypting processor
    AP4_IsmaEncryptingProcessor processor(kms_uri);

    // setup default values
    const char* input_filename = NULL;
    const char* output_filename = NULL;

    char* arg;
    while ((arg = *++argv)) {
        if (!strcmp(arg, "--key")) {
            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* salt_ascii = NULL;
            if (AP4_FAILED(AP4_SplitArgs(arg, track_ascii, key_ascii, salt_ascii))) {
                fprintf(stderr, "ERROR: invalid argument for --key option\n");
                return 1;
            }
            unsigned char key[16];
            unsigned char salt[8];
            unsigned int track = strtoul(track_ascii, NULL, 10);
            if (AP4_ParseHex(key_ascii, key, 16)) {
                fprintf(stderr, "ERROR: invalid hex format for key\n");
            }
            if (AP4_ParseHex(salt_ascii, salt, 8)) {
                fprintf(stderr, "ERROR: invalid hex format for salt\n");
            }
            // set the key in the map
            processor.GetKeyMap().SetKey(track, key, salt);
        } 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 (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;
    }
    if (kms_uri == NULL) {
        fprintf(stderr, "ERROR: missing kms uri\n");
        return 1;
    }

    // create the input stream
    AP4_ByteStream* input;
    try{
        input = new AP4_FileByteStream(input_filename,
            AP4_FileByteStream::STREAM_MODE_READ);
    } catch (AP4_Exception) {
        fprintf(stderr, "ERROR: cannot open input file (%s)\n", input_filename);
        return 1;
    }

    // create the output stream
    AP4_ByteStream* output;
    try {
        output = new AP4_FileByteStream(output_filename,
            AP4_FileByteStream::STREAM_MODE_WRITE);
    } catch (AP4_Exception) {
        fprintf(stderr, "ERROR: cannot open output file (%s)\n", output_filename);
        return 1;
    }

    // process/decrypt the file
    processor.Process(*input, *output);

    // cleanup
    input->Release();
    output->Release();

    return 0;
}
Exemplo n.º 4
0
/*----------------------------------------------------------------------
|   main
+---------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    if (argc < 3) {
        PrintUsageAndExit();
    }
    
    // parse command line
    AP4_Result result;
    char** args = argv+1;
    unsigned char key[16];
    bool          key_option = false;
    if (!strcmp(*args, "--key")) {
        if (argc != 5) {
            fprintf(stderr, "ERROR: invalid command line\n");
            return 1;
        }
        ++args;
        if (AP4_ParseHex(*args++, key, 16)) {
            fprintf(stderr, "ERROR: invalid hex format for key\n");
            return 1;
        }
        key_option = true;
    }

	// create the input stream
    AP4_ByteStream* input = NULL;
    result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_READ, input);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open input (%d)\n", result);
    }
    
	// create the output stream
    AP4_ByteStream* output = NULL;
    result = AP4_FileByteStream::Create(*args++, AP4_FileByteStream::STREAM_MODE_WRITE, output);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot open output (%d)\n", result);
    }

	// open the file
    AP4_File* input_file = new AP4_File(*input);   

    // get the movie
    AP4_SampleDescription* sample_description;
    AP4_Track* video_track;
    AP4_Movie* movie = input_file->GetMovie();
    if (movie == NULL) {
        fprintf(stderr, "ERROR: no movie in file\n");
        goto end;
    }

    // get the video track
    video_track = movie->GetTrack(AP4_Track::TYPE_VIDEO);
    if (video_track == NULL) {
        fprintf(stderr, "ERROR: no video track found\n");
        goto end;
    }

    // check that the track is of the right type
    sample_description = video_track->GetSampleDescription(0);
    if (sample_description == NULL) {
        fprintf(stderr, "ERROR: unable to parse sample description\n");
        goto end;
    }

    // show info
    AP4_Debug("Video Track:\n");
    AP4_Debug("  duration: %ld ms\n", video_track->GetDurationMs());
    AP4_Debug("  sample count: %ld\n", video_track->GetSampleCount());

    switch (sample_description->GetType()) {
        case AP4_SampleDescription::TYPE_HEVC:
            WriteSamples(video_track, sample_description, output);
            break;

        case AP4_SampleDescription::TYPE_PROTECTED: 
            if (!key_option) {
                fprintf(stderr, "ERROR: encrypted tracks require a key\n");
                goto end;
            }
            DecryptAndWriteSamples(video_track, sample_description, key, output);
            break;

        default:
            fprintf(stderr, "ERROR: unsupported sample type\n");
            break;
    }

end:
    delete input_file;
    input->Release();
    output->Release();

    return 0;
}
Exemplo n.º 5
0
/*----------------------------------------------------------------------
|   main
+---------------------------------------------------------------------*/
int
main(int argc, char** argv)
{
    if (argc == 1) PrintUsageAndExit();

    // parse options
    AP4_UI08       encryption_method        = 0;
    bool           encryption_method_is_set = false;
    AP4_UI08       padding_scheme           = 0;
    const char*    input_filename           = NULL;
    const char*    output_filename          = NULL;
    bool           show_progress            = false;
    bool           key_is_set               = false;
    unsigned char  key[16];
    unsigned char  iv[16];
    AP4_BlockCipher::CipherMode cipher_mode = AP4_BlockCipher::CBC;
    const char*    content_type             = "";
    const char*    content_id               = "";
    const char*    rights_issuer_url        = "";
    AP4_LargeSize  plaintext_length         = 0;
    AP4_DataBuffer textual_headers_buffer;
    AP4_TrackPropertyMap textual_headers;

    AP4_SetMemory(key, 0, sizeof(key));
    AP4_SetMemory(iv, 0, sizeof(iv));

    // parse the command line arguments
    char* arg;
    while ((arg = *++argv)) {
        if (!strcmp(arg, "--method")) {
            arg = *++argv;
            if (!strcmp(arg, "CBC")) {
                encryption_method = AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CBC;
                encryption_method_is_set = true;
                padding_scheme = AP4_OMA_DCF_PADDING_SCHEME_RFC_2630;
                cipher_mode = AP4_BlockCipher::CBC;
            } else if (!strcmp(arg, "CTR")) {
                encryption_method = AP4_OMA_DCF_ENCRYPTION_METHOD_AES_CTR;
                encryption_method_is_set = true;
                padding_scheme = AP4_OMA_DCF_PADDING_SCHEME_NONE;
                cipher_mode = AP4_BlockCipher::CTR;
            } else if (!strcmp(arg, "NULL")) {
                encryption_method = AP4_OMA_DCF_ENCRYPTION_METHOD_NULL;
                encryption_method_is_set = true;
                padding_scheme = AP4_OMA_DCF_PADDING_SCHEME_NONE;
            } else {
                fprintf(stderr, "ERROR: invalid value for --method argument\n");
                return 1;
            }
        } else if (!strcmp(arg, "--show-progress")) {
            show_progress = true;
        } else if (!strcmp(arg, "--content-type")) {
            content_type = *++argv;
            if (content_type == NULL) {
                fprintf(stderr, "ERROR: missing argument for --content-type option\n");
                return 1;
            }
        } else if (!strcmp(arg, "--content-id")) {
            content_id = *++argv;
            if (content_type == NULL) {
                fprintf(stderr, "ERROR: missing argument for --content-id option\n");
                return 1;
            }
        } else if (!strcmp(arg, "--rights-issuer")) {
             rights_issuer_url = *++argv;
            if (rights_issuer_url == NULL) {
                fprintf(stderr, "ERROR: missing argument for --rights-issuer option\n");
                return 1;
            }
        } else if (!strcmp(arg, "--key")) {
            if (!encryption_method_is_set) {
                fprintf(stderr, "ERROR: --method argument must appear before --key\n");
                return 1;
            } else if (encryption_method_is_set == AP4_OMA_DCF_ENCRYPTION_METHOD_NULL) {
                fprintf(stderr, "ERROR: --key cannot be used with --method NULL\n");
                return 1;
            }
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --key option\n");
                return 1;
            }
            char* key_ascii = NULL;
            char* iv_ascii = NULL;
            if (AP4_FAILED(AP4_SplitArgs(arg, key_ascii, iv_ascii))) {
                fprintf(stderr, "ERROR: invalid argument for --key option\n");
                return 1;
            }
            if (AP4_ParseHex(key_ascii, key, 16)) {
                fprintf(stderr, "ERROR: invalid hex format for key\n");
            }
            if (AP4_ParseHex(iv_ascii, iv, 16)) {
                fprintf(stderr, "ERROR: invalid hex format for iv\n");
                return 1;
            }
            // check that the key is not already there
            if (key_is_set) {
                fprintf(stderr, "ERROR: key already set\n");
                return 1;
            }
            key_is_set = true;
        } else if (!strcmp(arg, "--textual-header")) {
            char* name = NULL;
            char* value = NULL;
            arg = *++argv;
            if (arg == NULL) {
                fprintf(stderr, "ERROR: missing argument for --textual-header option\n");
                return 1;
            }
            if (AP4_FAILED(AP4_SplitArgs(arg, name, value))) {
                fprintf(stderr, "ERROR: invalid argument for --textual-header option\n");
                return 1;
            }

            // check that the property is not already set
            if (textual_headers.GetProperty(0, name)) {
                fprintf(stderr, "ERROR: textual header %s already set\n", name);
                return 1;
            }
            // set the property in the map
            textual_headers.SetProperty(0, 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 (!encryption_method_is_set) {
        fprintf(stderr, "ERROR: missing --method argument\n");
        return 1;
    }
    if (!key_is_set) {
        fprintf(stderr, "ERROR: encryption key not specified\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;
    }
    (void)show_progress; // avoid warnings

    // convert to a textual headers buffer
    textual_headers.GetTextualHeaders(0, textual_headers_buffer);

    // create the input stream
    AP4_Result result;
    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) %d\n", input_filename, result);
        return 1;
    }

    // get the size of the input
    result = input->GetSize(plaintext_length);
    if (AP4_FAILED(result)) {
        fprintf(stderr, "ERROR: cannot get the size of the input\n");
        return 1;
    }
    
    // create an encrypting stream for the input
    AP4_ByteStream* encrypted_stream;
    if (encryption_method == AP4_OMA_DCF_ENCRYPTION_METHOD_NULL) {
        encrypted_stream = input;
    } else {
        result = AP4_EncryptingStream::Create(cipher_mode, *input, iv, 16, key, 16, true, &AP4_DefaultBlockCipherFactory::Instance, encrypted_stream);
        if (AP4_FAILED(result)) {
            fprintf(stderr, "ERROR: failed to create cipher (%d)\n", result);
            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) %d\n", output_filename, result);
        return 1;
    }

    // create the file
    AP4_File file;
    
    // set the brand
    AP4_UI32 compatible_brands[1] = {AP4_OMA_DCF_BRAND_ODCF};
    file.SetFileType(AP4_OMA_DCF_BRAND_ODCF, 2, compatible_brands, 1);
    
    // create the odrm atom (force a 64-bit size)
    AP4_ContainerAtom* odrm = new AP4_ContainerAtom(AP4_ATOM_TYPE_ODRM, AP4_FULL_ATOM_HEADER_SIZE_64, true, 0, 0);
    
    // create the ohdr atom
    AP4_OhdrAtom* ohdr = new AP4_OhdrAtom(encryption_method, 
                                          padding_scheme,
                                          plaintext_length,
                                          content_id,
                                          rights_issuer_url,
                                          textual_headers_buffer.GetData(),
                                          textual_headers_buffer.GetDataSize());

    // create the odhe atom (the ownership is transfered)
    AP4_OdheAtom* odhe = new AP4_OdheAtom(content_type, ohdr);
    odrm->AddChild(odhe);
    
    // create the odda atom
    AP4_OddaAtom* odda = new AP4_OddaAtom(*encrypted_stream);
    odrm->AddChild(odda);
    
    // add the odrm atom to the file (the owndership is transfered)
    file.GetTopLevelAtoms().Add(odrm);
    
    // write the file to the output
    AP4_FileWriter::Write(file, *output);
    
    // cleanup
    input->Release();
    output->Release();

    return 0;
}
Exemplo n.º 6
0
bool Session::initialize()
{
  // Get URN's wich are supported by this addon
  if (!license_type_.empty())
    GetSupportedDecrypterURN(dashtree_.adp_pssh_);

  // Open mpd file
  const char* delim(strrchr(mpdFileURL_.c_str(), '/'));
  if (!delim)
  {
    xbmc->Log(ADDON::LOG_ERROR, "Invalid mpdURL: / expected (%s)", mpdFileURL_.c_str());
    return false;
  }
  dashtree_.base_url_ = std::string(mpdFileURL_.c_str(), (delim - mpdFileURL_.c_str()) + 1);

  if (!dashtree_.open(mpdFileURL_.c_str()) || dashtree_.empty())
  {
    xbmc->Log(ADDON::LOG_ERROR, "Could not open / parse mpdURL (%s)", mpdFileURL_.c_str());
    return false;
  }
  xbmc->Log(ADDON::LOG_INFO, "Successfully parsed .mpd file. Download speed: %0.4f Bytes/s", dashtree_.download_speed_);

  if (dashtree_.encryptionState_ == dash::DASHTree::ENCRYTIONSTATE_ENCRYPTED)
  {
    xbmc->Log(ADDON::LOG_ERROR, "Unable to handle decryption. Unsupported!");
    return false;
  }

  uint32_t min_bandwidth(0), max_bandwidth(0);
  {
    int buf;
    xbmc->GetSetting("MINBANDWIDTH", (char*)&buf); min_bandwidth = buf;
    xbmc->GetSetting("MAXBANDWIDTH", (char*)&buf); max_bandwidth = buf;
  }

  // create SESSION::STREAM objects. One for each AdaptationSet
  unsigned int i(0);
  const dash::DASHTree::AdaptationSet *adp;

  for (std::vector<STREAM*>::iterator b(streams_.begin()), e(streams_.end()); b != e; ++b)
    SAFE_DELETE(*b);
  streams_.clear();

  while ((adp = dashtree_.GetAdaptationSet(i++)))
  {
    streams_.push_back(new STREAM(dashtree_, adp->type_));
    STREAM &stream(*streams_.back());
    stream.stream_.prepare_stream(adp, width_, height_, min_bandwidth, max_bandwidth);

    const dash::DASHTree::Representation *rep(stream.stream_.getRepresentation());

    stream.info_.m_Width = rep->width_;
    stream.info_.m_Height = rep->height_;
    stream.info_.m_Aspect = rep->aspect_;
    stream.info_.m_pID = i;
    switch (adp->type_)
    {
    case dash::DASHTree::VIDEO:
      stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_VIDEO;
      break;
    case dash::DASHTree::AUDIO:
      stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_AUDIO;
      break;
    case dash::DASHTree::TEXT:
      stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_TELETEXT;
      break;
    default:
      break;
    }

    // we currently use only the first track!
    std::string::size_type pos = rep->codecs_.find(",");
    if (pos == std::string::npos)
      pos = rep->codecs_.size();

    strncpy(stream.info_.m_codecInternalName, rep->codecs_.c_str(), pos);
    stream.info_.m_codecInternalName[pos] = 0;

    if (rep->codecs_.find("mp4a") == 0)
      strcpy(stream.info_.m_codecName, "aac");
    else if (rep->codecs_.find("ec-3") == 0 || rep->codecs_.find("ac-3") == 0)
      strcpy(stream.info_.m_codecName, "eac3");
    else if (rep->codecs_.find("avc") == 0)
      strcpy(stream.info_.m_codecName, "h264");
    else if (rep->codecs_.find("hevc") == 0)
      strcpy(stream.info_.m_codecName, "hevc");

    stream.info_.m_FpsRate = rep->fpsRate_;
    stream.info_.m_FpsScale = rep->fpsScale_;
    stream.info_.m_SampleRate = rep->samplingRate_;
    stream.info_.m_Channels = rep->channelCount_;
    stream.info_.m_Bandwidth = rep->bandwidth_;
    strcpy(stream.info_.m_language, adp->language_.c_str());
  }
  
  // Try to initialize an SingleSampleDecryptor
  if (dashtree_.encryptionState_)
  {
    AP4_DataBuffer init_data;

    if (dashtree_.adp_pssh_.second == "FILE")
    {
      std::string strkey(dashtree_.adp_pssh_.first.substr(9));
      while (size_t pos = strkey.find('-') != std::string::npos)
        strkey.erase(pos, 1);
      if (strkey.size() != 32)
      {
        xbmc->Log(ADDON::LOG_ERROR, "Key system mismatch (%s)!", dashtree_.adp_pssh_.first.c_str());
        return false;
      }

      unsigned char key_system[16];
      AP4_ParseHex(strkey.c_str(), key_system, 16);

      Session::STREAM *stream(streams_[0]);

      stream->enabled = true;
      stream->stream_.start_stream(0);
      stream->stream_.select_stream(true);

      stream->input_ = new AP4_DASHStream(&stream->stream_);
      stream->input_file_ = new AP4_File(*stream->input_, AP4_DefaultAtomFactory::Instance, true);
      AP4_Movie* movie = stream->input_file_->GetMovie();
      if (movie == NULL)
      {
        xbmc->Log(ADDON::LOG_ERROR, "No MOOV in stream!");
        stream->disable();
        return false;
      }
      AP4_Array<AP4_PsshAtom*>& pssh = movie->GetPsshAtoms();

      for (unsigned int i = 0; !init_data.GetDataSize() && i < pssh.ItemCount(); i++)
      {
        if (memcmp(pssh[i]->GetSystemId(), key_system, 16) == 0)
          init_data.AppendData(pssh[i]->GetData().GetData(), pssh[i]->GetData().GetDataSize());
      }

      if (!init_data.GetDataSize())
      {
        xbmc->Log(ADDON::LOG_ERROR, "Could not extract license from video stream (PSSH not found)");
        stream->disable();
        return false;
      }
      stream->disable();
    }
    else
    {
      init_data.SetBufferSize(1024);
      unsigned int init_data_size(1024);
      b64_decode(dashtree_.pssh_.second.data(), dashtree_.pssh_.second.size(), init_data.UseData(), init_data_size);
      init_data.SetDataSize(init_data_size);
    }
    return (single_sample_decryptor_ = CreateSingleSampleDecrypter(init_data))!=0;
  }
  return true;
}
Exemplo n.º 7
0
/*----------------------------------------------------------------------
|   AP4_MarlinIpmpEncryptingProcessor::Initialize
+---------------------------------------------------------------------*/
AP4_Result 
AP4_MarlinIpmpEncryptingProcessor::Initialize(
    AP4_AtomParent&                  top_level,
    AP4_ByteStream&                  /*stream*/,
    AP4_Processor::ProgressListener* /*listener = NULL*/)
{
    // get the moov atom
    AP4_MoovAtom* moov = AP4_DYNAMIC_CAST(AP4_MoovAtom, top_level.GetChild(AP4_ATOM_TYPE_MOOV));
    if (moov == NULL) return AP4_ERROR_INVALID_FORMAT;
    
    // deal with the file type
    AP4_FtypAtom* ftyp = AP4_DYNAMIC_CAST(AP4_FtypAtom, top_level.GetChild(AP4_ATOM_TYPE_FTYP));
    if (ftyp) {
        // remove the atom, it will be replaced with a new one
        top_level.RemoveChild(ftyp);
        
        // keep the existing brand and compatible brands
        AP4_Array<AP4_UI32> compatible_brands;
        compatible_brands.EnsureCapacity(ftyp->GetCompatibleBrands().ItemCount()+1);
        for (unsigned int i=0; i<ftyp->GetCompatibleBrands().ItemCount(); i++) {
            compatible_brands.Append(ftyp->GetCompatibleBrands()[i]);
        }
        
        // add the MGSV compatible brand if it is not already there
        if (!ftyp->HasCompatibleBrand(AP4_MARLIN_BRAND_MGSV)) {
            compatible_brands.Append(AP4_MARLIN_BRAND_MGSV);
        }

        // create a replacement for the major brand
        AP4_FtypAtom* new_ftyp = new AP4_FtypAtom(AP4_MARLIN_BRAND_MGSV,
                                                  0x13c078c, //AP4_MARLIN_BRAND_MGSV_MAJOR_VERSION,
                                                  &compatible_brands[0],
                                                  compatible_brands.ItemCount());
        delete ftyp;
        ftyp = new_ftyp;
    } else {
        AP4_UI32 isom = AP4_FTYP_BRAND_ISOM;
        ftyp = new AP4_FtypAtom(AP4_MARLIN_BRAND_MGSV, 0, &isom, 1);
    }
    
    // insert the ftyp atom as the first child
    top_level.AddChild(ftyp, 0);

    // create and 'mpod' track reference atom
    AP4_TrefTypeAtom* mpod = new AP4_TrefTypeAtom(AP4_ATOM_TYPE_MPOD);
    
    // look for an available track ID, starting at 1
    unsigned int od_track_id       = 0;
    unsigned int od_track_position = 0;
    for (AP4_List<AP4_TrakAtom>::Item* trak_item = moov->GetTrakAtoms().FirstItem();
                                       trak_item;
                                       trak_item = trak_item->GetNext()) {
        AP4_TrakAtom* trak = trak_item->GetData();
        if (trak) {
            od_track_position++;
            if (trak->GetId() >= od_track_id) {
                od_track_id = trak->GetId()+1;
            }
            
            // if the track is encrypted, reference it in the mpod
            if (m_KeyMap.GetKey(trak->GetId())) {
                mpod->AddTrackId(trak->GetId());
            }
            
            //m_SinfEntries.Add(new SinfEntry(trak->GetId(), NULL));
        }   
    }
    
    // check that there was at least one track in the file
    if (od_track_id == 0) return AP4_ERROR_INVALID_FORMAT;
    
    // create an initial object descriptor
    AP4_InitialObjectDescriptor* iod = 
        // FIXME: get real values from the property map
        new AP4_InitialObjectDescriptor(AP4_DESCRIPTOR_TAG_MP4_IOD,
                                        1022, // object descriptor id
                                        false, 
                                        0xFE,    // OD profile level (0xFE = No OD profile specified)
                                        0xFF,    // scene profile level
                                        0xFE,    // audio profile level
                                        0xFE,    // visual profile level
                                        0xFF);   // graphics profile

    // create an ES_ID_Inc subdescriptor and add it to the initial object descriptor
    AP4_EsIdIncDescriptor* es_id_inc = new AP4_EsIdIncDescriptor(od_track_id);
    iod->AddSubDescriptor(es_id_inc);
    
    // create an iods atom to hold the initial object descriptor
    AP4_IodsAtom* iods = new AP4_IodsAtom(iod);
    
    // add the iods atom to the moov atom (try to put it just after mvhd)
    int iods_position = 0;
    int item_position = 0;
    for (AP4_List<AP4_Atom>::Item* moov_item = moov->GetChildren().FirstItem();
                                   moov_item;
                                   moov_item = moov_item->GetNext()) {
         ++item_position;
         if (moov_item->GetData()->GetType() == AP4_ATOM_TYPE_MVHD) {
            iods_position = item_position;
            break;
         }
    }
    AP4_Result result = moov->AddChild(iods, iods_position);
    if (AP4_FAILED(result)) {
        delete iods;
        return result;
    }
    
    // create a sample table for the OD track
    AP4_SyntheticSampleTable* od_sample_table = new AP4_SyntheticSampleTable();
    
    // create the sample description for the OD track 
    AP4_MpegSystemSampleDescription* od_sample_description;
    od_sample_description = new AP4_MpegSystemSampleDescription(AP4_STREAM_TYPE_OD,
                                                                AP4_OTI_MPEG4_SYSTEM,
                                                                NULL,
                                                                32768, // buffer size
                                                                1024,  // max bitrate
                                                                512);  // avg bitrate
    od_sample_table->AddSampleDescription(od_sample_description, true);
    
    // create the OD descriptor update 
    AP4_DescriptorUpdateCommand od_update(AP4_COMMAND_TAG_OBJECT_DESCRIPTOR_UPDATE);
    for (unsigned int i=0; i<mpod->GetTrackIds().ItemCount(); i++) {
        AP4_ObjectDescriptor* od = new AP4_ObjectDescriptor(AP4_DESCRIPTOR_TAG_MP4_OD, 256+i); // descriptor id = 256+i
        od->AddSubDescriptor(new AP4_EsIdRefDescriptor(i+1));     // index into mpod (1-based)
        od->AddSubDescriptor(new AP4_IpmpDescriptorPointer(i+1)); // descriptor id = i+1
        od_update.AddDescriptor(od);
    }
    
    // create the IPMP descriptor update 
    AP4_DescriptorUpdateCommand ipmp_update(AP4_COMMAND_TAG_IPMP_DESCRIPTOR_UPDATE);
    for (unsigned int i=0; i<mpod->GetTrackIds().ItemCount(); i++) {
        // create the ipmp descriptor
        AP4_IpmpDescriptor* ipmp_descriptor = new AP4_IpmpDescriptor(i+1, AP4_MARLIN_IPMPS_TYPE_MGSV);

        // create the sinf container
        AP4_ContainerAtom* sinf = new AP4_ContainerAtom(AP4_ATOM_TYPE_SINF);

        // add the scheme type atom
        sinf->AddChild(new AP4_SchmAtom(m_UseGroupKey?
                                        AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACGK:
                                        AP4_PROTECTION_SCHEME_TYPE_MARLIN_ACBC, 
                                        0x0100, NULL, true));

        // create the 'schi' container
        AP4_ContainerAtom* schi = new AP4_ContainerAtom(AP4_ATOM_TYPE_SCHI);

        // add the content ID 
        const char* content_id = m_PropertyMap.GetProperty(mpod->GetTrackIds()[i], "ContentId");
        if (content_id) {
            // add the content ID (8id_)
            schi->AddChild(new AP4_NullTerminatedStringAtom(AP4_ATOM_TYPE_8ID_, content_id));
        }
                
        // find what the track type is (necessary for the next step) and the key
        const AP4_DataBuffer* key = NULL;
        AP4_Track::Type track_type = AP4_Track::TYPE_UNKNOWN;
        for (AP4_List<AP4_TrakAtom>::Item* trak_item = moov->GetTrakAtoms().FirstItem();
                                           trak_item;
                                           trak_item = trak_item->GetNext()) {
            AP4_TrakAtom* trak = trak_item->GetData();
            if (trak->GetId() == mpod->GetTrackIds()[i]) {
                // find the handler type
                AP4_Atom* sub = trak->FindChild("mdia/hdlr");
                if (sub) {
                    AP4_HdlrAtom* hdlr = AP4_DYNAMIC_CAST(AP4_HdlrAtom, sub);
                    if (hdlr) {
                        AP4_UI32 type = hdlr->GetHandlerType();
                        if (type == AP4_HANDLER_TYPE_SOUN) {
                            track_type = AP4_Track::TYPE_AUDIO;
                        } else if (type == AP4_HANDLER_TYPE_VIDE) {
                            track_type = AP4_Track::TYPE_VIDEO;
                        }
                    }
                }
                
                // find the key
                key = m_KeyMap.GetKey(trak->GetId());
                break;
            }
        }
        
        // group key
        if (m_UseGroupKey && key) {
            // find the group key
            const AP4_DataBuffer* group_key = m_KeyMap.GetKey(0);
            if (group_key) {
                AP4_DataBuffer wrapped_key;
                result = AP4_AesKeyWrap(group_key->GetData(), key->GetData(), key->GetDataSize(), wrapped_key);
                if (AP4_FAILED(result)) return result;
                AP4_UnknownAtom* gkey = new AP4_UnknownAtom(AP4_ATOM_TYPE_GKEY, 
                                                            wrapped_key.GetData(), 
                                                            wrapped_key.GetDataSize());
                schi->AddChild(gkey);
            }
        }
                
        // create and add the security attributes (satr)
        if (track_type != AP4_Track::TYPE_UNKNOWN && key != NULL && key != NULL) {
            AP4_ContainerAtom* satr = new AP4_ContainerAtom(AP4_ATOM_TYPE_SATR);
            switch (track_type) {
                case AP4_Track::TYPE_AUDIO:
                    satr->AddChild(new AP4_NullTerminatedStringAtom(AP4_ATOM_TYPE_STYP, AP4_MARLIN_IPMP_STYP_AUDIO));
                    break;
                case AP4_Track::TYPE_VIDEO:
                    satr->AddChild(new AP4_NullTerminatedStringAtom(AP4_ATOM_TYPE_STYP, AP4_MARLIN_IPMP_STYP_VIDEO));
                    break;
                default:
                    break;
            }
            
            // add the signed attributes, if any
            const char* signed_attributes = m_PropertyMap.GetProperty(mpod->GetTrackIds()[i], "SignedAttributes");
            if (signed_attributes) {
                // decode the hex-encoded data
                unsigned int size = (unsigned int)AP4_StringLength(signed_attributes)/2;
                AP4_DataBuffer attributes_atoms;
                attributes_atoms.SetDataSize(size);
                if (AP4_SUCCEEDED(AP4_ParseHex(signed_attributes, attributes_atoms.UseData(), size))) {
                    // parse all the atoms encoded in the data and add them to the 'schi' container
                    AP4_MemoryByteStream* mbs = new AP4_MemoryByteStream(attributes_atoms.GetData(), 
                                                                         attributes_atoms.GetDataSize());
                    do {
                        AP4_Atom* atom = NULL;
                        result = AP4_DefaultAtomFactory::Instance.CreateAtomFromStream(*mbs, atom);
                        if (AP4_SUCCEEDED(result) && atom) {
                            satr->AddChild(atom);
                        }
                    } while (AP4_SUCCEEDED(result));
                    mbs->Release();
                }
            }

            // compute the hmac
            AP4_MemoryByteStream* mbs = new AP4_MemoryByteStream();
            satr->Write(*mbs);
            AP4_Hmac* digester = NULL;
            AP4_Hmac::Create(AP4_Hmac::SHA256, key->GetData(), key->GetDataSize(), digester);
            digester->Update(mbs->GetData(), mbs->GetDataSize());
            AP4_DataBuffer hmac_value;
            digester->Final(hmac_value);
            AP4_Atom* hmac = new AP4_UnknownAtom(AP4_ATOM_TYPE_HMAC, hmac_value.GetData(), hmac_value.GetDataSize());
            
            schi->AddChild(satr);
            schi->AddChild(hmac);
            
            mbs->Release();
        }
            
        sinf->AddChild(schi);
         
        // serialize the sinf atom to a buffer and set it as the ipmp data
        AP4_MemoryByteStream* sinf_data = new AP4_MemoryByteStream((AP4_Size)sinf->GetSize());
        sinf->Write(*sinf_data);
        ipmp_descriptor->SetData(sinf_data->GetData(), sinf_data->GetDataSize());
        sinf_data->Release();
        
        ipmp_update.AddDescriptor(ipmp_descriptor);
    }
    
    // add the sample with the descriptors and updates
    AP4_MemoryByteStream* sample_data = new AP4_MemoryByteStream();
    od_update.Write(*sample_data);
    ipmp_update.Write(*sample_data);
    od_sample_table->AddSample(*sample_data, 0, sample_data->GetDataSize(), 0, 0, 0, 0, true);
    
    // create the OD track
    AP4_TrakAtom* od_track = new AP4_TrakAtom(od_sample_table,
                                              AP4_HANDLER_TYPE_ODSM,
                                              "Bento4 Marlin OD Handler",
                                              od_track_id,
                                              0, 0,
                                              1, 1000, 1, 0, "und",
                                              0, 0);
    
    // add an entry in the processor's stream table to indicate that the 
    // media data for the OD track is not in the file stream, but in our
    // memory stream.
    m_ExternalTrackData.Add(new ExternalTrackData(od_track_id, sample_data));
    sample_data->Release();
    
    // add a tref track reference atom
    AP4_ContainerAtom* tref = new AP4_ContainerAtom(AP4_ATOM_TYPE_TREF);
    tref->AddChild(mpod);
    od_track->AddChild(tref, 1); // add after 'tkhd'
    
    // add the track to the moov atoms (just after the last track)
    moov->AddChild(od_track, od_track_position);
    
    return AP4_SUCCESS;
}
Exemplo n.º 8
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;
}