DRM Callback

概要

KollusモバイルダウンロードDRMを利用してダウンロードしたコンテンツの「再生回数」、「再生有効期限」、「再生時間」を設定してコンテンツの再生を制御する機能について説明する文書です。サービス提供形態によって重複制限の設定として使用されることもあります。

Expire option

設定項目のdata-typeや値が範囲から外れた場合、エンドユーザーのコンテンツの利用に問題が発生する可能性があります。また、誤った設定値による使用回数の過剰などを回収する方法はありませんので設定に注意してください。

  • Expire count : 再生回数の制限

    • data-type : integer
    • range : 0 ~ 1000
    • 0 : unlimited (無制限)
  • Expire date : 有効期限の制限

    • data-type : integer, unixtime stamp

    • コンテンツの有効期限(再生可能期限)

      • 終了日(日時)
        例) 2014. 3. 3.  5時 45分 30秒 GMT → 1393825531

      • 0 : unlimited (無制限)

      • 最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)

  • Expire playtime : 再生時間(倍速再生の場合は倍速が適用された時間で計算)

    • data-type : integer
    • range : 0, 60 ~ 604800 (単位: 秒, min : 60秒, max:1週間)
    • 0 : unlimited (無制限)

DRM callback

DRM ポリシーを適用するためには、チャンネルにDRM Callback URLを設定する必要があります。

チャンネルにDRM Callback URLを設定すると、コンテンツをダウンロードする時点にDRM Callback URLを呼び出して、リターン値をDRMポリシーとして使用します。ダウンロードされるDRMポリシー情報はJWT EncodeされてHttp Bodyにリターンしなければなりません。

注意:

  1. DRM Callback URLがレスポンスしないとダウンロードできません。
  2. アルゴリズムはHS256のみ対応しており、Httpヘッダに指定された(X-KOLLUS-USERKEY)ヘッダに“ユーザーキー”を共に転送しなければなりません。

Callback ​flow

  1. 配信チャンネルに DRM CallbackURLを設定します。
  2. JSONデータを生成してJWTでエンコードする
  3. Kollus mobile playerがhttp://www.foo.com/auth.php に以下の情報をPOST転送します。
    • session_key : Playerが生成したリクエスト確認用のセッションキー
      • kind3のcontent_expire_resetの場合、リクエストしたsession_keyを確認します。
      • Callback v2が適用されるv1.6以降から使用可能です。
    • kind : 1 - 3
    • client_user_id : ユーザID(サービス会員情報)
      • Media token 生成の際に含まれたID
    • player_id : ユーザのデバイスID
    • device_name : ユーザのデバイス名
      • Android, iOS別に転送される端末名が異なります。
      • iOS:事前にApple社が定義した文字列で転送されます。
      • Android:デバイス名、モデル名が転送されます。※該当する情報がない場合NULLで転送
    • media_content_key : ダウンロードするコンテンツキー
      • チャンネルに登録されたコンテンツのメディアコンテンツキー(ユニーク)
      • 同じコンテンツを複数のチャンネルに登録する場合、それぞれのmedia_content_keyは全て異なります。
  4. 顧客のDRM認証サーバーは転送された上記の情報に基づいて以下のjson フォーマットのdataをJWT payloadに追加してEncoding します。ヘッダーに指定された“ユーザキー(X-KOLLUS-USERKEY)"を一緒に転送します。
    (顧客が認証データをPlayerに転送する全てのデータは必ずinteger型で転送しなければなりません。)

device_name 追加説明

  • Andoid

    Androidアプリケーションの開発に使用される Build.DEVICE, Build.MODELは /(スラッシュ)で区分した文字列を作成して使用してください。

    • Build.DEVICE+”/”+Build.MODEL
    • デバイスによって該当情報はNULLで表示される可能性があります。
  • iOS

    iOSから提供するdevice-nameを使用します。

