Working PlayReady/Verimatrix DRM support
This commit is contained in:
parent
27fdbb2468
commit
0913d2607e
25 changed files with 1360 additions and 103 deletions
|
@ -53,6 +53,22 @@ namespace Mist {
|
|||
option["long"] = "stream";
|
||||
option["help"] = "The name of the stream that this connector will provide in player mode";
|
||||
config->addOption("streamname", option);
|
||||
|
||||
/*LTS-START*/
|
||||
//Encryption
|
||||
option["arg"] = "string";
|
||||
option["long"] = "verimatrix-playready";
|
||||
option["short"] = "P";
|
||||
option["help"] = "URL of the Verimatrix PlayReady keyserver";
|
||||
config->addOption("verimatrix-playready", option);
|
||||
capa["optional"]["verimatrix-playready"]["name"] = "Verimatrix PlayReady Server";
|
||||
capa["optional"]["verimatrix-playready"]["help"] = "URL of the Verimatrix PlayReady keyserver";
|
||||
capa["optional"]["verimatrix-playready"]["option"] = "--verimatrix-playready";
|
||||
capa["optional"]["verimatrix-playready"]["type"] = "str";
|
||||
capa["optional"]["verimatrix-playready"]["default"] = "";
|
||||
option.null();
|
||||
|
||||
/*LTS-END*/
|
||||
|
||||
capa["optional"]["debug"]["name"] = "debug";
|
||||
capa["optional"]["debug"]["help"] = "The debug level at which messages need to be printed.";
|
||||
|
|
|
@ -45,6 +45,7 @@ namespace Mist {
|
|||
capa["optional"]["record"]["type"] = "str";
|
||||
capa["optional"]["record"]["default"] = "";
|
||||
option.null();
|
||||
|
||||
option["arg"] = "integer";
|
||||
option["long"] = "cut";
|
||||
option["short"] = "c";
|
||||
|
@ -57,6 +58,7 @@ namespace Mist {
|
|||
capa["optional"]["cut"]["type"] = "uint";
|
||||
capa["optional"]["cut"]["default"] = 0LL;
|
||||
option.null();
|
||||
|
||||
option["arg"] = "integer";
|
||||
option["long"] = "segment-size";
|
||||
option["short"] = "S";
|
||||
|
@ -68,6 +70,7 @@ namespace Mist {
|
|||
capa["optional"]["segmentsize"]["option"] = "--segment-size";
|
||||
capa["optional"]["segmentsize"]["type"] = "uint";
|
||||
capa["optional"]["segmentsize"]["default"] = 5000LL;
|
||||
option.null();
|
||||
/*LTS-end*/
|
||||
capa["source_match"] = "push://*";
|
||||
capa["priority"] = 9ll;
|
||||
|
@ -746,6 +749,7 @@ namespace Mist {
|
|||
recBpos = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/*LTS-END*/
|
||||
configLock.post();
|
||||
configLock.close();
|
||||
|
|
158
src/input/input_dtsccrypt.cpp
Normal file
158
src/input/input_dtsccrypt.cpp
Normal file
|
@ -0,0 +1,158 @@
|
|||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <mist/stream.h>
|
||||
#include <mist/base64.h>
|
||||
#include <mist/defines.h>
|
||||
#include <mist/encryption.h>
|
||||
#include <mist/bitfields.h>
|
||||
|
||||
#include "input_dtsccrypt.h"
|
||||
#include <ctime>
|
||||
|
||||
namespace Mist {
|
||||
inputDTSC::inputDTSC(Util::Config * cfg) : Input(cfg) {
|
||||
capa["name"] = "DTSC";
|
||||
capa["desc"] = "Enables DTSC Input";
|
||||
capa["priority"] = 9ll;
|
||||
capa["source_match"] = "/*.dtsc";
|
||||
capa["codecs"][0u][0u].append("H264");
|
||||
capa["codecs"][0u][0u].append("H263");
|
||||
capa["codecs"][0u][0u].append("VP6");
|
||||
capa["codecs"][0u][0u].append("theora");
|
||||
capa["codecs"][0u][1u].append("AAC");
|
||||
capa["codecs"][0u][1u].append("MP3");
|
||||
capa["codecs"][0u][1u].append("vorbis");
|
||||
|
||||
JSON::Value option;
|
||||
option["long"] = "key";
|
||||
option["short"] = "k";
|
||||
option["arg"] = "string";
|
||||
option["help"] = "The key to en/decrypt the current file with";
|
||||
config->addOption("key", option);
|
||||
option.null();
|
||||
|
||||
option["long"] = "keyseed";
|
||||
option["short"] = "s";
|
||||
option["arg"] = "string";
|
||||
option["help"] = "The keyseed to en/decrypt the current file with";
|
||||
config->addOption("keyseed", option);
|
||||
option.null();
|
||||
|
||||
option["long"] = "keyid";
|
||||
option["short"] = "i";
|
||||
option["arg"] = "string";
|
||||
option["help"] = "The keyid to en/decrypt the current file with";
|
||||
config->addOption("keyid", option);
|
||||
option.null();
|
||||
|
||||
srand(time(NULL));
|
||||
}
|
||||
|
||||
bool inputDTSC::setup() {
|
||||
key = Base64::decode(config->getString("key"));
|
||||
if (key == ""){
|
||||
if (config->getString("keyseed") == "" || config->getString("keyid") == ""){
|
||||
std::cerr << "No key given, and no keyseed/keyid geven" << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::string tmpSeed = Base64::decode(config->getString("keyseed"));
|
||||
std::string tmpID = Base64::decode(config->getString("keyid"));
|
||||
std::string guid = Encryption::PR_GuidToByteArray(tmpID);
|
||||
key = Encryption::PR_GenerateContentKey(tmpSeed, guid);
|
||||
}
|
||||
|
||||
if (config->getString("input") == "-") {
|
||||
std::cerr << "Input from stdin not yet supported" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!config->getString("streamname").size()){
|
||||
if (config->getString("output") == "-") {
|
||||
std::cerr << "Output to stdout not yet supported" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
if (config->getString("output") != "-") {
|
||||
std::cerr << "File output in player mode not supported" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//open File
|
||||
inFile = DTSC::File(config->getString("input"));
|
||||
if (!inFile) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool inputDTSC::readHeader() {
|
||||
if (!inFile) {
|
||||
return false;
|
||||
}
|
||||
DTSC::File tmp(config->getString("input") + ".dtsh");
|
||||
if (tmp) {
|
||||
myMeta = tmp.getMeta();
|
||||
DEBUG_MSG(DLVL_HIGH,"Meta read in with %lu tracks", myMeta.tracks.size());
|
||||
return true;
|
||||
}
|
||||
if (inFile.getMeta().moreheader < 0 || inFile.getMeta().tracks.size() == 0) {
|
||||
DEBUG_MSG(DLVL_FAIL,"Missing external header file");
|
||||
return false;
|
||||
}
|
||||
myMeta = DTSC::Meta(inFile.getMeta());
|
||||
DEBUG_MSG(DLVL_DEVEL,"Meta read in with %lu tracks", myMeta.tracks.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
void inputDTSC::getNext(bool smart) {
|
||||
if (smart){
|
||||
inFile.seekNext();
|
||||
}else{
|
||||
inFile.parseNext();
|
||||
}
|
||||
thisPacket = inFile.getPacket();
|
||||
//Do encryption/decryption here
|
||||
int tid = thisPacket.getTrackId();
|
||||
char * ivec;
|
||||
unsigned int ivecLen;
|
||||
thisPacket.getString("ivec", ivec, ivecLen);
|
||||
char iVec[16];
|
||||
if (ivecLen){
|
||||
memcpy(iVec, ivec, 8);
|
||||
}else{
|
||||
if (iVecs.find(tid) == iVecs.end()){
|
||||
iVecs[tid] = ((long long unsigned int)rand() << 32) + rand();
|
||||
}
|
||||
Bit::htobll(iVec, iVecs[tid]);
|
||||
iVecs[tid] ++;
|
||||
}
|
||||
Encryption::encryptPlayReady(thisPacket, myMeta.tracks[tid].codec, iVec, key.data());
|
||||
|
||||
}
|
||||
|
||||
void inputDTSC::seek(int seekTime) {
|
||||
inFile.seek_time(seekTime);
|
||||
initialTime = 0;
|
||||
playUntil = 0;
|
||||
}
|
||||
|
||||
void inputDTSC::trackSelect(std::string trackSpec) {
|
||||
selectedTracks.clear();
|
||||
long long unsigned int index;
|
||||
while (trackSpec != "") {
|
||||
index = trackSpec.find(' ');
|
||||
selectedTracks.insert(atoi(trackSpec.substr(0, index).c_str()));
|
||||
if (index != std::string::npos) {
|
||||
trackSpec.erase(0, index + 1);
|
||||
} else {
|
||||
trackSpec = "";
|
||||
}
|
||||
}
|
||||
inFile.selectTracks(selectedTracks);
|
||||
}
|
||||
}
|
||||
|
25
src/input/input_dtsccrypt.h
Normal file
25
src/input/input_dtsccrypt.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "input.h"
|
||||
#include <mist/dtsc.h>
|
||||
|
||||
namespace Mist {
|
||||
class inputDTSC : public Input {
|
||||
public:
|
||||
inputDTSC(Util::Config * cfg);
|
||||
protected:
|
||||
//Private Functions
|
||||
bool setup();
|
||||
bool readHeader();
|
||||
void getNext(bool smart = true);
|
||||
void seek(int seekTime);
|
||||
void trackSelect(std::string trackSpec);
|
||||
|
||||
DTSC::File inFile;
|
||||
|
||||
std::map<int,unsigned long long int> iVecs;
|
||||
std::string key;
|
||||
};
|
||||
}
|
||||
|
||||
typedef Mist::inputDTSC mistIn;
|
||||
|
||||
|
93
src/io.cpp
93
src/io.cpp
|
@ -1,3 +1,9 @@
|
|||
#include <mist/stream.h>
|
||||
#include <mist/json.h>
|
||||
#include <mist/auth.h>
|
||||
#include <mist/base64.h>
|
||||
#include <mist/bitfields.h>
|
||||
#include <cstdlib>
|
||||
#include "io.h"
|
||||
|
||||
namespace Mist {
|
||||
|
@ -16,8 +22,42 @@ namespace Mist {
|
|||
|
||||
//Write the metadata to the page
|
||||
myMeta.writeTo(metaPages[0].mapped);
|
||||
|
||||
}
|
||||
|
||||
/*LTS-START*/
|
||||
void InOutBase::initiateEncryption(){
|
||||
static bool encInit = false;
|
||||
if (encInit){
|
||||
return;
|
||||
}
|
||||
encrypt = false;
|
||||
JSON::Value cfg = Util::getStreamConfig(streamName);
|
||||
vmData.url = cfg.isMember("verimatrix-playready")?cfg["verimatrix-playready"].asString():"";
|
||||
vmData.name = streamName;
|
||||
if (vmData.url != ""){
|
||||
Encryption::fillVerimatrix(vmData);
|
||||
}else{
|
||||
vmData.keyid = cfg.isMember("keyid")?cfg["keyid"].asString():"";
|
||||
vmData.keyseed = cfg.isMember("keyseed")?cfg["keyseed"].asString():"";
|
||||
if (vmData.keyid != "" && vmData.keyseed != ""){
|
||||
vmData.keyid = Base64::decode(vmData.keyid);
|
||||
vmData.keyseed = Base64::decode(vmData.keyseed);
|
||||
vmData.key = Encryption:: PR_GenerateContentKey(vmData.keyseed, vmData.keyid);
|
||||
vmData.laurl = cfg.isMember("la_url")?cfg["la_url"].asString():"";
|
||||
}
|
||||
}
|
||||
if (vmData.key != ""){
|
||||
encrypt = true;
|
||||
char pageName[NAME_BUFFER_SIZE];
|
||||
snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_ENCRYPT, streamName.c_str());
|
||||
encryptionPage.init(pageName, 8 * 1024 * 1024, true);
|
||||
vmData.write(encryptionPage.mapped);
|
||||
}
|
||||
encInit = true;
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
///Starts the buffering of a new page.
|
||||
///
|
||||
///Does not do any actual buffering, just sets the right bits for buffering to go right.
|
||||
|
@ -27,6 +67,7 @@ namespace Mist {
|
|||
///\param pageNumber The number of the page to start buffering
|
||||
bool InOutBase::bufferStart(unsigned long tid, unsigned long pageNumber) {
|
||||
VERYHIGH_MSG("bufferStart for stream %s, track %lu, page %lu", streamName.c_str(), tid, pageNumber);
|
||||
initiateEncryption();
|
||||
//Initialize the stream metadata if it does not yet exist
|
||||
if (!metaPages.count(0)) {
|
||||
initiateMeta();
|
||||
|
@ -79,12 +120,19 @@ namespace Mist {
|
|||
//Open the correct page for the data
|
||||
char pageId[NAME_BUFFER_SIZE];
|
||||
snprintf(pageId, NAME_BUFFER_SIZE, SHM_TRACK_DATA, streamName.c_str(), mapTid, pageNumber);
|
||||
std::string pageName(pageId);
|
||||
int pageSize = pagesByTrack[tid][pageNumber].dataSize;
|
||||
#ifdef __CYGWIN__
|
||||
curPage[tid].init(pageName, 26 * 1024 * 1024, true);
|
||||
#else
|
||||
curPage[tid].init(pageName, pagesByTrack[tid][pageNumber].dataSize, true);
|
||||
pageSize = 26 * 1024 * 1024;
|
||||
#endif
|
||||
/*LTS-START*/
|
||||
INFO_MSG("Page size %d", pageSize);
|
||||
if (encrypt){
|
||||
pageSize = pageSize * 1.5;
|
||||
INFO_MSG("Adjusted page size to %d", pageSize);
|
||||
}
|
||||
/*LTS-END*/
|
||||
std::string pageName(pageId);
|
||||
curPage[tid].init(pageName, pageSize, true);
|
||||
//Make sure the data page is not destroyed when we are done buffering it later on.
|
||||
curPage[tid].master = false;
|
||||
//Store the pagenumber of the currently buffer page
|
||||
|
@ -223,27 +271,52 @@ namespace Mist {
|
|||
}
|
||||
|
||||
//Brain melt starts here
|
||||
char iVec[16];
|
||||
if (encrypt){
|
||||
if (iVecs.find(tid) == iVecs.end()){
|
||||
iVecs[tid] = ((long long unsigned int)rand() << 32) + rand();
|
||||
}
|
||||
Bit::htobll(iVec, iVecs[tid]);
|
||||
iVecs[tid] ++;
|
||||
|
||||
Encryption::encryptPlayReady(pack, myMeta.tracks[tid].codec, iVec, vmData.key.data());
|
||||
}
|
||||
|
||||
//First memcpy only the payload to the destination
|
||||
//Leaves the 20 bytes inbetween empty to ensure the data is not accidentally read before it is complete
|
||||
memcpy(curPage[tid].mapped + curOffset + 20, pack.getData() + 20, pack.getDataLen() - 20);
|
||||
if (encrypt){
|
||||
//write ivec field + new object end at (currOffset + pack.getDataLen() - 3);
|
||||
int ivecOffset = curOffset + pack.getDataLen() - 3;
|
||||
memcpy(curPage[tid].mapped+ivecOffset, "\000\004ivec\002\000\000\000\010", 11);
|
||||
memcpy(curPage[tid].mapped+ivecOffset+11, iVec, 8);
|
||||
//finish container with 0x0000EE
|
||||
memcpy(curPage[tid].mapped+ivecOffset+19, "\000\000\356", 3);
|
||||
}
|
||||
//Copy the remaing values in reverse order:
|
||||
//8 byte timestamp
|
||||
memcpy(curPage[tid].mapped + curOffset + 12, pack.getData() + 12, 8);
|
||||
//The mapped track id
|
||||
((int *)(curPage[tid].mapped + curOffset + 8))[0] = htonl(mapTid);
|
||||
//Write the size and 'DTP2' bytes to conclude the packet and allow for reading it
|
||||
memcpy(curPage[tid].mapped + curOffset, pack.getData(), 8);
|
||||
int size = Bit::btohl(pack.getData() + 4);
|
||||
if (encrypt){
|
||||
//Alter size to reflect the addition of the ivec field ( + 19 )
|
||||
size += 19;
|
||||
}
|
||||
//Write the size
|
||||
Bit::htobl(curPage[tid].mapped + curOffset + 4, size);
|
||||
//write the 'DTP2' bytes to conclude the packet and allow for reading it
|
||||
memcpy(curPage[tid].mapped + curOffset, pack.getData(), 4);
|
||||
|
||||
|
||||
if (myMeta.live){
|
||||
//Update the metadata
|
||||
DTSC::Packet updatePack(curPage[tid].mapped + curOffset, pack.getDataLen(), true);
|
||||
DTSC::Packet updatePack(curPage[tid].mapped + curOffset, size + 8, true);
|
||||
myMeta.update(updatePack);
|
||||
}
|
||||
|
||||
//End of brain melt
|
||||
pagesByTrack[tid][curPageNum[tid]].curOffset += pack.getDataLen();
|
||||
pagesByTrack[tid][curPageNum[tid]].curOffset += size + 8;
|
||||
}
|
||||
|
||||
///Wraps up the buffering of a shared memory data page
|
||||
|
@ -497,8 +570,8 @@ namespace Mist {
|
|||
metaPages[tid].init(pageName, 8 * 1024 * 1024, true);
|
||||
metaPages[tid].master = false;
|
||||
DTSC::Meta tmpMeta;
|
||||
tmpMeta.tracks[tid] = myMeta.tracks[tid];
|
||||
tmpMeta.tracks[tid].trackID = newTid;
|
||||
tmpMeta.tracks[newTid] = myMeta.tracks[tid];
|
||||
tmpMeta.tracks[newTid].trackID = newTid;
|
||||
JSON::Value tmpVal = tmpMeta.toJSON();
|
||||
std::string tmpStr = tmpVal.toNetPacked();
|
||||
memcpy(metaPages[tid].mapped, tmpStr.data(), tmpStr.size());
|
||||
|
|
7
src/io.h
7
src/io.h
|
@ -6,6 +6,7 @@
|
|||
#include <mist/defines.h>
|
||||
#include <mist/dtsc.h>
|
||||
|
||||
#include <mist/encryption.h>//LTS
|
||||
namespace Mist {
|
||||
enum negotiationState {
|
||||
FILL_NEW,///< New track, just sent negotiation request
|
||||
|
@ -40,6 +41,7 @@ namespace Mist {
|
|||
protected:
|
||||
bool standAlone;
|
||||
static Util::Config * config;
|
||||
void initiateEncryption();//LTS
|
||||
|
||||
void continueNegotiate(unsigned long tid);
|
||||
|
||||
|
@ -64,5 +66,10 @@ namespace Mist {
|
|||
std::map<unsigned long, unsigned long> curPageNum;///< For each track, holds the number page that is currently being written.
|
||||
std::map<unsigned long, IPC::sharedPage> curPage;///< For each track, holds the page that is currently being written.
|
||||
std::map<unsigned long, std::deque<DTSC::Packet> > trackBuffer; ///< Buffer to be used during active track negotiation
|
||||
|
||||
bool encrypt;
|
||||
Encryption::verimatrixData vmData;
|
||||
std::map<int,unsigned long long int> iVecs;
|
||||
IPC::sharedPage encryptionPage;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,8 +7,10 @@
|
|||
#include <mist/base64.h>
|
||||
#include <mist/http_parser.h>
|
||||
#include <mist/stream.h>
|
||||
#include <mist/bitfields.h>
|
||||
#include <mist/checksum.h>
|
||||
#include <unistd.h>
|
||||
#include <mist/nal.h>/*LTS*/
|
||||
|
||||
///\todo Maybe move to util?
|
||||
long long unsigned int binToInt(std::string & binary) {
|
||||
|
@ -58,11 +60,9 @@ namespace Mist {
|
|||
capa["methods"][0u]["handler"] = "http";
|
||||
capa["methods"][0u]["type"] = "html5/application/vnd.ms-ss";
|
||||
capa["methods"][0u]["priority"] = 9ll;
|
||||
capa["methods"][0u]["nolive"] = 1;
|
||||
capa["methods"][1u]["handler"] = "http";
|
||||
capa["methods"][1u]["type"] = "silverlight";
|
||||
capa["methods"][1u]["priority"] = 1ll;
|
||||
capa["methods"][1u]["nolive"] = 1;
|
||||
}
|
||||
|
||||
void OutHSS::sendNext() {
|
||||
|
@ -203,11 +203,11 @@ namespace Mist {
|
|||
|
||||
//Wrap everything in mp4 boxes
|
||||
MP4::MFHD mfhd_box;
|
||||
mfhd_box.setSequenceNumber(((keyObj.getNumber() - 1) * 2) + tid);///\todo Urgent: Check this for multitrack... :P wtf... :P
|
||||
mfhd_box.setSequenceNumber(((keyObj.getNumber() - 1) * 2) + (myMeta.tracks[tid].type == "video" ? 1 : 2));
|
||||
|
||||
MP4::TFHD tfhd_box;
|
||||
tfhd_box.setFlags(MP4::tfhdSampleFlag);
|
||||
tfhd_box.setTrackID(tid);
|
||||
tfhd_box.setTrackID((myMeta.tracks[tid].type == "video" ? 1 : 2));
|
||||
if (myMeta.tracks[tid].type == "video") {
|
||||
tfhd_box.setDefaultSampleFlags(0x00004001);
|
||||
} else {
|
||||
|
@ -256,6 +256,11 @@ namespace Mist {
|
|||
//If the stream is live, we want to have a fragref box if possible
|
||||
//////HEREHEREHERE
|
||||
if (myMeta.live) {
|
||||
MP4::UUID_TFXD tfxd_box;
|
||||
tfxd_box.setTime(keyObj.getTime());
|
||||
tfxd_box.setDuration(keyObj.getLength());
|
||||
traf_box.setContent(tfxd_box, 3);
|
||||
|
||||
MP4::UUID_TrackFragmentReference fragref_box;
|
||||
fragref_box.setVersion(1);
|
||||
fragref_box.setFragmentCount(0);
|
||||
|
@ -268,42 +273,47 @@ namespace Mist {
|
|||
fragref_box.setFragmentCount(++fragCount);
|
||||
}
|
||||
}
|
||||
traf_box.setContent(fragref_box, 3);
|
||||
traf_box.setContent(fragref_box, 4);
|
||||
}
|
||||
|
||||
MP4::MOOF moof_box;
|
||||
moof_box.setContent(mfhd_box, 0);
|
||||
moof_box.setContent(traf_box, 1);
|
||||
/*LTS-START*/
|
||||
if (myMeta.tracks[tid].keys.size() == myMeta.tracks[tid].ivecs.size()) {
|
||||
std::string tmpVec = std::string(myMeta.tracks[tid].ivecs[keyObj.getNumber() - myMeta.tracks[tid].keys[0].getNumber()].getData(), 8);
|
||||
unsigned long long int curVec = binToInt(tmpVec);
|
||||
if (encrypt){
|
||||
MP4::UUID_SampleEncryption sEnc;
|
||||
sEnc.setVersion(0);
|
||||
if (myMeta.tracks[tid].type == "audio") {
|
||||
sEnc.setFlags(0);
|
||||
for (int i = 0; i < keyObj.getParts(); i++) {
|
||||
MP4::UUID_SampleEncryption_Sample newSample;
|
||||
newSample.InitializationVector = intToBin(curVec);
|
||||
curVec++;
|
||||
prepareNext();
|
||||
thisPacket.getString("ivec", newSample.InitializationVector);
|
||||
sEnc.setSample(newSample, i);
|
||||
}
|
||||
} else {
|
||||
sEnc.setFlags(2);
|
||||
std::deque<long long int> tmpParts;
|
||||
for (int i = 0; i < keyObj.getParts(); i++) {
|
||||
//Get the correct packet
|
||||
prepareNext();
|
||||
MP4::UUID_SampleEncryption_Sample newSample;
|
||||
newSample.InitializationVector = intToBin(curVec);
|
||||
curVec++;
|
||||
MP4::UUID_SampleEncryption_Sample_Entry newEntry;
|
||||
newEntry.BytesClear = 5;
|
||||
newEntry.BytesEncrypted = myMeta.tracks[tid].parts[partOffset + i].getSize() - 5;
|
||||
newSample.Entries.push_back(newEntry);
|
||||
thisPacket.getString("ivec", newSample.InitializationVector);
|
||||
|
||||
std::deque<int> nalSizes = h264::parseNalSizes(thisPacket);
|
||||
for(std::deque<int>::iterator it = nalSizes.begin(); it != nalSizes.end(); it++){
|
||||
int encrypted = (*it - 5) & ~0xF;//Bitmask to a multiple of 16
|
||||
MP4::UUID_SampleEncryption_Sample_Entry newEntry;
|
||||
newEntry.BytesClear = *it - encrypted;//Size + nal_unit_type
|
||||
newEntry.BytesEncrypted = encrypted;//Entire NAL except nal_unit_type;
|
||||
newSample.Entries.push_back(newEntry);
|
||||
}
|
||||
sEnc.setSample(newSample, i);
|
||||
}
|
||||
}
|
||||
traf_box.setContent(sEnc, 3);
|
||||
}
|
||||
seek(seekTime);
|
||||
/*LTS-END*/
|
||||
//Setting the correct offsets.
|
||||
moof_box.setContent(traf_box, 1);
|
||||
|
@ -324,11 +334,27 @@ namespace Mist {
|
|||
}
|
||||
|
||||
/*LTS-START*/
|
||||
std::string OutHSS::protectionHeader(JSON::Value & encParams) {
|
||||
void OutHSS::loadEncryption(){
|
||||
static bool encryptionLoaded = false;
|
||||
if (!encryptionLoaded){
|
||||
//Load the encryption data page
|
||||
char pageName[NAME_BUFFER_SIZE];
|
||||
snprintf(pageName, NAME_BUFFER_SIZE, SHM_STREAM_ENCRYPT, streamName.c_str());
|
||||
encryptionPage.init(pageName, 8 * 1024 * 1024, false, false);
|
||||
if (encryptionPage.mapped) {
|
||||
vmData.read(encryptionPage.mapped);
|
||||
encrypt = true;
|
||||
}
|
||||
encryptionLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string OutHSS::protectionHeader() {
|
||||
loadEncryption();
|
||||
std::string xmlGen = "<WRMHEADER xmlns=\"http://schemas.microsoft.com/DRM/2007/03/PlayReadyHeader\" version=\"4.0.0.0\"><DATA><PROTECTINFO><KEYLEN>16</KEYLEN><ALGID>AESCTR</ALGID></PROTECTINFO><KID>";
|
||||
xmlGen += encParams["keyid"].asString();
|
||||
xmlGen += vmData.keyid;
|
||||
xmlGen += "</KID><LA_URL>";
|
||||
xmlGen += encParams["la_url"].asString();
|
||||
xmlGen += vmData.laurl;
|
||||
xmlGen += "</LA_URL></DATA></WRMHEADER>";
|
||||
std::string tmp = toUTF16(xmlGen);
|
||||
tmp = tmp.substr(2);
|
||||
|
@ -346,13 +372,12 @@ namespace Mist {
|
|||
}
|
||||
/*LTS-END*/
|
||||
|
||||
|
||||
///\brief Builds an index file for HTTP Smooth streaming.
|
||||
///\param encParams The encryption parameters. /*LTS*/
|
||||
///\return The index file for HTTP Smooth Streaming.
|
||||
/*LTS
|
||||
std::string smoothIndex(){
|
||||
LTS*/
|
||||
std::string OutHSS::smoothIndex(JSON::Value encParams) { /*LTS*/
|
||||
std::string OutHSS::smoothIndex(){
|
||||
loadEncryption();//LTS
|
||||
updateMeta();
|
||||
std::stringstream Result;
|
||||
Result << "<?xml version=\"1.0\" encoding=\"utf-16\"?>\n";
|
||||
|
@ -366,7 +391,6 @@ namespace Mist {
|
|||
long long int maxHeight = 0;
|
||||
long long int minWidth = 99999999;
|
||||
long long int minHeight = 99999999;
|
||||
bool encrypted = false;/*LTS*/
|
||||
for (std::map<unsigned int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
|
||||
if (it->second.codec == "AAC") {
|
||||
audioIters.push_back(it);
|
||||
|
@ -410,7 +434,6 @@ namespace Mist {
|
|||
"Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n";
|
||||
int index = 0;
|
||||
for (std::deque<std::map<unsigned int, DTSC::Track>::iterator>::iterator it = audioIters.begin(); it != audioIters.end(); it++) {
|
||||
encrypted |= ((*it)->second.keys.size() == (*it)->second.ivecs.size()); /*LTS*/
|
||||
Result << "<QualityLevel "
|
||||
"Index=\"" << index << "\" "
|
||||
"Bitrate=\"" << (*it)->second.bps * 8 << "\" "
|
||||
|
@ -456,7 +479,6 @@ namespace Mist {
|
|||
"DisplayHeight=\"" << maxHeight << "\">\n";
|
||||
int index = 0;
|
||||
for (std::deque<std::map<unsigned int, DTSC::Track>::iterator>::iterator it = videoIters.begin(); it != videoIters.end(); it++) {
|
||||
encrypted |= ((*it)->second.keys.size() == (*it)->second.ivecs.size()); /*LTS*/
|
||||
//Add video qualities
|
||||
Result << "<QualityLevel "
|
||||
"Index=\"" << index << "\" "
|
||||
|
@ -490,9 +512,9 @@ namespace Mist {
|
|||
Result << "</StreamIndex>\n";
|
||||
}
|
||||
/*LTS-START*/
|
||||
if (encrypted) {
|
||||
if (encrypt) {
|
||||
Result << "<Protection><ProtectionHeader SystemID=\"9a04f079-9840-4286-ab92-e65be0885f95\">";
|
||||
Result << protectionHeader(encParams);
|
||||
Result << protectionHeader();
|
||||
Result << "</ProtectionHeader></Protection>";
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
@ -517,16 +539,14 @@ namespace Mist {
|
|||
return;
|
||||
}
|
||||
initialize();
|
||||
loadEncryption();//LTS
|
||||
if (H.url.find("Manifest") != std::string::npos) {
|
||||
//Manifest, direct reply
|
||||
H.Clean();
|
||||
H.SetHeader("Content-Type", "text/xml");
|
||||
H.SetHeader("Cache-Control", "no-cache");
|
||||
H.setCORSHeaders();
|
||||
/*LTS
|
||||
std::string manifest = smoothIndex();
|
||||
LTS*/
|
||||
std::string manifest = smoothIndex(encryption);/*LTS*/
|
||||
H.SetBody(manifest);
|
||||
H.SendResponse("200", "OK", myConn);
|
||||
H.Clean();
|
||||
|
@ -536,17 +556,4 @@ namespace Mist {
|
|||
sendHeader();
|
||||
}
|
||||
}
|
||||
|
||||
/*LTS-START*/
|
||||
void OutHSS::initialize() {
|
||||
Output::initialize();
|
||||
JSON::Value servConf = JSON::fromFile(Util::getTmpFolder() + "streamlist");
|
||||
encryption["keyseed"] = servConf["streams"][streamName]["keyseed"];
|
||||
encryption["keyid"] = servConf["streams"][streamName]["keyid"];
|
||||
encryption["contentkey"] = servConf["streams"][streamName]["contentkey"];
|
||||
encryption["la_url"] = servConf["streams"][streamName]["la_url"];
|
||||
servConf.null();
|
||||
}
|
||||
/*LTS-END*/
|
||||
|
||||
}
|
||||
|
|
|
@ -9,15 +9,11 @@ namespace Mist {
|
|||
static void init(Util::Config * cfg);
|
||||
void onHTTP();
|
||||
void sendNext();
|
||||
void initialize();/*LTS*/
|
||||
void sendHeader();
|
||||
protected:
|
||||
JSON::Value encryption;
|
||||
std::string protectionHeader(JSON::Value & encParams);/*LTS*/
|
||||
/*LTS
|
||||
std::string protectionHeader();/*LTS*/
|
||||
std::string smoothIndex();
|
||||
LTS*/
|
||||
std::string smoothIndex(JSON::Value encParams = JSON::Value());/*LTS*/
|
||||
void loadEncryption();/*LTS*/
|
||||
int canSeekms(unsigned int ms);
|
||||
int keysToSend;
|
||||
int myTrackStor;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue