From 7d982d34184fff957ed6719425e01988128d2302 Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Tue, 19 Mar 2013 08:49:21 +0100 Subject: [PATCH] hlsdemux: support encrypted streams --- gst/hls/gsthlsdemux.c | 59 ++++++++++++++++++++++++++++++++++++++++++- gst/hls/m3u8.c | 13 +++++++++- gst/hls/m3u8.h | 4 ++- 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 0d39d3f4dd..787207a130 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -47,6 +47,8 @@ #include #include +#include +#include #include "gsthlsdemux.h" static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src_%u", @@ -1209,6 +1211,55 @@ gst_hls_demux_switch_playlist (GstHLSDemux * demux) return gst_hls_demux_change_playlist (demux, bitrate * demux->bitrate_limit); } +static GstFragment * +gst_hls_demux_decrypt_fragment (GstHLSDemux * demux, + GstFragment * encrypted_fragment, const gchar * key, const guint8 * iv) +{ + GstFragment *key_fragment, *ret; + GstBuffer *key_buffer, *encrypted_buffer, *decrypted_buffer; + GstMapInfo key_info, encrypted_info, decrypted_info; + EVP_CIPHER_CTX aes_ctx; + int out_size = 0; + + GST_INFO_OBJECT (demux, "Fetching key %s", key); + key_fragment = gst_uri_downloader_fetch_uri (demux->downloader, key); + if (key_fragment == NULL) + return NULL; + + key_buffer = gst_fragment_get_buffer (key_fragment); + encrypted_buffer = gst_fragment_get_buffer (encrypted_fragment); + decrypted_buffer = + gst_buffer_new_allocate (NULL, gst_buffer_get_size (encrypted_buffer), + NULL); + + gst_buffer_map (key_buffer, &key_info, GST_MAP_READ); + gst_buffer_map (encrypted_buffer, &encrypted_info, GST_MAP_READ); + gst_buffer_map (decrypted_buffer, &decrypted_info, GST_MAP_WRITE); + + EVP_CIPHER_CTX_init (&aes_ctx); + EVP_CipherInit_ex (&aes_ctx, EVP_aes_128_cbc (), NULL, key_info.data, iv, + AES_DECRYPT); + EVP_CipherUpdate (&aes_ctx, decrypted_info.data, &out_size, + encrypted_info.data, encrypted_info.size); + EVP_CipherFinal_ex (&aes_ctx, decrypted_info.data + out_size, &out_size); + EVP_CIPHER_CTX_cleanup (&aes_ctx); + + gst_buffer_unmap (decrypted_buffer, &decrypted_info); + gst_buffer_unmap (encrypted_buffer, &encrypted_info); + gst_buffer_unmap (key_buffer, &key_info); + + gst_buffer_unref (key_buffer); + gst_buffer_unref (encrypted_buffer); + g_object_unref (key_fragment); + g_object_unref (encrypted_fragment); + + ret = gst_fragment_new (); + gst_fragment_add_buffer (ret, decrypted_buffer); + ret->completed = TRUE; + + return ret; +} + static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean caching) { @@ -1218,9 +1269,11 @@ gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean caching) GstClockTime timestamp; GstBuffer *buf; gboolean discont; + const gchar *key = NULL; + const guint8 *iv = NULL; if (!gst_m3u8_client_get_next_fragment (demux->client, &discont, - &next_fragment_uri, &duration, ×tamp)) { + &next_fragment_uri, &duration, ×tamp, &key, &iv)) { GST_INFO_OBJECT (demux, "This playlist doesn't contain more fragments"); demux->end_of_playlist = TRUE; gst_task_start (demux->stream_task); @@ -1232,10 +1285,14 @@ gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean caching) download = gst_uri_downloader_fetch_uri (demux->downloader, next_fragment_uri); + if (download && key) + download = gst_hls_demux_decrypt_fragment (demux, download, key, iv); + if (download == NULL) goto error; buf = gst_fragment_get_buffer (download); + GST_BUFFER_DURATION (buf) = duration; GST_BUFFER_PTS (buf) = timestamp; diff --git a/gst/hls/m3u8.c b/gst/hls/m3u8.c index 3b82458a13..63372239f8 100644 --- a/gst/hls/m3u8.c +++ b/gst/hls/m3u8.c @@ -102,6 +102,7 @@ gst_m3u8_media_file_free (GstM3U8MediaFile * self) g_free (self->title); g_free (self->uri); + g_free (self->key); g_free (self); } @@ -297,6 +298,14 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated) file = gst_m3u8_media_file_new (data, title, duration, self->mediasequence++); + + /* set encryption params */ + file->key = g_strdup (self->key); + if (file->key) { + guint8 *iv = file->iv + 12; + GST_WRITE_UINT32_BE (iv, file->sequence); + } + duration = 0; title = NULL; self->files = g_list_append (self->files, file); @@ -531,7 +540,7 @@ gst_m3u8_client_get_current_position (GstM3U8Client * client, gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client, gboolean * discontinuity, const gchar ** uri, GstClockTime * duration, - GstClockTime * timestamp) + GstClockTime * timestamp, const gchar ** key, const guint8 ** iv) { GList *l; GstM3U8MediaFile *file; @@ -558,6 +567,8 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client, *uri = file->uri; *duration = file->duration; + *key = file->key; + *iv = file->iv; GST_M3U8_CLIENT_UNLOCK (client); return TRUE; diff --git a/gst/hls/m3u8.h b/gst/hls/m3u8.h index 208e00a5ab..33fd500827 100644 --- a/gst/hls/m3u8.h +++ b/gst/hls/m3u8.h @@ -66,6 +66,8 @@ struct _GstM3U8MediaFile GstClockTime duration; gchar *uri; guint sequence; /* the sequence nb of this file */ + gchar *key; + guint8 iv[16]; }; struct _GstM3U8Client @@ -84,7 +86,7 @@ gboolean gst_m3u8_client_update (GstM3U8Client * client, gchar * data); void gst_m3u8_client_set_current (GstM3U8Client * client, GstM3U8 * m3u8); gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client, gboolean * discontinuity, const gchar ** uri, GstClockTime * duration, - GstClockTime * timestamp); + GstClockTime * timestamp, const gchar ** key, const guint8 ** iv); void gst_m3u8_client_get_current_position (GstM3U8Client * client, GstClockTime * timestamp); GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client);