Device TypeProduct Name
iPhone1,1iPhone
iPhone1,2iPhone 3G
iPhone2,1iPhone 3GS
iPhone3,1iPhone 4 (GSM)
iPhone3,3iPhone 4 (CDMA)
iPhone4,1iPhone 4S
iPhone5,1iPhone 5 (A1428)
iPhone5,2iPhone 5 (A1429)
iPhone5,3iPhone 5c (A1456/A1532)
iPhone5,4iPhone 5c (A1507/A1516/A1529)
iPhone6,1iPhone 5s (A1433/A1453)
iPhone6,2iPhone 5s (A1457/A1518/A1530)
iPhone7,1iPhone 6 Plus
iPhone7,2iPhone 6
iPhone8,1iPhone 6s
iPhone8,2iPhone 6s Plus
iPhone8,4iPhone SE
iPhone9,1iPhone 7 (A1660/A1779/A1780)
iPhone9,2iPhone 7 Plus (A1661/A1785/A1786)
iPhone9,3iPhone 7 (A1778)
iPhone9,4iPhone 7 Plus (A1784)
iPhone10,1iPhone 8 (A1863/A1906)
iPhone10,2iPhone 8 Plus (A1864/A1898)
iPhone10,3iPhone X (A1865/A1902)
iPhone10,4iPhone 8 (A1905)
iPhone10,5iPhone 8 Plus (A1897)
iPhone10,6iPhone X (A1901)
iPad1,1iPad
iPad2,1iPad 2 (Wi-Fi)
iPad2,2iPad 2 (GSM)
iPad2,3iPad 2 (CDMA)
iPad2,4iPad 2 (Wi-Fi, revised)
iPad2,5iPad mini (Wi-Fi)
iPad2,6iPad mini (A1454)
iPad2,7iPad mini (A1455)
iPad3,1iPad (3rd gen, Wi-Fi)
iPad3,2iPad (3rd gen, Wi-Fi+LTE Verizon)
iPad3,3iPad (3rd gen, Wi-Fi+LTE AT&T)
iPad3,4iPad (4th gen, Wi-Fi)
iPad3,5iPad (4th gen, A1459)
iPad3,6iPad (4th gen, A1460)
iPad4,1iPad Air (Wi-Fi)
iPad4,2iPad Air (Wi-Fi+LTE)
iPad4,3iPad Air (Rev)
iPad4,4iPad mini 2 (Wi-Fi)
iPad4,5iPad mini 2 (Wi-Fi+LTE)
iPad4,6iPad mini 2 (Rev)
iPad4,7iPad mini 3 (Wi-Fi)
iPad4,8iPad mini 3 (A1600)
iPad4,9iPad mini 3 (A1601)
iPad5,1iPad mini 4 (Wi-Fi)
iPad5,2iPad mini 4 (Wi-Fi+LTE)
iPad5,3iPad Air 2 (Wi-Fi)
iPad5,4iPad Air 2 (Wi-Fi+LTE)
iPad6,3iPad Pro (9.7 inch) (Wi-Fi)
iPad6,4iPad Pro (9.7 inch) (Wi-Fi+LTE)
iPad6,7iPad Pro (12.9 inch, Wi-Fi)
iPad6,8iPad Pro (12.9 inch, Wi-Fi+LTE)
iPad6,11iPad 9.7-Inch 5th Gen (Wi-Fi Only)
iPad6,12iPad 9.7-Inch 5th Gen (Wi-Fi/Cellular)
iPad7,1iPad Pro (12.9 inch, A1670)
iPad7,2iPad Pro (12.9 inch, A18219)
iPad7,3iPad Pro (10.5 inch, A1701)
iPad7,4iPad Pro (10.5 inch, A1709)
iPad7,5iPad (6th gen, A1893)
iPad7,6iPad (6th gen, A1954)
iPod1,1iPod touch
iPod2,1iPod touch (2nd gen)
iPod3,1iPod touch (3rd gen)
iPod4,1iPod touch (4th gen)
iPod5,1iPod touch (5th gen)
iPod7,1iPod touch (6th gen)

DRM Callback 新規バージョン v2

バージョン1.4まで提供していた「kind1, kind2, kind3」の個別で呼び出しを行われたことを、一回の呼出で統合しました。ユーザー側の呼出が増えることで対応が難しいとの多数の顧客からの意見に対応しました。新しいバージョンを"v2"で、以前バージョンを"v1"で記載します。

