On Wed, Jan 07, 2015 at 08:59:11AM -0600, Christian Suloway wrote: > Added HLS encryption with -hls_key_info_file <key_info_file> option. The > first line of key_info_file specifies the key URI for the playlist. The > second line specifies the path to the file containing the encryption > key. An optional third line specifies an IV to use instead of the > segment number. Changes to key_info_file will be reflected in segment > encryption along with an entry in the playlist for the new key URI and > IV. > > Signed-off-by: Christian Suloway <csulo...@globaleagleent.com>
Please add a testcase/example to either the documentation or commit message > --- > doc/muxers.texi | 9 ++++ > libavformat/hlsenc.c | 130 > +++++++++++++++++++++++++++++++++++++++++++++++++-- > 2 files changed, 136 insertions(+), 3 deletions(-) > > diff --git a/doc/muxers.texi b/doc/muxers.texi > index a1264d2..f2ecf8b 100644 > --- a/doc/muxers.texi > +++ b/doc/muxers.texi > @@ -263,6 +263,15 @@ ffmpeg in.nut -hls_segment_filename 'file%03d.ts' > out.m3u8 > This example will produce the playlist, @file{out.m3u8}, and segment files: > @file{file000.ts}, @file{file001.ts}, @file{file002.ts}, etc. > > +@item hls_key_info_file @var{file} > +Use in the information in @var{file} for segment encryption. The first line > of > +@var{file} specifies the key URI for the playlist. The second line specifies > +the path to the file containing the encryption key as a single packed array > of > +16 octets in binary format. The optional third line specifies a hexidecimal > +string for the initialization vector (IV) to be used instead of the segment > +number. Changes to @var{file} will result in segment encryption with the new > +key/IV and an entry in the playlist for the new key URI/IV. > + > @item hls_flags single_file > If this flag is set, the muxer will store all segments in a single MPEG-TS > file, and will use byte ranges in the playlist. HLS playlists generated with > diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c > index f46e8d4..7f2bc96 100644 > --- a/libavformat/hlsenc.c > +++ b/libavformat/hlsenc.c > @@ -37,12 +37,18 @@ > #include "internal.h" > #include "os_support.h" > > +#define BLOCKSIZE 16 > +#define LINE_BUFFER_SIZE 1024 > + > typedef struct HLSSegment { > char filename[1024]; > double duration; /* in seconds */ > int64_t pos; > int64_t size; > > + char key_uri[LINE_BUFFER_SIZE + 1]; > + char iv_string[BLOCKSIZE*2 + 1]; > + > struct HLSSegment *next; > } HLSSegment; > > @@ -86,6 +92,12 @@ typedef struct HLSContext { > char *format_options_str; > AVDictionary *format_options; > > + char *key_info_file; > + char key_file[LINE_BUFFER_SIZE + 1]; > + char key_uri[LINE_BUFFER_SIZE + 1]; > + char key_string[BLOCKSIZE*2 + 1]; > + char iv_string[BLOCKSIZE*2 + 1]; > + > AVIOContext *pb; > } HLSContext; > > @@ -154,6 +166,62 @@ fail: > return ret; > } > > +static int hls_encryption_start(AVFormatContext *s) > +{ > + HLSContext *hls = s->priv_data; > + int ret; > + AVIOContext *pb; > + uint8_t key[BLOCKSIZE]; > + > + if ((ret = avio_open2(&pb, hls->key_info_file, AVIO_FLAG_READ, > + &s->interrupt_callback, NULL)) < 0) { > + av_log(hls, AV_LOG_ERROR, > + "error opening key info file %s\n", hls->key_info_file); > + return ret; > + } > + > + ff_get_line(pb, hls->key_uri, sizeof(hls->key_uri)); > + hls->key_uri[strcspn(hls->key_uri, "\r\n")] = '\0'; > + > + ff_get_line(pb, hls->key_file, sizeof(hls->key_file)); > + hls->key_file[strcspn(hls->key_file, "\r\n")] = '\0'; > + > + ff_get_line(pb, hls->iv_string, sizeof(hls->iv_string)); > + hls->iv_string[strcspn(hls->iv_string, "\r\n")] = '\0'; in what case are the strcspn() needed ? > + > + avio_close(pb); > + > + if (!*hls->key_uri) { > + av_log(hls, AV_LOG_ERROR, "no key URI specified in key info file\n"); > + return AVERROR(EINVAL); > + } > + av_log(hls, AV_LOG_DEBUG, "key URI = %s\n", hls->key_uri); > + > + if (!*hls->key_file) { > + av_log(hls, AV_LOG_ERROR, "no key file specified in key info > file\n"); > + return AVERROR(EINVAL); > + } > + av_log(hls, AV_LOG_DEBUG, "key file = %s\n", hls->key_file); > + > + if ((ret = avio_open2(&pb, hls->key_file, AVIO_FLAG_READ, > + &s->interrupt_callback, NULL)) < 0) { > + av_log(hls, AV_LOG_ERROR, "error opening key file %s\n", > hls->key_file); > + return ret; > + } > + > + ret = avio_read(pb, key, sizeof(key)); > + avio_close(pb); > + if (ret != sizeof(key)) { > + av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", > hls->key_file); > + if (ret >= 0 || ret == AVERROR_EOF) > + ret = AVERROR(EINVAL); > + return ret; > + } > + ff_data_to_hex(hls->key_string, key, sizeof(key), 0); > + > + return 0; > +} > + > static int hls_mux_init(AVFormatContext *s) > { > HLSContext *hls = s->priv_data; > @@ -200,6 +268,11 @@ static int hls_append_segment(HLSContext *hls, double > duration, int64_t pos, > en->size = size; > en->next = NULL; > > + if (hls->key_info_file) { > + av_strlcpy(en->key_uri, hls->key_uri, sizeof(en->key_uri)); > + av_strlcpy(en->iv_string, hls->iv_string, sizeof(en->iv_string)); > + } > + > if (!hls->segments) > hls->segments = en; > else > @@ -237,6 +310,14 @@ static void hls_free_segments(HLSSegment *p) > } > } > > +static void print_encryption_tag(HLSContext *hls, HLSSegment *en) > +{ > + avio_printf(hls->pb, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", > en->key_uri); > + if (*en->iv_string) > + avio_printf(hls->pb, ",IV=0x%s", en->iv_string); > + avio_printf(hls->pb, "\n"); > +} > + > static int hls_window(AVFormatContext *s, int last) > { > HLSContext *hls = s->priv_data; > @@ -245,6 +326,8 @@ static int hls_window(AVFormatContext *s, int last) > int ret = 0; > int64_t sequence = FFMAX(hls->start_sequence, hls->sequence - > hls->nb_entries); > int version = hls->flags & HLS_SINGLE_FILE ? 4 : 3; > + char *key_uri = NULL; > + char *iv_string = NULL; > > if ((ret = avio_open2(&hls->pb, s->filename, AVIO_FLAG_WRITE, > &s->interrupt_callback, NULL)) < 0) > @@ -267,6 +350,13 @@ static int hls_window(AVFormatContext *s, int last) > sequence); > > for (en = hls->segments; en; en = en->next) { > + if (hls->key_info_file && (!key_uri || strcmp(en->key_uri, key_uri) > || > + av_strcasecmp(en->iv_string, > iv_string))) { > + print_encryption_tag(hls, en); > + key_uri = en->key_uri; > + iv_string = en->iv_string; > + } > + > avio_printf(hls->pb, "#EXTINF:%f,\n", en->duration); > if (hls->flags & HLS_SINGLE_FILE) > avio_printf(hls->pb, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n", > @@ -288,6 +378,10 @@ static int hls_start(AVFormatContext *s) > { > HLSContext *c = s->priv_data; > AVFormatContext *oc = c->avf; > + AVDictionary *options = NULL; > + const char *prefix = "crypto:"; > + int filename_size; > + char *filename, iv_string[BLOCKSIZE*2 + 1]; > int err = 0; > > if (c->flags & HLS_SINGLE_FILE) > @@ -301,9 +395,38 @@ static int hls_start(AVFormatContext *s) > } > c->number++; > > - if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, > - &s->interrupt_callback, NULL)) < 0) > - return err; > + if (c->key_info_file) { > + if ((err = hls_encryption_start(s)) < 0) > + return err; > + av_log(c, AV_LOG_DEBUG, "key = 0x%s\n", c->key_string); > + if ((err = av_dict_set(&options, "encryption_key", c->key_string, 0)) > + < 0) > + return err; > + err = av_strlcpy(iv_string, c->iv_string, sizeof(iv_string)); > + if (!err) > + snprintf(iv_string, sizeof(iv_string), "%032llx", c->sequence); libavformat/hlsenc.c:407:13: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘int64_t’ [-Wformat] libavformat/hlsenc.c:407:13: warning: format ‘%llx’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘int64_t’ [-Wformat] > + av_log(c, AV_LOG_DEBUG, "IV = 0x%s\n", iv_string); > + if ((err = av_dict_set(&options, "encryption_iv", iv_string, 0)) < 0) > + return err; > + > + filename_size = strlen(prefix) + strlen(oc->filename) + 1; > + filename = av_malloc(filename_size); > + if (!filename) { > + av_dict_free(&options); > + return AVERROR(ENOMEM); > + } > + av_strlcpy(filename, prefix, filename_size); > + av_strlcat(filename, oc->filename, filename_size); this looks like it can be simplified with av_asprintf() > + err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, > + &s->interrupt_callback, &options); > + av_free(filename); > + av_dict_free(&options); > + if (err < 0) > + return err; > + } else > + if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, > + &s->interrupt_callback, NULL)) < 0) > + return err; > > if (oc->oformat->priv_class && oc->priv_data) > av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); > @@ -501,6 +624,7 @@ static const AVOption options[] = { > {"hls_allow_cache", "explicitly set whether the client MAY (1) or MUST > NOT (0) cache media segments", OFFSET(allowcache), AV_OPT_TYPE_INT, {.i64 = > -1}, INT_MIN, INT_MAX, E}, > {"hls_base_url", "url to prepend to each playlist entry", > OFFSET(baseurl), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, > {"hls_segment_filename", "filename template for segment files", > OFFSET(segment_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, > 0, E}, > + {"hls_key_info_file", "file with key URI and key file path", > OFFSET(key_info_file), AV_OPT_TYPE_STRING, {.str = NULL}, 0, > 0, E}, > {"hls_flags", "set flags affecting HLS playlist and media file > generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, > "flags"}, > {"single_file", "generate a single media file indexed with byte > ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, > "flags"}, > {"delete_segments", "delete segment files that are no longer part of the > playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, UINT_MAX, > E, "flags"}, > -- > 1.9.3 (Apple Git-50) > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > -- Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB Everything should be made as simple as possible, but not simpler. -- Albert Einstein
signature.asc
Description: Digital signature
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel