summaryrefslogtreecommitdiffstats
path: root/media/wmf-clearkey/WMFClearKeySession.cpp
blob: 077665a8ae12404a6b775fe4081eee9f60a1ac1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "WMFClearKeySession.h"

#include <codecvt>
#include <cmath>
#include <Mferror.h>
#include <winerror.h>

#include "WMFClearKeyCDM.h"
#include "WMFClearKeyUtils.h"

namespace mozilla {

using Microsoft::WRL::ComPtr;

static cdm::SessionType ToCdmSessionType(MF_MEDIAKEYSESSION_TYPE aSessionType) {
  switch (aSessionType) {
    case MF_MEDIAKEYSESSION_TYPE_TEMPORARY:
      return cdm::SessionType::kTemporary;
    default:
      MOZ_ASSERT_UNREACHABLE("Only support temporary type for testing");
      return cdm::SessionType::kTemporary;
  }
}

static MF_MEDIAKEY_STATUS ToMFKeyStatus(cdm::KeyStatus aStatus) {
  switch (aStatus) {
    case cdm::KeyStatus::kUsable:
      return MF_MEDIAKEY_STATUS_USABLE;
    case cdm::KeyStatus::kExpired:
      return MF_MEDIAKEY_STATUS_EXPIRED;
    case cdm::KeyStatus::kOutputDownscaled:
      return MF_MEDIAKEY_STATUS_OUTPUT_DOWNSCALED;
    case cdm::KeyStatus::kStatusPending:
      return MF_MEDIAKEY_STATUS_STATUS_PENDING;
    case cdm::KeyStatus::kInternalError:
      return MF_MEDIAKEY_STATUS_INTERNAL_ERROR;
    case cdm::KeyStatus::kReleased:
      return MF_MEDIAKEY_STATUS_RELEASED;
    case cdm::KeyStatus::kOutputRestricted:
      return MF_MEDIAKEY_STATUS_OUTPUT_RESTRICTED;
  }
}

HRESULT WMFClearKeySession::RuntimeClassInitialize(
    MF_MEDIAKEYSESSION_TYPE aSessionType,
    IMFContentDecryptionModuleSessionCallbacks* aCallbacks,
    SessionManagerWrapper* aManager) {
  ENTRY_LOG();
  MOZ_ASSERT(aCallbacks);
  MOZ_ASSERT(aManager);
  mSessionType = ToCdmSessionType(aSessionType);
  mCallbacks = aCallbacks;
  mSessionManager = aManager;
  return S_OK;
}

WMFClearKeySession::~WMFClearKeySession() { ENTRY_LOG(); }

STDMETHODIMP WMFClearKeySession::GenerateRequest(LPCWSTR aInitDataType,
                                                 const BYTE* aInitData,
                                                 DWORD aInitDataSize) {
  if (!mSessionManager) {
    return MF_E_SHUTDOWN;
  }
  ENTRY_LOG_ARGS("init-type=%ls", aInitDataType);
  cdm::InitDataType initType;
  if (wcscmp(aInitDataType, L"cenc") == 0) {
    initType = cdm::InitDataType::kCenc;
  } else if (wcscmp(aInitDataType, L"keyids") == 0) {
    initType = cdm::InitDataType::kKeyIds;
  } else if (wcscmp(aInitDataType, L"webm") == 0) {
    initType = cdm::InitDataType::kWebM;
  } else {
    return MF_NOT_SUPPORTED_ERR;
  }
  RETURN_IF_FAILED(mSessionManager->GenerateRequest(
      initType, aInitData, aInitDataSize, mSessionType, this, mSessionId));
  MOZ_ASSERT(!mSessionId.empty());
  ENTRY_LOG_ARGS("created session, sid=%s", mSessionId.c_str());
  return S_OK;
}

STDMETHODIMP WMFClearKeySession::Load(LPCWSTR session_id, BOOL* loaded) {
  ENTRY_LOG();
  // Per step 5, Load can only be called on persistent session, but we only
  // support temporary session for MF clearkey due to testing reason.
  // https://rawgit.com/w3c/encrypted-media/V1/index.html#dom-mediakeysession-load
  return MF_E_NOT_AVAILABLE;
}

STDMETHODIMP WMFClearKeySession::Update(const BYTE* aResponse,
                                        DWORD aResponseSize) {
  ENTRY_LOG();
  if (!mSessionManager) {
    return MF_E_SHUTDOWN;
  }
  RETURN_IF_FAILED(
      mSessionManager->UpdateSession(mSessionId, aResponse, aResponseSize));
  return S_OK;
}

STDMETHODIMP WMFClearKeySession::Close() {
  ENTRY_LOG();
  // It has been shutdowned and sesssion has been closed.
  if (!mSessionManager) {
    return S_OK;
  }
  RETURN_IF_FAILED(mSessionManager->CloseSession(mSessionId));
  return S_OK;
}

STDMETHODIMP WMFClearKeySession::Remove() {
  ENTRY_LOG();
  // It has been shutdowned and sesssion has been removed.
  if (!mSessionManager) {
    return S_OK;
  }
  RETURN_IF_FAILED(mSessionManager->RemoveSession(mSessionId));
  return S_OK;
}

STDMETHODIMP WMFClearKeySession::GetSessionId(LPWSTR* aSessionId) {
  ENTRY_LOG();
  if (!mSessionManager) {
    return S_OK;
  }
  if (mSessionId.empty()) {
    *aSessionId = (LPWSTR)CoTaskMemAlloc(sizeof(wchar_t));
    if (*aSessionId != NULL) {
      **aSessionId = L'\0';
      return S_OK;
    } else {
      return E_OUTOFMEMORY;
    }
  }

  std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
  std::wstring wideStr = converter.from_bytes(mSessionId);
  *aSessionId =
      (LPWSTR)CoTaskMemAlloc((wideStr.length() + 1) * sizeof(wchar_t));
  if (*aSessionId != NULL) {
    wcscpy_s(*aSessionId, wideStr.length() + 1, wideStr.c_str());
    return S_OK;
  } else {
    return E_OUTOFMEMORY;
  }
}

STDMETHODIMP WMFClearKeySession::GetExpiration(double* expiration) {
  ENTRY_LOG();
  // This is used for testing, never expires.
  *expiration = std::nan("1");
  return S_OK;
}

STDMETHODIMP WMFClearKeySession::GetKeyStatuses(MFMediaKeyStatus** aKeyStatuses,
                                                UINT* aKeyStatusesCount) {
  ENTRY_LOG();
  *aKeyStatuses = nullptr;
  *aKeyStatusesCount = 0;

  const auto keyStatusCount = mKeyInfo.size();
  if (mSessionId.empty() || keyStatusCount == 0) {
    ENTRY_LOG_ARGS("No session ID or no key info!");
    return S_OK;
  }

  MFMediaKeyStatus* keyStatusArray = nullptr;
  keyStatusArray = static_cast<MFMediaKeyStatus*>(
      CoTaskMemAlloc(keyStatusCount * sizeof(MFMediaKeyStatus)));
  if (!keyStatusArray) {
    ENTRY_LOG_ARGS("OOM when alloacting keyStatusArray!");
    return E_OUTOFMEMORY;
  }
  ZeroMemory(keyStatusArray, keyStatusCount * sizeof(MFMediaKeyStatus));
  for (UINT idx = 0; idx < keyStatusCount; idx++) {
    keyStatusArray[idx].cbKeyId = mKeyInfo[idx].mKeyId.size();
    keyStatusArray[idx].pbKeyId =
        static_cast<BYTE*>(CoTaskMemAlloc(sizeof(mKeyInfo[idx].mKeyId.size())));
    if (keyStatusArray[idx].pbKeyId == nullptr) {
      ENTRY_LOG_ARGS("OOM when alloacting keyStatusArray's pbKeyId!");
      return E_OUTOFMEMORY;
    }

    keyStatusArray[idx].eMediaKeyStatus =
        ToMFKeyStatus(mKeyInfo[idx].mKeyStatus);
    memcpy(keyStatusArray[idx].pbKeyId, mKeyInfo[idx].mKeyId.data(),
           mKeyInfo[idx].mKeyId.size());
  }

  *aKeyStatuses = keyStatusArray;
  *aKeyStatusesCount = keyStatusCount;
  ENTRY_LOG_ARGS("return key status!");
  return S_OK;
}

void WMFClearKeySession::OnKeyMessage(
    MF_MEDIAKEYSESSION_MESSAGETYPE aMessageType, const BYTE* aMessage,
    DWORD aMessageSize) {
  ENTRY_LOG_ARGS("aMessageSize=%lu", aMessageSize);
  if (!mSessionManager) {
    return;
  }
  mCallbacks->KeyMessage(aMessageType, aMessage, aMessageSize, nullptr);
}

void WMFClearKeySession::OnKeyStatusChanged(
    const cdm::KeyInformation* aKeysInfo, uint32_t aKeysInfoCount) {
  ENTRY_LOG_ARGS("aKeysInfoCount=%u", aKeysInfoCount);
  if (!mSessionManager) {
    return;
  }
  mKeyInfo.clear();
  for (uint32_t idx = 0; idx < aKeysInfoCount; idx++) {
    const cdm::KeyInformation& key = aKeysInfo[idx];
    mKeyInfo.push_back(KeyInformation{key.key_id, key.key_id_size, key.status});
    ENTRY_LOG_ARGS("idx=%u, keySize=%u, status=%u", idx, key.key_id_size,
                   key.status);
  }
  mCallbacks->KeyStatusChanged();
}

void WMFClearKeySession::Shutdown() {
  ENTRY_LOG();
  mCallbacks = nullptr;
  mSessionManager = nullptr;
}

}  // namespace mozilla