/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | 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; } } }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | 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; }
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; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | 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; }