diff --git a/cpp/PallyConCpixClient/CpixClient.cpp b/cpp/PallyConCpixClient/CpixClient.cpp index 0fe3558..6108918 100644 --- a/cpp/PallyConCpixClient/CpixClient.cpp +++ b/cpp/PallyConCpixClient/CpixClient.cpp @@ -35,7 +35,7 @@ namespace pallycon { LPBYTE bytePtr = Base64Decode(buffer, outputLength); if (bytePtr == NULL) { - throw std::exception("base64Decode() Failed!"); + throw CpixClientException("base64Decode() Failed!"); } std::shared_ptr smartPtr(bytePtr, std::default_delete()); return smartPtr; @@ -92,7 +92,7 @@ namespace pallycon { { } - std::string CpixClient::GetRequestData(std::string contentId, DrmType drmType, EncryptionScheme encryptionScheme, TrackType trackType) + std::string CpixClient::GetRequestData(std::string contentId, DrmType drmType, EncryptionScheme encryptionScheme, TrackType trackType, long periodIndex) { _keyMap.clear(); std::string requestData = "\n"; @@ -128,8 +128,11 @@ namespace pallycon { reqRoot.addAttribute("xmlns:speke", "urn:aws:amazon:com:speke"); XMLNode reqContentKeyList = reqRoot.addChild("cpix:ContentKeyList"); - XMLNode reqContentKeyUsageRuleList = reqRoot.addChild("cpix:ContentKeyUsageRuleList"); XMLNode reqDRMList = reqRoot.addChild("cpix:DRMSystemList"); + XMLNode reqContentKeyUsageRuleList = reqRoot.addChild("cpix:ContentKeyUsageRuleList"); + XMLNode reqContentKeyPeriodList; + if(periodIndex > 0) + reqContentKeyPeriodList = reqRoot.addChild("cpix:ContentKeyPeriodList"); for (auto& map : _keyMap) { @@ -141,6 +144,21 @@ namespace pallycon { reqContentKeyUsageRule.addAttribute("intendedTrackType", map.first.c_str()); reqContentKeyUsageRule.addAttribute("kid", map.second.c_str()); + if (periodIndex > 0) + { + std::string keyPeriodId = "keyPeriod_"; + char randomUuid[UUID_SIZE_INCLUDING_NULL_CHAR]; + __GenerateUUID(randomUuid); + keyPeriodId.append(randomUuid); + + XMLNode reqContentKeyPeriod = reqContentKeyPeriodList.addChild("cpix:ContentKeyPeriod"); + reqContentKeyPeriod.addAttribute("id", keyPeriodId.c_str()); + reqContentKeyPeriod.addAttribute("index", std::to_string(periodIndex).c_str()); + + XMLNode reqKeyPeriodFilter = reqContentKeyUsageRule.addChild("cpix:KeyPeriodFilter"); + reqKeyPeriodFilter.addAttribute("periodId", keyPeriodId.c_str()); + } + if (drmType & WIDEVINE) { XMLNode reqWidevineNode = reqDRMList.addChild("cpix:DRMSystem"); @@ -196,11 +214,11 @@ namespace pallycon { { std::string errMsg; errMsg = "CpixClient::parseResponse() : Response parsing failed. Response body: \n" + responseBody; - throw std::runtime_error(errMsg); + throw CpixClientException(errMsg); } if (responseRoot.getAttribute("id") == NULL) - throw std::runtime_error("CpixClient::parseResponse() : No CID in response."); + throw CpixClientException("CpixClient::parseResponse() : No CID in response."); packInfo.contentId = responseRoot.getAttribute("id"); @@ -228,6 +246,21 @@ namespace pallycon { drmInfo.trackType = resTrackType; drmInfo.keyId = keyId; + XMLNode resKeyPeriodFilter = resContentKeyUsageRule.getChildNode("cpix:KeyPeriodFilter", 0); + if (!resKeyPeriodFilter.isEmpty()) + { + XMLCSTR resPeriodId = resKeyPeriodFilter.getAttribute("periodId"); + XMLNode resContentKeyPeriodList = responseRoot.getChildNode("cpix:ContentKeyPeriodList"); + for (int i = 0; i < resContentKeyPeriodList.nChildNode(); i++) + { + XMLNode resContentKeyPeriod = resContentKeyPeriodList.getChildNode("cpix:ContentKeyPeriod", i); + if (0 == strcmp(resPeriodId, resContentKeyPeriod.getAttribute("id"))) + { + drmInfo.periodIndex = resContentKeyPeriod.getAttribute("index"); + } + } + } + XMLNode resContentKey; for (int i = 0; i < resContentKeyList.nChildNode(); i++) { @@ -322,23 +355,24 @@ namespace pallycon { return packInfo; } - ContentPackagingInfo CpixClient::GetContentKeyInfoFromPallyConKMS(const std::string contentId, DrmType drmType, EncryptionScheme encryptionScheme, TrackType trackType) + ContentPackagingInfo CpixClient::GetContentKeyInfoFromPallyConKMS(const std::string contentId + , DrmType drmType, EncryptionScheme encryptionScheme, TrackType trackType, long periodIndex) { if (contentId.empty()) { - throw std::runtime_error("CpixClient::getContentPackagingInfoFromKmsServer() : invalid parameter."); + throw CpixClientException("CpixClient::getContentPackagingInfoFromKmsServer() : invalid parameter."); } std::shared_ptr httpRequester(new CurlHttpRequester()); CpixRequester client(_kmsUrl, _encToken, httpRequester); - _lastRequestRowData = GetRequestData(contentId, drmType, encryptionScheme, trackType); + _lastRequestRowData = GetRequestData(contentId, drmType, encryptionScheme, trackType, periodIndex); client.SetRequestData(_lastRequestRowData); std::string responseRowData = client.Request(); if (responseRowData == "") { _lastRequestStatus = httpRequester->getRequestStatus(); std::string errorMessage = "CpixRequester:request() : " + httpRequester->getReason(); - throw std::exception(errorMessage.c_str()); + throw CpixClientException(errorMessage.c_str()); } _lastResponseRowData = responseRowData; diff --git a/cpp/PallyConCpixClient/CpixClient.h b/cpp/PallyConCpixClient/CpixClient.h index c039a72..86051a5 100644 --- a/cpp/PallyConCpixClient/CpixClient.h +++ b/cpp/PallyConCpixClient/CpixClient.h @@ -3,6 +3,7 @@ #include #include #include +#include "CpixClientException.h" namespace pallycon { @@ -96,6 +97,7 @@ namespace pallycon std::string key; std::string keyId; std::string iv; + std::string periodIndex; std::string widevinePSSH; std::string widevinePSSHpayload; std::string playreadyPSSH; @@ -124,7 +126,7 @@ namespace pallycon std::map _keyMap; - std::string GetRequestData(std::string contentId, DrmType drmType, EncryptionScheme encryptionScheme, TrackType trackType); + std::string GetRequestData(std::string contentId, DrmType drmType, EncryptionScheme encryptionScheme, TrackType trackType, long periodIndex); ContentPackagingInfo ParseResponse(const std::string& responseBody); public: @@ -167,8 +169,9 @@ namespace pallycon * @param encryptionScheme Encryption scheme. (e.g. CENC, CBCS, etc) * @param trackType Track type for multi-key packaging. (e.g. SD|HD|AUDIO) * For single-key packaging, it should be ALL_TRACKS. + * @param periodIndex Period index for key rotation. */ - ContentPackagingInfo GetContentKeyInfoFromPallyConKMS(const std::string contentId, DrmType drmType, EncryptionScheme encryptionScheme = CENC, TrackType trackType = ALL_TRACKS); + ContentPackagingInfo GetContentKeyInfoFromPallyConKMS(const std::string contentId, DrmType drmType, EncryptionScheme encryptionScheme = CENC, TrackType trackType = ALL_TRACKS, long periodIndex = 0); }; } diff --git a/cpp/PallyConCpixClient/CpixClientException.cpp b/cpp/PallyConCpixClient/CpixClientException.cpp new file mode 100644 index 0000000..df3e9e2 --- /dev/null +++ b/cpp/PallyConCpixClient/CpixClientException.cpp @@ -0,0 +1,7 @@ +#include "CpixClientException.h" + +namespace pallycon { + CpixClientException::CpixClientException(const std::string& msg) : message(msg) {} + CpixClientException::~CpixClientException() throw() {} + const char* CpixClientException::what() { return (this->message).c_str(); } +} \ No newline at end of file diff --git a/cpp/PallyConCpixClient/CpixClientException.h b/cpp/PallyConCpixClient/CpixClientException.h new file mode 100644 index 0000000..048e0ec --- /dev/null +++ b/cpp/PallyConCpixClient/CpixClientException.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace pallycon { + class CpixClientException : public std::exception + { + private: + std::string message; + + public: + CpixClientException(const std::string& msg); + ~CpixClientException() throw(); + const char* what(); + }; +} + diff --git a/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj b/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj index 1f973f1..2dfbc28 100644 --- a/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj +++ b/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj @@ -19,6 +19,7 @@ + @@ -30,6 +31,7 @@ + diff --git a/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj.filters b/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj.filters index 5c3ae66..83bc262 100644 --- a/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj.filters +++ b/cpp/PallyConCpixClient/PallyConCpixClient.vcxproj.filters @@ -16,6 +16,7 @@ helper + @@ -36,6 +37,7 @@ helper\json + diff --git a/cpp/PallyConCpixClientSample/PallyConCpixClientSample.cpp b/cpp/PallyConCpixClientSample/PallyConCpixClientSample.cpp index ee81318..2497b81 100644 --- a/cpp/PallyConCpixClientSample/PallyConCpixClientSample.cpp +++ b/cpp/PallyConCpixClientSample/PallyConCpixClientSample.cpp @@ -7,6 +7,7 @@ #include "helper/base64.h" #include "helper/json/json.h" #include "CpixClient.h" +#include "CpixClientException.h" #if defined(_WIN32) FILE _iob[] = { *stdin, *stdout, *stderr }; @@ -152,6 +153,10 @@ int main() outFile << strJson << std::endl; outFile.close(); } + catch (pallycon::CpixClientException& e) + { + std::cout << "CpixClientException : " << e.what() << std::endl; + } catch (std::exception& e) { std::cout << "Exception : " << e.what() << std::endl; diff --git a/cpp/README.md b/cpp/README.md index 1a82f56..a0924bb 100644 --- a/cpp/README.md +++ b/cpp/README.md @@ -39,11 +39,13 @@ * @param trackType Track type for multi-key packaging. (e.g. SD|HD|AUDIO) * For single-key packaging, it should be ALL_TRACKS. */ -ContentPackagingInfo GetContentKeyInfosFromPallyConKMS(const std::string cid, DrmType drmType, EncryptionScheme encryptionScheme = CENC, TrackType trackType = ALL_TRACKS); +ContentPackagingInfo GetContentKeyInfosFromPallyConKMS(const std::string cid, DrmType drmType, EncryptionScheme encryptionScheme = CENC, TrackType trackType = ALL_TRACKS, long periodIndex = 0); ``` ***EncryptionScheme*** and ***TrackType*** are optional parameters that default to `CENC` and `ALL_TRACKS`(single-key) when nothing is entered. +If you want to enable key rotation, you can put a value greater than 0 in `periodIndex` which defaults to 0. + ```c++