Reuest

区分Description
POSTHttp POSTでリクエスト (parameterではない)
itemsJsonArrayで構成されたstring

items項目はkind1, kind2, kind3の全てのデータが含まれた呼出にすることができます。

items (JsonArray)
kind1, kind2
区分Description
kind1,2
client_user_idユーザーID, media_token 生成に使用されたclient_user_idと同一です。
player_idユーザーのデバイスID
hardware_idデバイスのhardware ID(PC, 内容がある場合)
device_nameユーザーデバイスのモデル名
media_content_keyコンテンツのメディアコンテンツキー
uservaluesJSON format (VideoGatewayの呼出に使用されたuservalue0~9)
localtimeデバイスの時刻 (UTC)
kind3
区分Description
kind3
session_keycontent_expire_resetリクエストの際に同じsession_keyを確認します。
client_user_idユーザーID, media_token 生成に使用されたclient_user_idと同一です。
player_idユーザのデバイスID
hardware_idデバイスのhardware ID(PC, 内容がある場合)
device_nameユーザーデバイスのモデル名
media_content_keyコンテンツのメディアコンテンツキー
start_atunixtimestamp (localtime)
- 転送リクエスト時刻
uservaluesJSON format (VideoGatewayの呼出に使用されたuservalue0~9)
content_expiredコンテンツの有効期限を確認 flag ( 1: 再生不可, 0: 再生可能)
check_expired更新チェックの有効期限を確認 flag ( 1: 再生不可, 0: 再生可能)
reset_req一括更新なのかを確認 ( 0 (default), 1: 一括更新)
expiration_date 有効期限終了日(unixtimestamp)
localtimeデバイスの時刻 (UTC)
Items Sample
[
    {
        "kind": 1,
        "media_content_key" : "XXX-MEDIA_CONTENTKEY-XXX",
        "client_user_id": "XXXXXXX",
        "player_id": "xxxxxxxxxxxxxxxx",
        "device_name": "XXXXX",
        "uservalues": {
            "uservalue0": "value0"
        }
    },
    {
        "kind": 2,
        "media_content_key" : "XXX-MEDIA_CONTENTKEY-XXX",
        "client_user_id": "XXXXXXX",
        "player_id": "xxxxxxxxxxxxxxxx",
        "device_name": "XXXXX",
        "uservalues": {
            "uservalue0": "value0"
        }
    },
    {
        "kind": 3,
        "session_key" : "XXX-SESSION_KEY-XXX", ← content_expire_reset リクエストする際に確認します。
        "media_content_key" : "XXX-MEDIA_CONTENTKEY-XXX",
        "client_user_id": "XXXXXXX",
        "player_id": "xxxxxxxxxxxxxxxx",
        "device_name": "XXXXX",
        "uservalues": {
       		"uservalue1": "value1"
        }
    }
]

Response

Arrayは“data”フィールドで受け取る

kind1
区分必須Description
(int) kindO1
(string) media_content_keyOコンテンツのメディアコンテンツキー
(int) expiration_date
有効期限のunixtime stamp
最大値 : 2029年 12月 31日 23時 59分 59秒 (1893455999)
(int) expiration_count
再生回数制限 例) 10 ← 10回まで再生可能
(int) expiration_playtime
再生時間制限 例) 3600 ← 3600秒(1時間)まで再生可能
(int) expiration_playtime_type
1の場合、play状態を起動時間として制限
(int) resultO0 (エラー), 1 (正常)
0の場合ダウンロードできません。
0の場合リトライしません。
(string) message
0 (エラー)の場合、message内容を追加するとエラーが起きた際に内容が表示されます。
(int)
expiration_refresh_popup

有効期限終了後、ポリシー更新表示の有無
0の場合表示しない (基本値)
1の場合有効期限終了後にユーザの確認を求めるメッセージを表示します。
(int) vmcheck
virtual machine 有無確認 (PC) 
0: 確認しない, 1: 確認する (基本値)
(int) check_abuse
DRM kind3 を常に確認 (0: 確認しない(基本値), 1: 確認する)
(int) offline_bookmark.download

