Bug 1257726 - Update to latest upstream version of nestegg. r=kinetik draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Wed, 04 May 2016 11:14:29 +1200
changeset 371146 4760d75856af777243f002495dd0a5de518acb00
parent 370842 d6d4e8417d2fd71fdf47c319b7a217f6ace9d5a5
child 371147 254ce4056298a991b0e2ce7f1d414c39b1af28b7
child 371148 d5333d5156d1263920524e217daa7dbc2bcb94c7
child 371152 cccfd59dac70a9f05df43e2c5afe03947292586b
child 371292 ec2eff1266dca53b38280c47b4a0ee8147ac7e03
push id19253
push userbvandyk@mozilla.com
push dateThu, 26 May 2016 01:17:42 +0000
reviewerskinetik
bugs1257726
milestone49.0a1
Bug 1257726 - Update to latest upstream version of nestegg. r=kinetik Bring in updated nestegg library, the newly exposed encryption functionality will be used to enable WebM EME. MozReview-Commit-ID: Hv6hSFjlS5c
media/libnestegg/README_MOZILLA
media/libnestegg/include/nestegg.h
media/libnestegg/src/nestegg.c
--- a/media/libnestegg/README_MOZILLA
+++ b/media/libnestegg/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the nestegg
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The nestegg git repository is: git://github.com/kinetiknz/nestegg.git
 
