1/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "CDMPrivateAVFoundation.h"
28
29#if ENABLE(ENCRYPTED_MEDIA_V2) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
30
31#import "CDM.h"
32#import "ExceptionCode.h"
33#import "MediaPlayer.h"
34#import "MediaPlayerPrivateAVFoundationObjC.h"
35#import "SoftLinking.h"
36#import "UUID.h"
37#import <AVFoundation/AVFoundation.h>
38#import <objc/objc-runtime.h>
39
40namespace WebCore {
41
42class CDMSessionAVFoundation : public CDMSession {
43public:
44    CDMSessionAVFoundation(CDMPrivateAVFoundation* parent);
45    virtual ~CDMSessionAVFoundation() { }
46
47    virtual const String& sessionId() const OVERRIDE { return m_sessionId; }
48    virtual PassRefPtr<Uint8Array> generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode) OVERRIDE;
49    virtual void releaseKeys() OVERRIDE;
50    virtual bool update(Uint8Array*, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode) OVERRIDE;
51
52protected:
53    CDMPrivateAVFoundation* m_parent;
54    String m_sessionId;
55    RetainPtr<AVAssetResourceLoadingRequest> m_request;
56};
57
58SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
59SOFT_LINK_CLASS(AVFoundation, AVURLAsset)
60SOFT_LINK_CLASS(AVFoundation, AVAssetResourceLoadingRequest)
61#define AVURLAsset getAVURLAssetClass()
62#define AVAssetResourceLoadingRequest getAVAssetResourceLoadingRequest()
63
64
65bool CDMPrivateAVFoundation::supportsKeySystem(const String& keySystem)
66{
67    return equalIgnoringCase(keySystem, "com.apple.fps") || equalIgnoringCase(keySystem, "com.apple.fps.1_0");
68}
69
70bool CDMPrivateAVFoundation::supportsKeySystemAndMimeType(const String& keySystem, const String& mimeType)
71{
72    if (!supportsKeySystem(keySystem))
73        return false;
74    return [AVURLAsset isPlayableExtendedMIMEType:mimeType];
75}
76
77bool CDMPrivateAVFoundation::supportsMIMEType(const String& mimeType)
78{
79    return [AVURLAsset isPlayableExtendedMIMEType:mimeType];
80}
81
82PassOwnPtr<CDMSession> CDMPrivateAVFoundation::createSession()
83{
84    return adoptPtr(new CDMSessionAVFoundation(this));
85}
86
87CDMSessionAVFoundation::CDMSessionAVFoundation(CDMPrivateAVFoundation* parent)
88    : m_parent(parent)
89    , m_sessionId(createCanonicalUUIDString())
90{
91}
92
93PassRefPtr<Uint8Array> CDMSessionAVFoundation::generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode)
94{
95    UNUSED_PARAM(mimeType);
96
97    MediaPlayer* mediaPlayer = m_parent->cdm()->mediaPlayer();
98    if (!mediaPlayer) {
99        errorCode = NOT_SUPPORTED_ERR;
100        return 0;
101    }
102
103    String keyURI;
104    String keyID;
105    RefPtr<Uint8Array> certificate;
106    if (!MediaPlayerPrivateAVFoundationObjC::extractKeyURIKeyIDAndCertificateFromInitData(initData, keyURI, keyID, certificate)) {
107        errorCode = INVALID_STATE_ERR;
108        return 0;
109    }
110
111    m_request = MediaPlayerPrivateAVFoundationObjC::takeRequestForPlayerAndKeyURI(mediaPlayer, keyURI);
112    if (!m_request) {
113        errorCode = INVALID_STATE_ERR;
114        return 0;
115    }
116
117    m_sessionId = createCanonicalUUIDString();
118
119    RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:certificate->baseAddress() length:certificate->byteLength()]);
120    NSString* assetStr = keyID;
121    RetainPtr<NSData> assetID = [NSData dataWithBytes: [assetStr cStringUsingEncoding:NSUTF8StringEncoding] length:[assetStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
122    NSError* error = 0;
123    RetainPtr<NSData> keyRequest = [m_request.get() streamingContentKeyRequestDataForApp:certificateData.get() contentIdentifier:assetID.get() options:nil error:&error];
124
125    if (!keyRequest) {
126        NSError* underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
127        errorCode = CDM::DomainError;
128        systemCode = [underlyingError code];
129        return 0;
130    }
131
132    errorCode = 0;
133    systemCode = 0;
134    destinationURL = String();
135
136    RefPtr<ArrayBuffer> keyRequestBuffer = ArrayBuffer::create([keyRequest.get() bytes], [keyRequest.get() length]);
137    return Uint8Array::create(keyRequestBuffer, 0, keyRequestBuffer->byteLength());
138}
139
140void CDMSessionAVFoundation::releaseKeys()
141{
142}
143
144bool CDMSessionAVFoundation::update(Uint8Array* key, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode)
145{
146    if (!key)
147        return false;
148
149    RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:key->baseAddress() length:key->byteLength()]);
150    [[m_request.get() dataRequest] respondWithData:keyData.get()];
151    [m_request.get() finishLoading];
152    errorCode = 0;
153    systemCode = 0;
154    nextMessage = nullptr;
155    m_request = nullptr;
156
157    return true;
158}
159
160}
161
162#endif
163