オフライン状態でブックマークダウンロード使用有無
コンテンツをダウンロードした時点のデータのみ反映され、以降サーバーとのシンクはしない
0: しない(基本値)
1: ブックマークのみダウンロード

(int) offline_bookmark.readonly
オフライン状態でブックマーク追加/削除の使用有無 (0: 使用する(基本値), 1: 使用しない)
kind2
区分必須Description
(int) kindO2
(string) media_content_keyOコンテンツのメディアコンテンツキー
(int) content_delete
0 (削除しない)、1 (ダウンロードしたファイルを削除)
ダウンロードしたコンテンツを削除するオプションです。
(string) message
0 (エラー)の場合、またはcontent_deleteが1(ダウンロードしたファイルを削除)の場合にmessageを追加すると状況によるメッセージが表示されます。(内容編集可能)
(int) check_expiration_date
使用しない(0), 更新チェックの有効期限終了日のunixtime stamp
(int) resultO0 (エラー)、1 (正常)
0の場合、追加作業はありません。
0の場合、追加オプションは無視されます。
0の場合、再度リクエストされません。
kind3
区分必須Description
(int) kindO3
(string) session_key
content_expire_reset リクエストした際にsession_keyを確認します。
(string) media_content_keyOコンテンツのメディアコンテンツキー
(int) start_atORequestに含まれたstart_at
(int) content_expired
0 (再生可能), 1 (再生不可)
ダウンロードしたコンテンツを強制的に有効期限切れ状態にします。
content_expire_resetオプションで復旧することができます。
* expiredコンテンツは 0(再生可能)でレスポンスしても再生できません。

* 1の場合、content_expire_reset オプションを無視します。
(int) content_delete
0 (削除しない), 1 (削除する)
ダウンロードしたコンテンツを削除するオプション
content_expired オプションを無視してcontent_deleteが存在する場合削除する。
(int) content_expire_reset
0 (何もしない), 1 (expiredされたコンテンツ権限をリセット)
expiredされたコンテンツの権限をリセットするオプション
(int) expiration_date
有効期限終了日のunixtime stamp
最大値: 2029年 12月 31日 23時 59分 59秒 (1893455999)
* content_expire_reset オプションが1の場合、再設定されます。
(int) expiration_count
再生回数制限 例) 10 ← 10回再生可能
* content_expire_reset オプションが1の場合、再設定されます。
(int) expiration_playtime
再生時間制限 例) 3600 ← 3600秒(1時間)まで再生可能
* content_expire_reset オプションが1の場合、再設定されます。
(int) resultO0 (エラー)、1 (正常)
0の場合、追加作業はありません。
0の場合、追加オプションは無視されます。
0の場合、再度リクエストされません。
(string) message
0 (비정상), content_expiredが 1(再生不可)・content_deleteが 1(コンテンツ削除)の場合にmessageを追加すると状況によるメッセージが表示されます。
(int) check_abuse
DRM kind3 常に確認 (0: 確認しない(基本値), 1: 確認する)
(int) check_expiration_date

更新チェックしない(0), 更新チェックの有効期限終了日のunixtime stamp

* content_expire_reset オプションが 1の場合、再設定されます。
Response ​Example
{
    “data” : [
        {
            "kind": 1,
            "media_content_key": "XXX-MEDIA_CONTENT_KEY-XXX",
            "expiration_date": 1402444800,
            "expiration_count": 10,
            "expiration_playtime": 60,
            "result": 1
        },
        {
            "kind": 2,
            "media_content_key": "XXX-MEDIA_CONTENT_KEY-XXX",
            "content_delete": 1,
            "result": 1
        },
        {
            "kind": 3,
            "session_key" : "XXX-SESSION_KEY-XXX",
            "media_content_key": "XXX-MEDIA_CONTENT_KEY-XXX",
            "start_at": 140000000,
            "content_expired": 1,
            "content_delete": 1,
            "content_expire_reset": 1,
            "expiration_date": 1402444800,
            "expiration_count": 10,
            "expiration_playtime": 3600,
            "result": 1
        }
    ]
}

JWT 対応