-The git commit ID used was d493c8a7abd05c6911cd546fd6b5c82b366f5203.
+The git commit ID used was 3bc788d4de8f11a1e6b625047f49b9d35dce824f.
--- a/media/libnestegg/include/nestegg.h
+++ b/media/libnestegg/include/nestegg.h
@@ -84,16 +84,23 @@ extern "C" {
 #define NESTEGG_SEEK_END 2 /**< Seek offset relative to end of stream. */
 
 #define NESTEGG_LOG_DEBUG    1     /**< Debug level log message. */
 #define NESTEGG_LOG_INFO     10    /**< Informational level log message. */
 #define NESTEGG_LOG_WARNING  100   /**< Warning level log message. */
 #define NESTEGG_LOG_ERROR    1000  /**< Error level log message. */
 #define NESTEGG_LOG_CRITICAL 10000 /**< Critical level log message. */
 
+#define NESTEGG_ENCODING_COMPRESSION 0 /**< Content encoding type is compression. */
+#define NESTEGG_ENCODING_ENCRYPTION  1 /**< Content encoding type is encryption. */
+
+#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE       0 /**< Packet does not have signal byte */
+#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED 1 /**< Packet has signal byte and is unencrypted */
+#define NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED   2 /**< Packet has signal byte and is encrypted */
+
 #define NESTEGG_PACKET_HAS_KEYFRAME_FALSE   0 /**< Packet contains only keyframes. */
 #define NESTEGG_PACKET_HAS_KEYFRAME_TRUE    1 /**< Packet does not contain any keyframes */
 #define NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN 2 /**< Packet may or may not contain keyframes */
 
 typedef struct nestegg nestegg;               /**< Opaque handle referencing the stream state. */
 typedef struct nestegg_packet nestegg_packet; /**< Opaque handle referencing a packet of data. */
 
 /** User supplied IO context. */
@@ -283,16 +290,38 @@ int nestegg_track_video_params(nestegg *
     @param context Stream context initialized by #nestegg_init.
     @param track   Zero based track number.
     @param params  Storage for the queried audio parameters.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_track_audio_params(nestegg * context, unsigned int track,
                                nestegg_audio_params * params);
 
+/** Query the encoding status for @a track. If a track has multiple encodings
+    the first will be returned.
+    @param context Stream context initialized by #nestegg_init.
+    @param track   Zero based track number.
+    @retval #NESTEGG_ENCODING_COMPRESSION The track is compressed, but not encrypted.
+    @retval #NESTEGG_ENCODING_ENCRYPTION The track is encrypted and compressed.
+    @retval -1 Error. */
+int nestegg_track_encoding(nestegg * context, unsigned int track);
+
+/** Query the ContentEncKeyId for @a track. Will return an error if the track
+    in not encrypted, or is not recognized.
+    @param context                   Stream context initialized by #nestegg_init.
+    @param track                     Zero based track number.
+    @param content_enc_key_id        Storage for queried id. The content encryption key used.
+                                     Owned by nestegg and will be freed separately.
+    @param content_enc_key_id_length Length of the queried ContentEncKeyId in bytes.
+    @retval  0 Success.
+    @retval -1 Error. */
+int nestegg_track_content_enc_key_id(nestegg * context, unsigned int track,
+                                     unsigned char const ** content_enc_key_id,
+                                     size_t * content_enc_key_id_length);
+
 /** Query the default frame duration for @a track.  For a video track, this
     is typically the inverse of the video frame rate.
     @param context  Stream context initialized by #nestegg_init.
     @param track    Zero based track number.
     @param duration Storage for the default duration in nanoseconds.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_track_default_duration(nestegg * context, unsigned int track,
@@ -382,16 +411,39 @@ int nestegg_packet_additional_data(neste
 /** Returns discard_padding for given packet
     @param packet  Packet initialized by #nestegg_read_packet.
     @param discard_padding pointer to store discard padding in.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_discard_padding(nestegg_packet * packet,
                                    int64_t * discard_padding);
 
+/** Query if a packet is encrypted.
+    @param packet Packet initialized by #nestegg_read_packet.
+    @retval  #NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE No signal byte, encryption
+             information not read from packet.
+    @retval  #NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED Encrypted bit not
+             set, encryption information not read from packet.
+    @retval  #NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED Encrypted bit set,
+             encryption infomation read from packet.
+    @retval -1 Error.*/
+int nestegg_packet_encryption(nestegg_packet * packet);
+
+/** Query the IV for an encrypted packet. Expects a packet from an encrypted
+    track, and will return error if given a packet that has no signal btye.
+    @param packet Packet initialized by #nestegg_read_packet.
+    @param iv     Storage for queried iv.
+    @param length Length of returned iv, may be 0.
+                  The data is owned by the #nestegg_packet packet.
+    @retval  0 Success.
+    @retval -1 Error.
+  */
+int nestegg_packet_iv(nestegg_packet * packet, unsigned char const ** iv,
+                      size_t * length);
+
 /** Returns reference_block given packet
     @param packet          Packet initialized by #nestegg_read_packet.
     @param reference_block pointer to store reference block in.
     @retval  0 Success.
     @retval -1 Error. */
 int nestegg_packet_reference_block(nestegg_packet * packet,
                                    int64_t * reference_block);
 
--- a/media/libnestegg/src/nestegg.c
+++ b/media/libnestegg/src/nestegg.c
@@ -7,148 +7,172 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "halloc.h"
 #include "nestegg/nestegg.h"
 
 /* EBML Elements */
-#define ID_EBML                 0x1a45dfa3
-#define ID_EBML_VERSION         0x4286
-#define ID_EBML_READ_VERSION    0x42f7
-#define ID_EBML_MAX_ID_LENGTH   0x42f2
-#define ID_EBML_MAX_SIZE_LENGTH 0x42f3
-#define ID_DOCTYPE              0x4282
-#define ID_DOCTYPE_VERSION      0x4287
-#define ID_DOCTYPE_READ_VERSION 0x4285
+#define ID_EBML                     0x1a45dfa3
+#define ID_EBML_VERSION             0x4286
+#define ID_EBML_READ_VERSION        0x42f7
+#define ID_EBML_MAX_ID_LENGTH       0x42f2
+#define ID_EBML_MAX_SIZE_LENGTH     0x42f3
+#define ID_DOCTYPE                  0x4282
+#define ID_DOCTYPE_VERSION          0x4287
+#define ID_DOCTYPE_READ_VERSION     0x4285
 
 /* Global Elements */
-#define ID_VOID                 0xec
-#define ID_CRC32                0xbf
+#define ID_VOID                     0xec
+#define ID_CRC32                    0xbf
 
 /* WebM Elements */
-#define ID_SEGMENT              0x18538067
+#define ID_SEGMENT                  0x18538067
 
 /* Seek Head Elements */
-#define ID_SEEK_HEAD            0x114d9b74
-#define ID_SEEK                 0x4dbb
-#define ID_SEEK_ID              0x53ab
-#define ID_SEEK_POSITION        0x53ac
+#define ID_SEEK_HEAD                0x114d9b74
+#define ID_SEEK                     0x4dbb
+#define ID_SEEK_ID                  0x53ab
+#define ID_SEEK_POSITION            0x53ac
 
 /* Info Elements */
-#define ID_INFO                 0x1549a966
-#define ID_TIMECODE_SCALE       0x2ad7b1
-#define ID_DURATION             0x4489
+#define ID_INFO                     0x1549a966
+#define ID_TIMECODE_SCALE           0x2ad7b1
+#define ID_DURATION                 0x4489
 
 /* Cluster Elements */
-#define ID_CLUSTER              0x1f43b675
-#define ID_TIMECODE             0xe7
-#define ID_BLOCK_GROUP          0xa0
-#define ID_SIMPLE_BLOCK         0xa3
+#define ID_CLUSTER                  0x1f43b675
+#define ID_TIMECODE                 0xe7
+#define ID_BLOCK_GROUP              0xa0
+#define ID_SIMPLE_BLOCK             0xa3
 
 /* BlockGroup Elements */
-#define ID_BLOCK                0xa1
-#define ID_BLOCK_ADDITIONS      0x75a1
-#define ID_BLOCK_DURATION       0x9b
-#define ID_REFERENCE_BLOCK      0xfb
-#define ID_DISCARD_PADDING      0x75a2
+#define ID_BLOCK                    0xa1
+#define ID_BLOCK_ADDITIONS          0x75a1
+#define ID_BLOCK_DURATION           0x9b
+#define ID_REFERENCE_BLOCK          0xfb
+#define ID_DISCARD_PADDING          0x75a2
 
 /* BlockAdditions Elements */
-#define ID_BLOCK_MORE           0xa6
+#define ID_BLOCK_MORE               0xa6
 
 /* BlockMore Elements */
-#define ID_BLOCK_ADD_ID         0xee
-#define ID_BLOCK_ADDITIONAL     0xa5
+#define ID_BLOCK_ADD_ID             0xee
+#define ID_BLOCK_ADDITIONAL         0xa5
 
 /* Tracks Elements */
-#define ID_TRACKS               0x1654ae6b
-#define ID_TRACK_ENTRY          0xae
-#define ID_TRACK_NUMBER         0xd7
-#define ID_TRACK_UID            0x73c5
-#define ID_TRACK_TYPE           0x83
-#define ID_FLAG_ENABLED         0xb9
-#define ID_FLAG_DEFAULT         0x88
-#define ID_FLAG_LACING          0x9c
-#define ID_TRACK_TIMECODE_SCALE 0x23314f
-#define ID_LANGUAGE             0x22b59c
-#define ID_CODEC_ID             0x86
-#define ID_CODEC_PRIVATE        0x63a2
-#define ID_CODEC_DELAY          0x56aa
-#define ID_SEEK_PREROLL         0x56bb
-#define ID_DEFAULT_DURATION     0x23e383
+#define ID_TRACKS                   0x1654ae6b
+#define ID_TRACK_ENTRY              0xae
+#define ID_TRACK_NUMBER             0xd7
+#define ID_TRACK_UID                0x73c5
+#define ID_TRACK_TYPE               0x83
+#define ID_FLAG_ENABLED             0xb9
+#define ID_FLAG_DEFAULT             0x88
+#define ID_FLAG_LACING              0x9c
+#define ID_TRACK_TIMECODE_SCALE     0x23314f
+#define ID_LANGUAGE                 0x22b59c
+#define ID_CODEC_ID                 0x86
+#define ID_CODEC_PRIVATE            0x63a2
+#define ID_CODEC_DELAY              0x56aa
+#define ID_SEEK_PREROLL             0x56bb
+#define ID_DEFAULT_DURATION         0x23e383
 
 /* Video Elements */
-#define ID_VIDEO                0xe0
-#define ID_STEREO_MODE          0x53b8
-#define ID_ALPHA_MODE           0x53c0
-#define ID_PIXEL_WIDTH          0xb0
-#define ID_PIXEL_HEIGHT         0xba
-#define ID_PIXEL_CROP_BOTTOM    0x54aa
-#define ID_PIXEL_CROP_TOP       0x54bb
-#define ID_PIXEL_CROP_LEFT      0x54cc
-#define ID_PIXEL_CROP_RIGHT     0x54dd
-#define ID_DISPLAY_WIDTH        0x54b0
-#define ID_DISPLAY_HEIGHT       0x54ba
+#define ID_VIDEO                    0xe0
+#define ID_STEREO_MODE              0x53b8
+#define ID_ALPHA_MODE               0x53c0
+#define ID_PIXEL_WIDTH              0xb0
+#define ID_PIXEL_HEIGHT             0xba
+#define ID_PIXEL_CROP_BOTTOM        0x54aa
+#define ID_PIXEL_CROP_TOP           0x54bb
+#define ID_PIXEL_CROP_LEFT          0x54cc
+#define ID_PIXEL_CROP_RIGHT         0x54dd
+#define ID_DISPLAY_WIDTH            0x54b0
+#define ID_DISPLAY_HEIGHT           0x54ba
 
 /* Audio Elements */
-#define ID_AUDIO                0xe1
-#define ID_SAMPLING_FREQUENCY   0xb5
-#define ID_CHANNELS             0x9f
-#define ID_BIT_DEPTH            0x6264
+#define ID_AUDIO                    0xe1
+#define ID_SAMPLING_FREQUENCY       0xb5
+#define ID_CHANNELS                 0x9f
+#define ID_BIT_DEPTH                0x6264
 
 /* Cues Elements */
-#define ID_CUES                 0x1c53bb6b
-#define ID_CUE_POINT            0xbb
-#define ID_CUE_TIME             0xb3
-#define ID_CUE_TRACK_POSITIONS  0xb7
-#define ID_CUE_TRACK            0xf7
-#define ID_CUE_CLUSTER_POSITION 0xf1
-#define ID_CUE_BLOCK_NUMBER     0x5378
+#define ID_CUES                     0x1c53bb6b
+#define ID_CUE_POINT                0xbb
+#define ID_CUE_TIME                 0xb3
+#define ID_CUE_TRACK_POSITIONS      0xb7
+#define ID_CUE_TRACK                0xf7
+#define ID_CUE_CLUSTER_POSITION     0xf1
+#define ID_CUE_BLOCK_NUMBER         0x5378
+
+/* Encoding Elements */
+#define ID_CONTENT_ENCODINGS        0x6d80
+#define ID_CONTENT_ENCODING         0x6240
+#define ID_CONTENT_ENCODING_TYPE    0x5033
+
+/* Encryption Elements */
+#define ID_CONTENT_ENCRYPTION       0x5035
+#define ID_CONTENT_ENC_ALGO         0x47e1
+#define ID_CONTENT_ENC_KEY_ID       0x47e2
+#define ID_CONTENT_ENC_AES_SETTINGS 0x47e7
+#define ID_AES_SETTINGS_CIPHER_MODE 0x47e8
 
 /* EBML Types */
 enum ebml_type_enum {
   TYPE_UNKNOWN,
   TYPE_MASTER,
   TYPE_UINT,
   TYPE_FLOAT,
   TYPE_STRING,
   TYPE_BINARY
 };
 
-#define LIMIT_STRING            (1 << 20)
-#define LIMIT_BINARY            (1 << 24)
-#define LIMIT_BLOCK             (1 << 30)
-#define LIMIT_FRAME             (1 << 28)
+#define LIMIT_STRING                (1 << 20)
+#define LIMIT_BINARY                (1 << 24)
+#define LIMIT_BLOCK                 (1 << 30)
+#define LIMIT_FRAME                 (1 << 28)
 
 /* Field Flags */
-#define DESC_FLAG_NONE          0
-#define DESC_FLAG_MULTI         (1 << 0)
-#define DESC_FLAG_SUSPEND       (1 << 1)
-#define DESC_FLAG_OFFSET        (1 << 2)
+#define DESC_FLAG_NONE              0
+#define DESC_FLAG_MULTI             (1 << 0)
+#define DESC_FLAG_SUSPEND           (1 << 1)
+#define DESC_FLAG_OFFSET            (1 << 2)
 
 /* Block Header Flags */
 #define SIMPLE_BLOCK_FLAGS_KEYFRAME (1 << 7)
 #define BLOCK_FLAGS_LACING          6
 
 /* Lacing Constants */
-#define LACING_NONE             0
-#define LACING_XIPH             1
-#define LACING_FIXED            2
-#define LACING_EBML             3
+#define LACING_NONE                 0
+#define LACING_XIPH                 1
+#define LACING_FIXED                2
+#define LACING_EBML                 3
 
 /* Track Types */
-#define TRACK_TYPE_VIDEO        1
-#define TRACK_TYPE_AUDIO        2
+#define TRACK_TYPE_VIDEO            1
+#define TRACK_TYPE_AUDIO            2
 
 /* Track IDs */
-#define TRACK_ID_VP8            "V_VP8"
-#define TRACK_ID_VP9            "V_VP9"
-#define TRACK_ID_VORBIS         "A_VORBIS"
-#define TRACK_ID_OPUS           "A_OPUS"
+#define TRACK_ID_VP8                "V_VP8"
+#define TRACK_ID_VP9                "V_VP9"
+#define TRACK_ID_VORBIS             "A_VORBIS"
+#define TRACK_ID_OPUS               "A_OPUS"
+
+/* Track Encryption */
+#define CONTENT_ENC_ALGO_AES        5
+#define AES_SETTINGS_CIPHER_CTR     1
+
+/* Packet Encryption */
+#define SIGNAL_BYTE_SIZE            1
+#define IV_SIZE                     8
+
+/* Signal Byte */
+#define PACKET_ENCRYPTED            1
+#define ENCRYPTED_BIT_MASK          (1 << 0)
 
 enum vint_mask {
   MASK_NONE,
   MASK_FIRST_BIT
 };
 
 struct ebml_binary {
   unsigned char * data;
@@ -218,32 +242,52 @@ struct video {
 };
 
 struct audio {
   struct ebml_type sampling_frequency;
   struct ebml_type channels;
   struct ebml_type bit_depth;
 };
 
+struct content_enc_aes_settings {
+  struct ebml_type aes_settings_cipher_mode;
+};
+
+struct content_encryption {
+  struct ebml_type content_enc_algo;
+  struct ebml_type content_enc_key_id;
+  struct ebml_list content_enc_aes_settings;
+};
+
+struct content_encoding {
+  struct ebml_type content_encoding_type;
+  struct ebml_list content_encryption;
+};
+
+struct content_encodings {
+  struct ebml_list content_encoding;
+};
+
 struct track_entry {
   struct ebml_type number;
   struct ebml_type uid;
   struct ebml_type type;
   struct ebml_type flag_enabled;
   struct ebml_type flag_default;
   struct ebml_type flag_lacing;
   struct ebml_type track_timecode_scale;
   struct ebml_type language;
   struct ebml_type codec_id;
   struct ebml_type codec_private;
   struct ebml_type codec_delay;
   struct ebml_type seek_preroll;
   struct ebml_type default_duration;
   struct video video;
   struct audio audio;
+  struct content_encodings content_encodings;
 };
 
 struct tracks {
   struct ebml_list track_entry;
 };
 
 struct cue_track_positions {
   struct ebml_type track;
@@ -280,19 +324,26 @@ struct list_node {
 
 struct saved_state {
   int64_t stream_offset;
   uint64_t last_id;
   uint64_t last_size;
   int last_valid;
 };
 
+struct frame_encryption {
+  unsigned char * iv;
+  size_t length;
+  uint8_t signal_byte;
+};
+
 struct frame {
   unsigned char * data;
   size_t length;
+  struct frame_encryption * frame_encryption;
   struct frame * next;
 };
 
 struct block_additional {
   unsigned int id;
   unsigned char * data;
   size_t length;
   struct block_additional * next;
@@ -404,32 +455,56 @@ static struct ebml_element_desc ne_video
 
 static struct ebml_element_desc ne_audio_elements[] = {
   E_FIELD(ID_SAMPLING_FREQUENCY, TYPE_FLOAT, struct audio, sampling_frequency),
   E_FIELD(ID_CHANNELS, TYPE_UINT, struct audio, channels),
   E_FIELD(ID_BIT_DEPTH, TYPE_UINT, struct audio, bit_depth),
   E_LAST
 };
 
+static struct ebml_element_desc ne_content_enc_aes_settings_elements[] = {
+  E_FIELD(ID_AES_SETTINGS_CIPHER_MODE, TYPE_UINT, struct content_enc_aes_settings, aes_settings_cipher_mode),
+  E_LAST
+};
+
+static struct ebml_element_desc ne_content_encryption_elements[] = {
+  E_FIELD(ID_CONTENT_ENC_ALGO, TYPE_UINT, struct content_encryption, content_enc_algo),
+  E_FIELD(ID_CONTENT_ENC_KEY_ID, TYPE_BINARY, struct content_encryption, content_enc_key_id),
+  E_MASTER(ID_CONTENT_ENC_AES_SETTINGS, TYPE_MASTER, struct content_encryption, content_enc_aes_settings),
+  E_LAST
+};
+
+static struct ebml_element_desc ne_content_encoding_elements[] = {
+  E_FIELD(ID_CONTENT_ENCODING_TYPE, TYPE_UINT, struct content_encoding, content_encoding_type),
+  E_MASTER(ID_CONTENT_ENCRYPTION, TYPE_MASTER, struct content_encoding, content_encryption),
+  E_LAST
+};
+
+static struct ebml_element_desc ne_content_encodings_elements[] = {
+  E_MASTER(ID_CONTENT_ENCODING, TYPE_MASTER, struct content_encodings, content_encoding),
+  E_LAST
+};
+
 static struct ebml_element_desc ne_track_entry_elements[] = {
   E_FIELD(ID_TRACK_NUMBER, TYPE_UINT, struct track_entry, number),
   E_FIELD(ID_TRACK_UID, TYPE_UINT, struct track_entry, uid),
   E_FIELD(ID_TRACK_TYPE, TYPE_UINT, struct track_entry, type),
   E_FIELD(ID_FLAG_ENABLED, TYPE_UINT, struct track_entry, flag_enabled),
   E_FIELD(ID_FLAG_DEFAULT, TYPE_UINT, struct track_entry, flag_default),
   E_FIELD(ID_FLAG_LACING, TYPE_UINT, struct track_entry, flag_lacing),
   E_FIELD(ID_TRACK_TIMECODE_SCALE, TYPE_FLOAT, struct track_entry, track_timecode_scale),
   E_FIELD(ID_LANGUAGE, TYPE_STRING, struct track_entry, language),
   E_FIELD(ID_CODEC_ID, TYPE_STRING, struct track_entry, codec_id),
   E_FIELD(ID_CODEC_PRIVATE, TYPE_BINARY, struct track_entry, codec_private),
   E_FIELD(ID_CODEC_DELAY, TYPE_UINT, struct track_entry, codec_delay),
   E_FIELD(ID_SEEK_PREROLL, TYPE_UINT, struct track_entry, seek_preroll),
   E_FIELD(ID_DEFAULT_DURATION, TYPE_UINT, struct track_entry, default_duration),
   E_SINGLE_MASTER(ID_VIDEO, TYPE_MASTER, struct track_entry, video),
   E_SINGLE_MASTER(ID_AUDIO, TYPE_MASTER, struct track_entry, audio),
+  E_SINGLE_MASTER(ID_CONTENT_ENCODINGS, TYPE_MASTER, struct track_entry, content_encodings),
   E_LAST
 };
 
 static struct ebml_element_desc ne_tracks_elements[] = {
   E_MASTER(ID_TRACK_ENTRY, TYPE_MASTER, struct tracks, track_entry),
   E_LAST
 };
 
@@ -1068,16 +1143,65 @@ ne_parse(nestegg * ctx, struct ebml_elem
   if (r != 1)
     while (ctx->ancestor)
       ne_ctx_pop(ctx);
 
   return r;
 }
 
 static int
+ne_read_block_encryption(nestegg * ctx, struct track_entry const * entry,
+                         uint64_t * encoding_type, uint64_t * encryption_algo,
+                         uint64_t * encryption_mode)
+{
+  struct content_encoding * encoding;
+  struct content_encryption * encryption;
+  struct content_enc_aes_settings * aes_settings;
+
+  *encoding_type = 0;
+  if (entry->content_encodings.content_encoding.head) {
+    encoding = entry->content_encodings.content_encoding.head->data;
+    if (ne_get_uint(encoding->content_encoding_type, encoding_type) != 0)
+      return -1;
+
+    if (*encoding_type == NESTEGG_ENCODING_ENCRYPTION) {
+      /* Metadata states content is encrypted */
+      if (!encoding->content_encryption.head)
+        return -1;
+
+      encryption = encoding->content_encryption.head->data;
+      if (ne_get_uint(encryption->content_enc_algo, encryption_algo) != 0) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAlgo element found");
+        return -1;
+      }
+
+      if (*encryption_algo != CONTENT_ENC_ALGO_AES) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo used");
+        return -1;
+      }
+
+      if (!encryption->content_enc_aes_settings.head) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAESSettings element found");
+        return -1;
+      }
+
+      aes_settings = encryption->content_enc_aes_settings.head->data;
+      *encryption_mode = AES_SETTINGS_CIPHER_CTR;
+      ne_get_uint(aes_settings->aes_settings_cipher_mode, encryption_mode);
+
+      if (*encryption_mode != AES_SETTINGS_CIPHER_CTR) {
+        ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingsCipherMode used");
+        return -1;
+      }
+    }
+  }
+  return 1;
+}
+
+static int
 ne_read_xiph_lace_value(nestegg_io * io, uint64_t * value, size_t * consumed)
 {
   int r;
   uint64_t lace;
 
   r = ne_read_uint(io, &lace, 1);
   if (r != 1)
     return r;
@@ -1219,20 +1343,21 @@ static int
 ne_read_block(nestegg * ctx, uint64_t block_id, uint64_t block_size, nestegg_packet ** data)
 {
   int r;
   int64_t timecode, abs_timecode;
   nestegg_packet * pkt;
   struct frame * f, * last;
   struct track_entry * entry;
   double track_scale;
-  uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total;
+  uint64_t track_number, length, frame_sizes[256], cluster_tc, flags, frames, tc_scale, total,
+           encoding_type, encryption_algo, encryption_mode;
   unsigned int i, lacing, track;
-  uint8_t keyframe = NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN;
-  size_t consumed = 0;
+  uint8_t signal_byte, keyframe = NESTEGG_PACKET_HAS_KEYFRAME_UNKNOWN;
+  size_t consumed = 0, data_size, encryption_size;
 
   *data = NULL;
 
   if (block_size > LIMIT_BLOCK)
     return -1;
 
   r = ne_read_vint(ctx->io, &track_number, &length);
   if (r != 1)
@@ -1319,16 +1444,26 @@ ne_read_block(nestegg * ctx, uint64_t bl
 
   if (ne_map_track_number_to_index(ctx, track_number, &track) != 0)
     return -1;
 
   entry = ne_find_track_entry(ctx, track);
   if (!entry)
     return -1;
 
+  r = ne_read_block_encryption(ctx, entry, &encoding_type, &encryption_algo, &encryption_mode);
+  if (r != 1)
+    return r;
+
+  /* Encryption does not support lacing */
+  if (lacing != LACING_NONE && encoding_type == NESTEGG_ENCODING_ENCRYPTION) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Encrypted blocks may not also be laced");
+    return -1;
+  }
+
   track_scale = 1.0;
 
   tc_scale = ne_get_timecode_scale(ctx);
 
   if (!ctx->read_cluster_timecode)
     return -1;
   cluster_tc = ctx->cluster_timecode;
 
@@ -1352,25 +1487,74 @@ ne_read_block(nestegg * ctx, uint64_t bl
       nestegg_free_packet(pkt);
       return -1;
     }
     f = ne_alloc(sizeof(*f));
     if (!f) {
       nestegg_free_packet(pkt);
       return -1;
     }
-    f->data = ne_alloc(frame_sizes[i]);
+    /* Parse encryption */
+    if (encoding_type == NESTEGG_ENCODING_ENCRYPTION) {
+      r = ne_io_read(ctx->io, &signal_byte, SIGNAL_BYTE_SIZE);
+      if (r != 1) {
+        free(f);
+        nestegg_free_packet(pkt);
+        return r;
+      }
+      f->frame_encryption = ne_alloc(sizeof(*f->frame_encryption));
+      if (!f->frame_encryption) {
+        free(f);
+        nestegg_free_packet(pkt);
+        return -1;
+      }
+      f->frame_encryption->signal_byte = signal_byte;
+      if ((signal_byte & ENCRYPTED_BIT_MASK) == PACKET_ENCRYPTED) {
+        f->frame_encryption->iv = ne_alloc(IV_SIZE);
+        if (!f->frame_encryption->iv) {
+          free(f->frame_encryption);
+          free(f);
+          nestegg_free_packet(pkt);
+          return -1;
+        }
+        r = ne_io_read(ctx->io, f->frame_encryption->iv, IV_SIZE);
+        if (r != 1) {
+          free(f->frame_encryption);
+          free(f);
+          nestegg_free_packet(pkt);
+          return r;
+        }
+        f->frame_encryption->length = IV_SIZE;
+        encryption_size = SIGNAL_BYTE_SIZE + IV_SIZE;
+      } else {
+        f->frame_encryption->iv = NULL;
+        f->frame_encryption->length = 0;
+        encryption_size = SIGNAL_BYTE_SIZE;
+      }
+    } else {
+      f->frame_encryption = NULL;
+      encryption_size = 0;
+    }
+    data_size = frame_sizes[i] - encryption_size;
+    /* Encryption parsed */
+    f->data = ne_alloc(data_size);
     if (!f->data) {
+      if (f->frame_encryption)
+        free(f->frame_encryption->iv);
+      free(f->frame_encryption);
       free(f);
       nestegg_free_packet(pkt);
       return -1;
     }
-    f->length = frame_sizes[i];
-    r = ne_io_read(ctx->io, f->data, frame_sizes[i]);
+    f->length = data_size;
+    r = ne_io_read(ctx->io, f->data, data_size);
     if (r != 1) {
+      if (f->frame_encryption)
+        free(f->frame_encryption->iv);
+      free(f->frame_encryption);
       free(f->data);
       free(f);
       nestegg_free_packet(pkt);
       return r;
     }
 
     if (!last)
       pkt->frame = f;
@@ -2297,16 +2481,118 @@ nestegg_track_audio_params(nestegg * ctx
   value = 0;
   ne_get_uint(entry->seek_preroll, &value);
   params->seek_preroll = value;
 
   return 0;
 }
 
 int
+nestegg_track_encoding(nestegg * ctx, unsigned int track)
+{
+  struct track_entry * entry;
+  struct content_encoding * encoding;
+  uint64_t encoding_value;
+
+  entry = ne_find_track_entry(ctx, track);
+  if (!entry) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found");
+    return -1;
+  }
+
+  if (!entry->content_encodings.content_encoding.head) {
+    /* Default encoding is compression */
+    return NESTEGG_ENCODING_COMPRESSION;
+  }
+
+  encoding = entry->content_encodings.content_encoding.head->data;
+
+  encoding_value = NESTEGG_ENCODING_COMPRESSION;
+  ne_get_uint(encoding->content_encoding_type, &encoding_value);
+  if (encoding_value != NESTEGG_ENCODING_COMPRESSION && encoding_value != NESTEGG_ENCODING_ENCRYPTION) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Invalid ContentEncoding element found");
+    return -1;
+  }
+
+  return encoding_value;
+}
+
+int
+nestegg_track_content_enc_key_id(nestegg * ctx, unsigned int track, unsigned char const ** content_enc_key_id,
+                                 size_t * content_enc_key_id_length)
+{
+  struct track_entry * entry;
+  struct content_encoding * encoding;
+  struct content_encryption * encryption;
+  struct content_enc_aes_settings * aes_settings;
+  struct nestegg_encryption_params;
+  uint64_t value;
+  struct ebml_binary enc_key_id;
+
+  entry = ne_find_track_entry(ctx, track);
+  if (!entry) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No track entry found");
+    return -1;
+  }
+
+  if (!entry->content_encodings.content_encoding.head) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncoding element found");
+    return -1;
+  }
+
+  encoding = entry->content_encodings.content_encoding.head->data;
+
+  value = 0;
+  ne_get_uint(encoding->content_encoding_type, &value);
+  if (value != NESTEGG_ENCODING_ENCRYPTION) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncodingType found");
+    return -1;
+  }
+
+  if (!encoding->content_encryption.head) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncryption element found");
+    return -1;
+  }
+
+  encryption = encoding->content_encryption.head->data;
+
+  value = 0;
+  ne_get_uint(encryption->content_enc_algo, &value);
+
+  if (value != CONTENT_ENC_ALGO_AES) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed ContentEncAlgo found");
+    return -1;
+  }
+
+  if (!encryption->content_enc_aes_settings.head) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "No ContentEncAesSettings element found");
+    return -1;
+  }
+
+  aes_settings = encryption->content_enc_aes_settings.head->data;
+  value = AES_SETTINGS_CIPHER_CTR;
+  ne_get_uint(aes_settings->aes_settings_cipher_mode, &value);
+
+  if (value != AES_SETTINGS_CIPHER_CTR) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Disallowed AESSettingCipherMode used");
+    return -1;
+  }
+
+  if (ne_get_binary(encryption->content_enc_key_id, &enc_key_id) != 0) {
+    ctx->log(ctx, NESTEGG_LOG_ERROR, "Could not retrieve track ContentEncKeyId");
+    return -1;
+  }
+
+  *content_enc_key_id = enc_key_id.data;
+  *content_enc_key_id_length = enc_key_id.length;
+
+  return 0;
+}
+
+int
 nestegg_track_default_duration(nestegg * ctx, unsigned int track,
                                uint64_t * duration)
 {
   struct track_entry * entry;
   uint64_t value;
 
   entry = ne_find_track_entry(ctx, track);
   if (!entry)
@@ -2471,16 +2757,20 @@ void
 nestegg_free_packet(nestegg_packet * pkt)
 {
   struct frame * frame;
   struct block_additional * block_additional;
 
   while (pkt->frame) {
     frame = pkt->frame;
     pkt->frame = frame->next;
+    if (frame->frame_encryption) {
+      free(frame->frame_encryption->iv);
+    }
+    free(frame->frame_encryption);
     free(frame->data);
     free(frame);
   }
 
   while (pkt->block_additional) {
     block_additional = pkt->block_additional;
     pkt->block_additional = block_additional->next;
     free(block_additional->data);
@@ -2592,16 +2882,60 @@ nestegg_packet_additional_data(nestegg_p
     }
     a = a->next;
   }
 
   return -1;
 }
 
 int
+nestegg_packet_encryption(nestegg_packet * pkt)
+{
+  struct frame * f = pkt->frame;
+  unsigned char encrypted_bit;
+
+  if (!f->frame_encryption)
+    return NESTEGG_PACKET_HAS_SIGNAL_BYTE_FALSE;
+
+  /* Should never have parsed blocks with both encryption and lacing */
+  assert(f->next == NULL);
+
+  encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK;
+
+  if (encrypted_bit != PACKET_ENCRYPTED)
+    return NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED;
+
+  return NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED;
+}
+
+int
+nestegg_packet_iv(nestegg_packet * pkt, unsigned char const ** iv, size_t * length)
+{
+  struct frame * f = pkt->frame;
+  unsigned char encrypted_bit;
+
+  *iv = NULL;
+  *length = 0;
+  if (!f->frame_encryption)
+    return -1;
+
+  /* Should never have parsed blocks with both encryption and lacing */
+  assert(f->next == NULL);
+
+  encrypted_bit = f->frame_encryption->signal_byte & ENCRYPTED_BIT_MASK;
+
+  if (encrypted_bit != PACKET_ENCRYPTED)
+    return 0;
+
+  *iv = f->frame_encryption->iv;
+  *length = f->frame_encryption->length;
+  return 0;
+}
+
+int
 nestegg_has_cues(nestegg * ctx)
 {
   return ctx->segment.cues.cue_point.head ||
     ne_find_seek_for_id(ctx->segment.seek_head.head, ID_CUES);
 }
 
 int
 nestegg_sniff(unsigned char const * buffer, size_t length)