blob: 7cfb5b2dcc9dccbab649e845283b5fa6d821a260 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef WEBKIT_MEDIA_CRYPTO_PPAPI_CONTENT_DECRYPTION_MODULE_H_
#define WEBKIT_MEDIA_CRYPTO_PPAPI_CONTENT_DECRYPTION_MODULE_H_
#if defined(_MSC_VER)
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef int int32_t;
typedef __int64 int64_t;
#else
#include <stdint.h>
#endif
#include "webkit/media/crypto/ppapi/cdm_export.h"
namespace cdm {
class Allocator;
class Buffer;
class CdmHost;
class ContentDecryptionModule;
class DecryptedBlock;
class KeyMessage;
class VideoFrame;
}
extern "C" {
// Caller retains ownership of arguments, which must outlive the call to
// DestroyCdmInstance below.
CDM_EXPORT cdm::ContentDecryptionModule* CreateCdmInstance(
cdm::Allocator* allocator, cdm::CdmHost* host);
CDM_EXPORT void DestroyCdmInstance(cdm::ContentDecryptionModule* instance);
CDM_EXPORT const char* GetCdmVersion();
}
namespace cdm {
enum Status {
kSuccess = 0,
kNeedMoreData, // Decoder needs more data to produce a decoded frame/sample.
kNoKey, // The required decryption key is not available.
kSessionError, // Session management error.
kDecryptError, // Decryption failed.
kDecodeError // Error decoding audio or video.
};
// An input buffer can be split into several continuous subsamples.
// A SubsampleEntry specifies the number of clear and cipher bytes in each
// subsample. For example, the following buffer has three subsamples:
//
// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
// | clear1 | cipher1 | clear2 | cipher2 | clear3 | cipher3 |
//
// For decryption, all of the cipher bytes in a buffer should be concatenated
// (in the subsample order) into a single logical stream. The clear bytes should
// not be considered as part of decryption.
//
// Stream to decrypt: | cipher1 | cipher2 | cipher3 |
// Decrypted stream: | decrypted1| decrypted2 | decrypted3 |
//
// After decryption, the decrypted bytes should be copied over the position
// of the corresponding cipher bytes in the original buffer to form the output
// buffer. Following the above example, the decrypted buffer should be:
//
// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
// | clear1 | decrypted1| clear2 | decrypted2 | clear3 | decrypted3 |
//
// TODO(xhwang): Add checks to make sure these structs have fixed layout.
struct SubsampleEntry {
SubsampleEntry(int32_t clear_bytes, int32_t cipher_bytes)
: clear_bytes(clear_bytes), cipher_bytes(cipher_bytes) {}
int32_t clear_bytes;
int32_t cipher_bytes;
};
// Represents an input buffer to be decrypted (and possibly decoded). It does
// own any pointers in this struct.
struct InputBuffer {
InputBuffer()
: data(NULL),
data_size(0),
data_offset(0),
key_id(NULL),
key_id_size(0),
iv(NULL),
iv_size(0),
subsamples(NULL),
num_subsamples(0),
timestamp(0) {}
const uint8_t* data; // Pointer to the beginning of the input data.
int32_t data_size; // Size (in bytes) of |data|.
int32_t data_offset; // Number of bytes to be discarded before decryption.
const uint8_t* key_id; // Key ID to identify the decryption key.
int32_t key_id_size; // Size (in bytes) of |key_id|.
const uint8_t* iv; // Initialization vector.
int32_t iv_size; // Size (in bytes) of |iv|.
const struct SubsampleEntry* subsamples;
int32_t num_subsamples; // Number of subsamples in |subsamples|.
int64_t timestamp; // Presentation timestamp in microseconds.
};
struct AudioDecoderConfig {
enum AudioCodec {
kUnknownAudioCodec = 0,
kCodecVorbis
};
AudioDecoderConfig()
: codec(kUnknownAudioCodec),
channel_count(0),
bits_per_channel(0),
samples_per_second(0),
extra_data(NULL),
extra_data_size(0) {}
AudioCodec codec;
int32_t channel_count;
int32_t bits_per_channel;
int32_t samples_per_second;
// Optional byte data required to initialize audio decoders, such as the
// vorbis setup header.
uint8_t* extra_data;
int32_t extra_data_size;
};
// Surface formats based on FOURCC labels, see:
// http://www.fourcc.org/yuv.php
enum VideoFormat {
kUnknownVideoFormat = 0, // Unknown format value. Used for error reporting.
kEmptyVideoFrame, // An empty frame.
kYv12, // 12bpp YVU planar 1x1 Y, 2x2 VU samples.
kI420 // 12bpp YVU planar 1x1 Y, 2x2 UV samples.
};
struct Size {
Size() : width(0), height(0) {}
Size(int32_t width, int32_t height) : width(width), height(height) {}
int32_t width;
int32_t height;
};
struct VideoDecoderConfig {
enum VideoCodec {
kUnknownVideoCodec = 0,
kCodecVP8
};
enum VideoCodecProfile {
kUnknownVideoCodecProfile = 0,
kVp8ProfileMain
};
VideoDecoderConfig()
: codec(kUnknownVideoCodec),
profile(kUnknownVideoCodecProfile),
format(kUnknownVideoFormat),
extra_data(NULL),
extra_data_size(0) {}
VideoCodec codec;
VideoCodecProfile profile;
VideoFormat format;
// Width and height of video frame immediately post-decode. Not all pixels
// in this region are valid.
Size coded_size;
// Optional byte data required to initialize video decoders, such as H.264
// AAVC data.
uint8_t* extra_data;
int32_t extra_data_size;
};
enum StreamType {
kStreamTypeAudio = 0,
kStreamTypeVideo = 1
};
// ContentDecryptionModule interface that all CDMs need to implement.
// Note: ContentDecryptionModule implementations must use the allocator
// provided in CreateCdmInstance() to allocate any Buffer that needs to
// be passed back to the caller. Implementations must call Buffer::Destroy()
// when a Buffer is created that will never be returned to the caller.
class ContentDecryptionModule {
public:
// Generates a |key_request| given the |init_data|.
//
// Returns kSuccess if the key request was successfully generated,
// in which case the callee should have allocated memory for the output
// parameters (e.g |session_id| in |key_request|) and passed the ownership
// to the caller.
// Returns kSessionError if any error happened, in which case the
// |key_request| should not be used by the caller.
//
// TODO(xhwang): It's not safe to pass the ownership of the dynamically
// allocated memory over library boundaries. Fix it after related PPAPI change
// and sample CDM are landed.
virtual Status GenerateKeyRequest(const uint8_t* init_data,
int init_data_size,
KeyMessage* key_request) = 0;
// Adds the |key| to the CDM to be associated with |key_id|.
//
// Returns kSuccess if the key was successfully added, kSessionError
// otherwise.
virtual Status AddKey(const char* session_id,
int session_id_size,
const uint8_t* key,
int key_size,
const uint8_t* key_id,
int key_id_size) = 0;
// Cancels any pending key request made to the CDM for |session_id|.
//
// Returns kSuccess if all pending key requests for |session_id| were
// successfully canceled or there was no key request to be canceled,
// kSessionError otherwise.
virtual Status CancelKeyRequest(const char* session_id,
int session_id_size) = 0;
// Optionally populates |*msg| and indicates so in |*populated|.
virtual void TimerExpired(KeyMessage* msg, bool* populated) = 0;
// Decrypts the |encrypted_buffer|.
//
// Returns kSuccess if decryption succeeded, in which case the callee
// should have filled the |decrypted_buffer| and passed the ownership of
// |data| in |decrypted_buffer| to the caller.
// Returns kNoKey if the CDM did not have the necessary decryption key
// to decrypt.
// Returns kDecryptError if any other error happened.
// If the return value is not kSuccess, |decrypted_buffer| should be ignored
// by the caller.
virtual Status Decrypt(const InputBuffer& encrypted_buffer,
DecryptedBlock* decrypted_buffer) = 0;
// Initializes the CDM audio decoder with |audio_decoder_config|. This
// function must be called before DecryptAndDecodeSamples() is called.
//
// Returns kSuccess if the |audio_decoder_config| is supported and the CDM
// audio decoder is successfully initialized.
// Returns kSessionError if |audio_decoder_config| is not supported. The CDM
// may still be able to do Decrypt().
//
// TODO(xhwang): Add stream ID here and in the following audio decoder
// functions when we need to support multiple audio streams in one CDM.
virtual Status InitializeAudioDecoder(
const AudioDecoderConfig& audio_decoder_config) = 0;
// Initializes the CDM video decoder with |video_decoder_config|. This
// function must be called before DecryptAndDecodeFrame() is called.
//
// Returns kSuccess if the |video_decoder_config| is supported and the CDM
// video decoder is successfully initialized.
// Returns kSessionError if |video_decoder_config| is not supported. The CDM
// may still be able to do Decrypt().
//
// TODO(xhwang): Add stream ID here and in the following video decoder
// functions when we need to support multiple video streams in one CDM.
virtual Status InitializeVideoDecoder(
const VideoDecoderConfig& video_decoder_config) = 0;
// De-initializes the CDM decoder and sets it to an uninitialized state. The
// caller can initialize the decoder again after this call to re-initialize
// it. This can be used to reconfigure the decoder if the configuration
// changes.
virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
// Resets the CDM decoder to an initialized clean state. All internal buffers
// MUST be flushed.
virtual void ResetDecoder(StreamType decoder_type) = 0;
// Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
// |video_frame|. Upon end-of-stream, the caller should call this function
// repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
// |video_frame| (|format| == kEmptyVideoFrame) is produced.
//
// Returns kSuccess if decryption and decoding both succeeded, in which case
// the callee will have filled the |video_frame| and passed the ownership of
// |frame_buffer| in |video_frame| to the caller.
// Returns kNoKey if the CDM did not have the necessary decryption key
// to decrypt.
// Returns kNeedMoreData if more data was needed by the decoder to generate
// a decoded frame (e.g. during initialization).
// Returns kDecryptError if any decryption error happened.
// Returns kDecodeError if any decoding error happened.
// If the return value is not kSuccess, |video_frame| should be ignored by
// the caller.
virtual Status DecryptAndDecodeFrame(const InputBuffer& encrypted_buffer,
VideoFrame* video_frame) = 0;
// Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
// |audio_frames|. Upon end-of-stream, the caller should call this function
// repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
// |audio_frames| is produced.
//
// Returns kSuccess if decryption and decoding both succeeded, in which case
// the callee will have filled |audio_frames| and passed the ownership of
// |data| in |audio_frames| to the caller.
// Returns kNoKey if the CDM did not have the necessary decryption key
// to decrypt.
// Returns kNeedMoreData if more data was needed by the decoder to generate
// audio samples (e.g. during initialization).
// Returns kDecryptError if any decryption error happened.
// Returns kDecodeError if any decoding error happened.
// If the return value is not kSuccess, |sample_buffer| should be ignored by
// the caller.
//
// |audio_frames| can contain multiple audio output buffers. Each buffer must
// be serialized in this format:
//
// |<------------------- serialized audio buffer ------------------->|
// | int64_t timestamp | int64_t length | length bytes of audio data |
//
// For example, with three audio output buffers, |audio_frames| will look
// like this:
//
// |<---------------- audio_frames ------------------>|
// | audio buffer 0 | audio buffer 1 | audio buffer 2 |
virtual Status DecryptAndDecodeSamples(const InputBuffer& encrypted_buffer,
Buffer* audio_frames) = 0;
virtual ~ContentDecryptionModule() {}
};
// Represents a buffer created by Allocator implementations.
class Buffer {
public:
// Destroys the buffer in the same context as it was created.
virtual void Destroy() = 0;
virtual uint8_t* data() = 0;
virtual int32_t size() const = 0;
protected:
Buffer() {}
virtual ~Buffer() {}
private:
Buffer(const Buffer&);
void operator=(const Buffer&);
};
// Interface class that hides cross object memory allocation details from CDMs.
class Allocator {
public:
// Returns a Buffer* containing non-zero members upon success, or NULL on
// failure. The caller owns the Buffer* until it is passed back to the CDM
// wrapper.
virtual Buffer* Allocate(int32_t size) = 0;
protected:
Allocator() {}
virtual ~Allocator() {}
};
class CdmHost {
public:
CdmHost() {}
virtual ~CdmHost() {}
// Requests the host to call ContentDecryptionModule::TimerFired() |delay_ms|
// from now.
virtual void SetTimer(int64 delay_ms) = 0;
// Returns the current epoch wall time in milliseconds.
virtual double GetCurrentWallTimeMs() = 0;
};
// Represents a decrypted block that has not been decoded.
class DecryptedBlock {
public:
virtual void set_buffer(Buffer* buffer) = 0;
virtual Buffer* buffer() = 0;
// TODO(tomfinegan): Figure out if timestamp is really needed. If it is not,
// we can just pass Buffer pointers around.
virtual void set_timestamp(int64_t timestamp) = 0;
virtual int64_t timestamp() const = 0;
protected:
DecryptedBlock() {}
virtual ~DecryptedBlock() {}
};
// Represents a key message sent by the CDM.
class KeyMessage {
public:
virtual void set_session_id(const char* session_id, int32_t length) = 0;
virtual const char* session_id() const = 0;
virtual int32_t session_id_length() const = 0;
virtual void set_message(Buffer* message) = 0;
virtual Buffer* message() = 0;
virtual void set_default_url(const char* default_url, int32_t length) = 0;
virtual const char* default_url() const = 0;
virtual int32_t default_url_length() const = 0;
protected:
KeyMessage() {}
virtual ~KeyMessage() {}
};
class VideoFrame {
public:
enum VideoPlane {
kYPlane = 0,
kUPlane = 1,
kVPlane = 2,
kMaxPlanes = 3,
};
virtual void set_format(VideoFormat format) = 0;
virtual VideoFormat format() const = 0;
virtual void set_size(cdm::Size size) = 0;
virtual cdm::Size size() const = 0;
virtual void set_frame_buffer(Buffer* frame_buffer) = 0;
virtual Buffer* frame_buffer() = 0;
virtual void set_plane_offset(VideoPlane plane, int32_t offset) = 0;
virtual int32_t plane_offset(VideoPlane plane) = 0;
virtual void set_stride(VideoPlane plane, int32_t stride) = 0;
virtual int32_t stride(VideoPlane plane) = 0;
virtual void set_timestamp(int64_t timestamp) = 0;
virtual int64_t timestamp() const = 0;
protected:
VideoFrame() {}
virtual ~VideoFrame() {}
};
} // namespace cdm
#endif // WEBKIT_MEDIA_CRYPTO_PPAPI_CONTENT_DECRYPTION_MODULE_H_