DRMポリシーをJWTでレスポンスすることができます。JWT ウェブサイト(https://jwt.io) から公認されたライブラリーを使用して生成されたTokenに変更してレスポンスする機能が追加されました。

PlayerにJWT Tokenで返還する場合には指定されたヘッダにユーザーキーを含めて転送しなければなりません。

HTTP/1.1 200 OK
Date: Fri, 14 Oct 2016 04:12:46 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Kollus-UserKey: 0993d76eb424a72f2005b874ac49405d44a6c
Content-Encoding: gzip

​指定した(X-Kollus-UserKey) ヘッダに転送されたユーザーキーの情報が一致する場合のみ再生されます。

JWT ​Secret,​ ​JWT​ ​ユーザーキー

ユーザーキー(2)をheaderに追加することと共に、既存のJWT scpecにあるsecret keyにはセキュリティキー(1)を入力しなければなりません。Kollus CMSで以下のように関連情報を確認することができます。

설정 > 서비스 계정 설정


JWT payload

各 callbackの responseを JWT playloadに入れて転送します。

Sample

 http://jwt.io から提供している様々な言語で具現されたライブラリーとサンプルを使用してください。JWT スペックと同じですが、レスポンスされるhttpヘッダに X-Kollus-UserKeyでユーザーキーを一緒に転送しなければなりません。

(サンプル) php

define('KOLLUS_KEY', '**ユーザーキー**'); // X-Kollus-UserKey
define('JWT_KEY', '**セキュリティキー**'); // JWT VERIFY SIGNATURE

function encode_jwt($payload, $key, $alg = 'HS256') {
    $header = array('alg' => $alg, 'typ' => 'JWT' );
    $unsignedToken = base64_encode(json_encode($header)).'.'.
    base64_encode(json_encode($payload));
    $signature = hash_hmac('sha256', $unsignedToken, $key, TRUE);
    return $unsignedToken.'.'.base64_encode($signature);
}
$payload = array(
    'data' => array(
        array(
            'kind' => 1,
            'result' =>1,
            'expiration_date' => time()+3600,
            'expiration_playtime' => 30,
            'vmcheck' => 1,
        ),
        array(
            'kind' => 2,
            'result' =>1,
            'expiration_date' => time()+3600,
            'expiration_playtime' => 30,
            'vmcheck' => 1,
        ),
        array(
            'kind' => 3,
            'result' =>1,
            'expiration_date' => time()+3600,
            'expiration_playtime' => 30,
            'vmcheck' => 1,
        ),
    ),
);

$result = encode_jwt($payload, JWT_KEY);

//header('Content-Type: application/jwt');
header('X-Kollus-UserKey: '.KOLLUS_KEY);
echo $result;

Code sample (v1.4 基準)

以下のように顧客のDBが構成されたことを想定したサンプルコードになります。

PHP ​sample

<?php
/**
* PHP Version : 5.4 above
*/
function get_jwt($payload, $key, $alg = 'HS256')
{
	$header = array('alg' => $alg, 'typ' => 'JWT' );
	$unsignedToken = base64_encode(json_encode($header)).'.'.base64_encode(json_encode($payload));
	$signature = hash_hmac('sha256', $unsignedToken, $key, TRUE);
	return $unsignedToken.'.'.base64_encode($signature);
}

function print_kollus_jwt($data)
{
    define('KOLLUS_KEY', '**ユーザーキー**'); // X-Kollus-UserKey
    define('JWT_KEY', '**セキュリティキー**'); // JWT VERIFY SIGNATURE
    $payload = array( ‘data’ => $data );
    $result = encode_jwt($payload, JWT_KEY);
    //header('Content-Type: application/jwt');
    header('X-Kollus-UserKey: '.KOLLUS_KEY);
    echo $result;
}

/////////////////////////////////////////////////

include "./config.php";
// 注意 : kindが1の場合、自動でexpiration_dateが生成されるサンプルページとなります。
// 再生回数制限値
$_default_expiration_count = 3;
$_expired_duration = 60 * 60 * 24; // 1 day

// DB Connection
$_db_conn = mysqli_connect($_hostname, $_username, $_password);
if (!$_db_conn) {
	die('Could not connect: ' . mysql_error());
}

$_db_selected = mysqli_select_db($_database, $_db_conn);
if (!$_db_selected) {
	die ('Can\'t use database : ' . mysqli_error());
}

$_kind = isset($_POST['kind']) ? ((int) $_POST['kind']) : NULL;
$_media_content_key = isset($_POST['media_content_key']) ? $_POST['media_content_key'] : NULL;
$_client_user_id = isset($_POST['client_user_id']) ? $_POST['client_user_id'] : NULL;

$channel = NULL;
$_query = sprintf("SELECT * FROM `channels` WHERE `media_content_key` = '%s'",
mysqli_real_escape_string($_media_content_key, $_db_conn));
$_result = mysqli_query($_query, $_db_conn);

if ($_result) {
	$channel = mysqli_fetch_array($_result, MYSQL_ASSOC);
	if ($channel === FALSE) $channel = NULL;
} else {
	die('Invalid query: ' . mysqli_error());
}

$user = NULL;
$_query = sprintf("SELECT * FROM `users` WHERE `client_user_id` = '%s'",
mysqli_real_escape_string($_client_user_id, $_db_conn));
$_result = mysqli_query($_query, $_db_conn);
if ($_result) {
	$user = mysqli_fetch_array($_result, MYSQL_ASSOC);
	if ($user === FALSE) $user = NULL;
} else {
	die('Invalid query: ' . mysqli_error());
}

$channel_user = NULL;
if (!is_null($_media_content_key) && !is_null($_client_user_id)) {
	$_query = sprintf("SELECT * FROM `channel_users` WHERE `user_id` = '%s', `channel_id` = '%s'", $user['id'], $channel['id']);
	$_result = mysqli_query($_query, $_db_conn);
	if ($_result) {
		$channel_user = mysqli_fetch_array($_result, MYSQL_ASSOC);
		if ($channel_user === FALSE) $channel_user = NULL;
	} else {
		die('Invalid query: ' . mysqli_error());
	}
}

$_jwt_result = array('result' => 0);
switch($_kind) {
case 1:
	if (is_null($channel_user)){
		$_expiration_date = time() + $_expired_duration;
        $_expiration_count = $_default_expiration_count;
        $_query = sprintf("INSERT INTO `channel_users`(`user_id`, `channel_id`, `expiration_date`
        ,`expiration_count` , `created_at`, `updated_at`) VALUES('%s', '%s', '%s', '%s', UNIX_TIMESTAMP(),
        UNIX_TIMESTAMP())", $user['id'], $channel['id'], $_expiration_date, $_expiration_count);
        $_result = mysqli_query($_query, $_db_conn);
		if (!$_result) {
			die('Invalid query: ' . mysqli_error());
		}
	} else {
        $_expiration_date = $channel_user['expiration_date'];
        $_expiration_count = $channel_user['$expiration_count'];
	}
    $_jwt_result['expiration_date'] = (int) $_expiration_date;
    $_jwt_result['expiration_count'] = (int) $_expiration_count;
	break;
case 2:
    if (!is_null($channel_user)){
        $_download_times = ++((int) $channel_user['download_times']);
        $_query = sprintf("UPDATE `channel_users` SET `download_times` = '%s', `updated_at` =
        UNIX_TIMESTAMP() WHERE id = %s", $_download_times, $channel_user['id']);
        $_result = mysqli_query($_query, $_db_conn);
  	    if (!$_result) {
    		die('Invalid query: ' . mysql_error());
   		}
        $_jwt_result['result'] = 1;
    }
	break;
case 3:
    if (!is_null($channel_user) && $channel_user['is_expired']) {
    	$_jwt_result['content_expired'] = 1;
    }
    break;
}

// DB Close
mysqli_close($_db_conn);
// 結果の data arrayをJWTに変換して出力
print_kollus_jwt($_jwt_result);

サンプルコード

 DRM – dotnet

 DRM – jsp

 DRM – php

Copyright © CATENOID, lnc. All Rights Reserved.
E-mail. jp_sales@catenoid.net | Tel. 03-4405-8462