Signed-off-by: Christian Suloway <csulo...@globaleagleent.com> --- libavformat/crypto.c | 233 ++++++++++++++++++++++++++++--- libavformat/hlsenc.c | 387 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 567 insertions(+), 53 deletions(-)
diff --git a/libavformat/crypto.c b/libavformat/crypto.c index a9b6e47..dc3dc88 100644 --- a/libavformat/crypto.c +++ b/libavformat/crypto.c @@ -38,17 +38,35 @@ typedef struct { int indata, indata_used, outdata; int eof; uint8_t *key; - int keylen; + int key_len; uint8_t *iv; - int ivlen; - struct AVAES *aes; + int iv_len; + uint8_t *decrypt_key; + int decrypt_key_len; + uint8_t *decrypt_iv; + int decrypt_iv_len; + uint8_t *encrypt_key; + int encrypt_key_len; + uint8_t *encrypt_iv; + int encrypt_iv_len; + struct AVAES *aes_decrypt; + struct AVAES *aes_encrypt; + + uint8_t pad[BLOCKSIZE]; + int pad_len; + } CryptoContext; #define OFFSET(x) offsetof(CryptoContext, x) #define D AV_OPT_FLAG_DECODING_PARAM +#define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - {"key", "AES decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D }, - {"iv", "AES decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D }, + {"key", "AES encryption/decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, .flags = D|E }, + {"iv", "AES encryption/decryption initialization vector", OFFSET(iv), AV_OPT_TYPE_BINARY, .flags = D|E }, + {"decryption_key", "AES decryption key", OFFSET(decrypt_key), AV_OPT_TYPE_BINARY, .flags = D }, + {"decryption_iv", "AES decryption initialization vector", OFFSET(decrypt_iv), AV_OPT_TYPE_BINARY, .flags = D }, + {"encryption_key", "AES encryption key", OFFSET(encrypt_key), AV_OPT_TYPE_BINARY, .flags = E }, + {"encryption_iv", "AES encryption initialization vector", OFFSET(encrypt_iv), AV_OPT_TYPE_BINARY, .flags = E }, { NULL } }; @@ -72,28 +90,145 @@ static int crypto_open2(URLContext *h, const char *uri, int flags, AVDictionary goto err; } - if (c->keylen < BLOCKSIZE || c->ivlen < BLOCKSIZE) { - av_log(h, AV_LOG_ERROR, "Key or IV not set\n"); - ret = AVERROR(EINVAL); - goto err; + if (flags & AVIO_FLAG_READ) { + if (!c->decrypt_key_len) { + if (!c->key_len) { + av_log(h, AV_LOG_ERROR, "decryption key not set\n"); + ret = AVERROR(EINVAL); + goto err; + } else if (c->key_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid key size " + "(%d bytes, block size is %d)\n", + c->key_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } + c->decrypt_key = av_malloc(c->key_len); + if (!c->decrypt_key) { + ret = AVERROR(ENOMEM); + goto err; + } + memcpy(c->decrypt_key, c->key, c->key_len); + c->decrypt_key_len = c->key_len; + } else if (c->decrypt_key_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid decryption key size " + "(%d bytes, block size is %d)\n", + c->decrypt_key_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } + if (!c->decrypt_iv_len) { + if (!c->iv_len) { + av_log(h, AV_LOG_ERROR, "decryption IV not set\n"); + ret = AVERROR(EINVAL); + goto err; + } else if (c->iv_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid IV size " + "(%d bytes, block size is %d)\n", + c->iv_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } + c->decrypt_iv = av_malloc(c->iv_len); + if (!c->decrypt_iv) { + ret = AVERROR(ENOMEM); + goto err; + } + memcpy(c->decrypt_iv, c->iv, c->iv_len); + c->decrypt_iv_len = c->iv_len; + } else if (c->decrypt_iv_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid decryption IV size " + "(%d bytes, block size is %d)\n", + c->decrypt_iv_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } } + if (flags & AVIO_FLAG_WRITE) { - av_log(h, AV_LOG_ERROR, "Only decryption is supported currently\n"); - ret = AVERROR(ENOSYS); - goto err; + if (!c->encrypt_key_len) { + if (!c->key_len) { + av_log(h, AV_LOG_ERROR, "encryption key not set\n"); + ret = AVERROR(EINVAL); + goto err; + } else if (c->key_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid key size " + "(%d bytes, block size is %d)\n", + c->key_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } + c->encrypt_key = av_malloc(c->key_len); + if (!c->encrypt_key) { + ret = AVERROR(ENOMEM); + goto err; + } + memcpy(c->encrypt_key, c->key, c->key_len); + c->encrypt_key_len = c->key_len; + } else if (c->encrypt_key_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid encryption key size " + "(%d bytes, block size is %d)\n", + c->encrypt_key_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } + if (!c->encrypt_iv_len) { + if (!c->iv_len) { + av_log(h, AV_LOG_ERROR, "encryption IV not set\n"); + ret = AVERROR(EINVAL); + goto err; + } else if (c->iv_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid IV size " + "(%d bytes, block size is %d)\n", + c->iv_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } + c->encrypt_iv = av_malloc(c->iv_len); + if (!c->encrypt_iv) { + ret = AVERROR(ENOMEM); + goto err; + } + memcpy(c->encrypt_iv, c->iv, c->iv_len); + c->encrypt_iv_len = c->iv_len; + } else if (c->encrypt_iv_len != BLOCKSIZE) { + av_log(h, AV_LOG_ERROR, "invalid encryption IV size " + "(%d bytes, block size is %d)\n", + c->encrypt_iv_len, BLOCKSIZE); + ret = AVERROR(EINVAL); + goto err; + } } - if ((ret = ffurl_open(&c->hd, nested_url, AVIO_FLAG_READ, + + if ((ret = ffurl_open(&c->hd, nested_url, flags, &h->interrupt_callback, options)) < 0) { - av_log(h, AV_LOG_ERROR, "Unable to open input\n"); + av_log(h, AV_LOG_ERROR, "Unable to open resource: %s\n", nested_url); goto err; } - c->aes = av_aes_alloc(); - if (!c->aes) { - ret = AVERROR(ENOMEM); - goto err; + + if (flags & AVIO_FLAG_READ) { + c->aes_decrypt = av_aes_alloc(); + if (!c->aes_decrypt) { + ret = AVERROR(ENOMEM); + goto err; + } + ret = av_aes_init(c->aes_decrypt, c->decrypt_key, BLOCKSIZE*8, 1); + if (ret < 0) + goto err; + } + + if (flags & AVIO_FLAG_WRITE) { + c->aes_encrypt = av_aes_alloc(); + if (!c->aes_encrypt) { + ret = AVERROR(ENOMEM); + goto err; + } + ret = av_aes_init(c->aes_encrypt, c->encrypt_key, BLOCKSIZE*8, 0); + if (ret < 0) + goto err; } - av_aes_init(c->aes, c->key, 128, 1); + c->pad_len = 0; h->is_streamed = 1; @@ -131,8 +266,8 @@ retry: return AVERROR_EOF; if (!c->eof) blocks--; - av_aes_crypt(c->aes, c->outbuffer, c->inbuffer + c->indata_used, blocks, - c->iv, 1); + av_aes_crypt(c->aes_decrypt, c->outbuffer, c->inbuffer + c->indata_used, + blocks, c->decrypt_iv, 1); c->outdata = BLOCKSIZE * blocks; c->outptr = c->outbuffer; c->indata_used += BLOCKSIZE * blocks; @@ -150,12 +285,65 @@ retry: goto retry; } +static int crypto_write(URLContext *h, const unsigned char *buf, int size) +{ + CryptoContext *c = h->priv_data; + int total_size, blocks, pad_len, out_size; + uint8_t *out_buf; + int ret = 0; + + total_size = size + c->pad_len; + pad_len = total_size % BLOCKSIZE; + out_size = total_size - pad_len; + blocks = out_size / BLOCKSIZE; + + if (out_size) { + out_buf = av_malloc(out_size); + if (!out_buf) + return AVERROR(ENOMEM); + + if (c->pad_len) { + memcpy(&c->pad[c->pad_len], buf, BLOCKSIZE - c->pad_len); + av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0); + blocks--; + } + + av_aes_crypt(c->aes_encrypt, &out_buf[c->pad_len ? BLOCKSIZE : 0], + &buf[c->pad_len ? BLOCKSIZE - c->pad_len: 0], + blocks, c->encrypt_iv, 0); + + ret = ffurl_write(c->hd, out_buf, out_size); + av_free(out_buf); + if (ret < 0) + return ret; + + memcpy(c->pad, &buf[size - pad_len], pad_len); + } else + memcpy(&c->pad[c->pad_len], buf, size); + + c->pad_len = pad_len; + + return size; +} + static int crypto_close(URLContext *h) { CryptoContext *c = h->priv_data; + uint8_t out_buf[BLOCKSIZE]; + int ret, pad; + + if (c->aes_encrypt) { + pad = BLOCKSIZE - c->pad_len; + memset(&c->pad[c->pad_len], pad, pad); + av_aes_crypt(c->aes_encrypt, out_buf, c->pad, 1, c->encrypt_iv, 0); + if ((ret = ffurl_write(c->hd, out_buf, BLOCKSIZE)) < 0) + return ret; + } + if (c->hd) ffurl_close(c->hd); - av_freep(&c->aes); + av_freep(&c->aes_decrypt); + av_freep(&c->aes_encrypt); return 0; } @@ -163,6 +351,7 @@ URLProtocol ff_crypto_protocol = { .name = "crypto", .url_open2 = crypto_open2, .url_read = crypto_read, + .url_write = crypto_write, .url_close = crypto_close, .priv_data_size = sizeof(CryptoContext), .priv_data_class = &crypto_class, diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index e13f438..516eac6 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -19,8 +19,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" #include <float.h> #include <stdint.h> +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdio.h> #include "libavutil/avassert.h" #include "libavutil/mathematics.h" @@ -28,16 +33,21 @@ #include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/log.h" +#include "libavutil/lfg.h" +#include "libavutil/random_seed.h" #include "avformat.h" #include "internal.h" typedef struct HLSSegment { - char filename[1024]; + char *filename; double duration; /* in seconds */ int64_t pos; int64_t size; + char *key_uri; + char *iv_string; + struct HLSSegment *next; } HLSSegment; @@ -58,6 +68,7 @@ typedef struct HLSContext { float time; // Set by a private option. int max_nb_segments; // Set by a private option. int wrap; // Set by a private option. + int delete_segments; uint32_t flags; // enum HLSFlags int allowcache; @@ -72,15 +83,219 @@ typedef struct HLSContext { HLSSegment *segments; HLSSegment *last_segment; + HLSSegment *old_segments; + char *dirname; char *basename; char *baseurl; char *format_options_str; AVDictionary *format_options; + char *segment_filename; + + char *key_info_file; + int random_iv; + int encrypt; + + char *key_file; + char *key_uri; + char *key_string; + char *iv_string; + + AVLFG *lfg; + AVIOContext *pb; } HLSContext; +static int hex_string(char **string, uint8_t *buf, int size) +{ + int i; + + *string = av_mallocz(size*2 + 1); + if (!*string) + return AVERROR(ENOMEM); + for (i = 0; i < size; i++) + snprintf(&(*string)[i*2], 3, "%02X", buf[i]); + + return 0; +} + + +static void hls_free_segment(HLSSegment *en) +{ + av_free(en->filename); + av_free(en->key_uri); + av_free(en->iv_string); + av_free(en); +} + +static int hls_delete_old_segments(HLSContext *hls) { + + HLSSegment *segment, *previous_segment = NULL; + float playlist_duration = 0.0f; + int path_size; + char *path; + + segment = hls->segments; + while (segment) { + playlist_duration += segment->duration; + segment = segment->next; + } + + segment = hls->old_segments; + while (segment) { + playlist_duration -= segment->duration; + previous_segment = segment; + segment = segment->next; + if (playlist_duration <= 0.0f) { + previous_segment->next = NULL; + break; + } + } + + while (segment) { + av_log(hls, AV_LOG_DEBUG, "deleting old segment %s\n", + segment->filename); + path_size = strlen(hls->dirname) + strlen(segment->filename) + 1; + path = av_malloc(path_size); + if (!path) + return AVERROR(ENOMEM); + av_strlcpy(path, hls->dirname, path_size); + av_strlcat(path, segment->filename, path_size); + if (unlink(path) < 0) { + av_log(hls, AV_LOG_ERROR, "failed to delete old segment %s: %s\n", + path, strerror(errno)); + } + av_free(path); + previous_segment = segment; + segment = segment->next; + hls_free_segment(previous_segment); + } + + return 0; +} + +static int hls_encryption_init(AVFormatContext *s) +{ + HLSContext *hls = s->priv_data; + + hls->key_file = NULL; + hls->key_uri = NULL; + hls->key_string = NULL; + hls->iv_string = NULL; + + if (hls->key_info_file) { + hls->encrypt = 1; + } else { + hls->encrypt = 0; + return 0; + } + + if (hls->random_iv) { + hls->lfg = av_malloc(sizeof(AVLFG)); + av_lfg_init(hls->lfg, av_get_random_seed()); + } else + hls->lfg = NULL; + + return 0; +} + +static int hls_encryption_start(HLSContext *hls) { + + uint8_t iv[16]; + int ret, i, j, rotate_iv = 0; + unsigned int u; + AVIOContext *pb = NULL; + AVIOContext *dyn_buf = NULL; + uint8_t buf[1024], *tmp; + char *p, *tstr, *saveptr = NULL; + char key[16], *key_string; + + if ((ret = avio_open(&pb, hls->key_info_file, AVIO_FLAG_READ)) < 0) { + av_log(hls, AV_LOG_ERROR, "error opening key info file %s\n", + hls->key_info_file); + return ret; + } + ret = avio_open_dyn_buf(&dyn_buf); + if (ret < 0) { + avio_closep(&pb); + return ret; + } + + while ((ret = avio_read(pb, buf, sizeof(buf))) > 0) + avio_write(dyn_buf, buf, ret); + avio_w8(dyn_buf, 0); + avio_closep(&pb); + + if ((ret = avio_close_dyn_buf(dyn_buf, &tmp)) < 0) + return ret; + + p = tmp; + if (!(tstr = av_strtok(p, "\n", &saveptr)) || !*tstr) { + av_log(hls, AV_LOG_ERROR, "no key URL specified in key info file %s\n", + hls->key_info_file); + return AVERROR(EINVAL); + } + p = NULL; + av_free(hls->key_uri); + hls->key_uri = av_strdup(tstr); + if (!hls->key_uri) + return AVERROR(ENOMEM); + if (!(tstr = av_strtok(p, "\n", &saveptr)) || !*tstr) { + av_log(hls, AV_LOG_ERROR, "no key file specified in key info file %s\n", + hls->key_info_file); + return AVERROR(EINVAL); + } + av_free(hls->key_file); + hls->key_file = av_strdup(tstr); + if (!hls->key_file) + return AVERROR(ENOMEM); + av_free(tmp); + + if ((ret = avio_open(&pb, hls->key_file, AVIO_FLAG_READ)) < 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_closep(&pb); + if (ret < sizeof(key)) { + av_log(hls, AV_LOG_ERROR, "error reading key file %s\n", + hls->key_file); + return ret; + } + + if ((ret = hex_string(&key_string, key, 16)) < 0) + return ret; + if (!hls->key_string || strncmp(key_string, hls->key_string, 32)) { + av_free(hls->key_string); + hls->key_string = key_string; + rotate_iv = 1; + } else { + av_free(key_string); + } + + if (!hls->random_iv) { + for (i = 0; i < 8; i++) + iv[15 - i] = (hls->sequence >> i*8) & 0xff; + memset(iv, 0, 8); + if ((ret = hex_string(&hls->iv_string, iv, 16)) < 0) + return ret; + } else if (!hls->iv_string || rotate_iv) { + for (i = 0; i < 4; i++) { + u = av_lfg_get(hls->lfg); + for (j = 0; j < 4; j++) + iv[i*4 + j] = (u >> j*8) & 0xff; + } + av_free(hls->iv_string); + if ((ret = hex_string(&hls->iv_string, iv, 16)) < 0) + return ret; + } + + return 0; +} + static int hls_mux_init(AVFormatContext *s) { HLSContext *hls = s->priv_data; @@ -115,17 +330,32 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, int64_t size) { HLSSegment *en = av_malloc(sizeof(*en)); + int ret; if (!en) return AVERROR(ENOMEM); - av_strlcpy(en->filename, av_basename(hls->avf->filename), sizeof(en->filename)); + en->filename = av_strdup(hls->avf->filename); + if (!en->filename) + return AVERROR(ENOMEM); en->duration = duration; en->pos = pos; en->size = size; en->next = NULL; + if (hls->encrypt) { + en->key_uri = av_strdup(hls->key_uri); + if (!en->key_uri) + return AVERROR(ENOMEM); + en->iv_string = av_strdup(hls->iv_string); + if (!en->iv_string) + return AVERROR(ENOMEM); + } else { + en->key_uri = NULL; + en->iv_string = NULL; + } + if (!hls->segments) hls->segments = en; else @@ -136,7 +366,14 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, if (hls->max_nb_segments && hls->nb_entries >= hls->max_nb_segments) { en = hls->segments; hls->segments = en->next; - av_free(en); + + if (en && hls->delete_segments && !hls->wrap) { + en->next = hls->old_segments; + hls->old_segments = en; + if ((ret = hls_delete_old_segments(hls)) < 0) + return ret; + } else + hls_free_segment(en); } else hls->nb_entries++; @@ -145,17 +382,25 @@ static int hls_append_segment(HLSContext *hls, double duration, int64_t pos, return 0; } -static void hls_free_segments(HLSContext *hls) +static void hls_free_segments(HLSSegment *p) { - HLSSegment *p = hls->segments, *en; + HLSSegment *en; while(p) { en = p; p = p->next; - av_free(en); + hls_free_segment(en); } } +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 (hls->random_iv) + 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; @@ -164,6 +409,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) @@ -186,6 +433,16 @@ static int hls_window(AVFormatContext *s, int last) sequence); for (en = hls->segments; en; en = en->next) { + if (hls->encrypt) { + if (!key_uri || av_strcasecmp(en->key_uri, key_uri) || + hls->random_iv && + (!iv_string || 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", @@ -208,6 +465,10 @@ static int hls_start(AVFormatContext *s) HLSContext *c = s->priv_data; AVFormatContext *oc = c->avf; int err = 0; + AVDictionary *options = NULL; + const char *prefix = "crypto:"; + int filename_size; + char *filename; if (c->flags & HLS_SINGLE_FILE) av_strlcpy(oc->filename, c->basename, @@ -220,8 +481,33 @@ static int hls_start(AVFormatContext *s) } c->number++; - if ((err = avio_open2(&oc->pb, oc->filename, AVIO_FLAG_WRITE, - &s->interrupt_callback, NULL)) < 0) + if (c->encrypt) { + if ((err = hls_encryption_start(c)) < 0) + return err; + av_dict_set(&options, "encryption_key", c->key_string, 0); + av_dict_set(&options, "encryption_iv", c->iv_string, 0); + + filename_size = strlen(prefix) + strlen(c->dirname) \ + + strlen(oc->filename) + 1; + filename = av_malloc(filename_size); + if (!filename) + return AVERROR(ENOMEM); + av_strlcpy(filename, prefix, filename_size); + } else { + filename_size = strlen(c->dirname) + strlen(oc->filename) + 1; + filename = av_malloc(filename_size); + if (!filename) + return AVERROR(ENOMEM); + *filename = '\0'; + } + av_strlcat(filename, c->dirname, filename_size); + av_strlcat(filename, oc->filename, filename_size); + + 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; if (oc->oformat->priv_class && oc->priv_data) @@ -237,15 +523,13 @@ static int hls_write_header(AVFormatContext *s) char *p; const char *pattern = "%d.ts"; AVDictionary *options = NULL; - int basename_size = strlen(s->filename) + strlen(pattern) + 1; + int basename_size; + char *basename; hls->sequence = hls->start_sequence; hls->recording_time = hls->time * AV_TIME_BASE; hls->start_pts = AV_NOPTS_VALUE; - if (hls->flags & HLS_SINGLE_FILE) - pattern = ".ts"; - if (hls->format_options_str) { ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); if (ret < 0) { @@ -270,21 +554,48 @@ static int hls_write_header(AVFormatContext *s) goto fail; } - hls->basename = av_malloc(basename_size); - - if (!hls->basename) { + hls->dirname = av_strdup(s->filename); + if (!hls->dirname) { ret = AVERROR(ENOMEM); goto fail; } - strcpy(hls->basename, s->filename); + basename = (char *)av_basename(hls->dirname); - p = strrchr(hls->basename, '.'); + if (hls->segment_filename) { + hls->basename = av_strdup(av_basename(hls->segment_filename)); + if (!hls->basename) { + ret = AVERROR(ENOMEM); + goto fail; + } + if (strlen(hls->basename) != strlen(hls->segment_filename)) { + av_log(hls, AV_LOG_ERROR, "invalid segment filename %s\n", + hls->segment_filename); + ret = AVERROR(EINVAL); + goto fail; + } + } else { + if (hls->flags & HLS_SINGLE_FILE) + pattern = ".ts"; - if (p) - *p = '\0'; + basename_size = strlen(basename) + strlen(pattern) + 1; + hls->basename = av_malloc(basename_size); + if (!hls->basename) { + ret = AVERROR(ENOMEM); + goto fail; + } + + av_strlcpy(hls->basename, basename, basename_size); + p = strrchr(hls->basename, '.'); + if (p) + *p = '\0'; + av_strlcat(hls->basename, pattern, basename_size); + } - av_strlcat(hls->basename, pattern, basename_size); + *basename = '\0'; + + if ((ret = hls_encryption_init(s) < 0)) + goto fail; if ((ret = hls_mux_init(s)) < 0) goto fail; @@ -309,6 +620,7 @@ fail: av_dict_free(&options); if (ret) { + av_free(hls->dirname); av_free(hls->basename); if (hls->avf) avformat_free_context(hls->avf); @@ -395,24 +707,37 @@ static int hls_write_trailer(struct AVFormatContext *s) hls->avf = NULL; hls_window(s, 1); - hls_free_segments(hls); + av_free(hls->dirname); + av_free(hls->key_file); + av_free(hls->key_uri); + av_free(hls->key_string); + av_free(hls->iv_string); + av_free(hls->lfg); + + hls_free_segments(hls->segments); + hls_free_segments(hls->old_segments); + avio_close(hls->pb); + return 0; } #define OFFSET(x) offsetof(HLSContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - {"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, - {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, - {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, - {"hls_ts_options","set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, - {"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E}, - {"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_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"}, - + {"start_number", "set first number in the sequence", OFFSET(start_sequence), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, + {"hls_time", "set segment length in seconds", OFFSET(time), AV_OPT_TYPE_FLOAT, {.dbl = 2}, 0, FLT_MAX, E}, + {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, + {"hls_ts_options", "set hls mpegts list of options for the container format used for hls", OFFSET(format_options_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, + {"hls_wrap", "set number after which the index wraps", OFFSET(wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E}, + {"hls_delete", "delete segment files that are no longer part of the playlist", OFFSET(delete_segments), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E}, + {"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_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"}, + {"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 URL and key file path", OFFSET(key_info_file), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, + {"hls_random_iv", "randomize initialization vector", OFFSET(random_iv), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, E}, { NULL }, }; -- 1.9.3 (Apple Git-50) _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel