aboutsummaryrefslogtreecommitdiff
path: root/src/http/modules/ngx_http_mp4_module.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/http/modules/ngx_http_mp4_module.c')
-rw-r--r--src/http/modules/ngx_http_mp4_module.c261
1 files changed, 226 insertions, 35 deletions
diff --git a/src/http/modules/ngx_http_mp4_module.c b/src/http/modules/ngx_http_mp4_module.c
index 0e93fbd09..9c3f627fe 100644
--- a/src/http/modules/ngx_http_mp4_module.c
+++ b/src/http/modules/ngx_http_mp4_module.c
@@ -11,31 +11,33 @@
#define NGX_HTTP_MP4_TRAK_ATOM 0
#define NGX_HTTP_MP4_TKHD_ATOM 1
-#define NGX_HTTP_MP4_MDIA_ATOM 2
-#define NGX_HTTP_MP4_MDHD_ATOM 3
-#define NGX_HTTP_MP4_HDLR_ATOM 4
-#define NGX_HTTP_MP4_MINF_ATOM 5
-#define NGX_HTTP_MP4_VMHD_ATOM 6
-#define NGX_HTTP_MP4_SMHD_ATOM 7
-#define NGX_HTTP_MP4_DINF_ATOM 8
-#define NGX_HTTP_MP4_STBL_ATOM 9
-#define NGX_HTTP_MP4_STSD_ATOM 10
-#define NGX_HTTP_MP4_STTS_ATOM 11
-#define NGX_HTTP_MP4_STTS_DATA 12
-#define NGX_HTTP_MP4_STSS_ATOM 13
-#define NGX_HTTP_MP4_STSS_DATA 14
-#define NGX_HTTP_MP4_CTTS_ATOM 15
-#define NGX_HTTP_MP4_CTTS_DATA 16
-#define NGX_HTTP_MP4_STSC_ATOM 17
-#define NGX_HTTP_MP4_STSC_START 18
-#define NGX_HTTP_MP4_STSC_DATA 19
-#define NGX_HTTP_MP4_STSC_END 20
-#define NGX_HTTP_MP4_STSZ_ATOM 21
-#define NGX_HTTP_MP4_STSZ_DATA 22
-#define NGX_HTTP_MP4_STCO_ATOM 23
-#define NGX_HTTP_MP4_STCO_DATA 24
-#define NGX_HTTP_MP4_CO64_ATOM 25
-#define NGX_HTTP_MP4_CO64_DATA 26
+#define NGX_HTTP_MP4_EDTS_ATOM 2
+#define NGX_HTTP_MP4_ELST_ATOM 3
+#define NGX_HTTP_MP4_MDIA_ATOM 4
+#define NGX_HTTP_MP4_MDHD_ATOM 5
+#define NGX_HTTP_MP4_HDLR_ATOM 6
+#define NGX_HTTP_MP4_MINF_ATOM 7
+#define NGX_HTTP_MP4_VMHD_ATOM 8
+#define NGX_HTTP_MP4_SMHD_ATOM 9
+#define NGX_HTTP_MP4_DINF_ATOM 10
+#define NGX_HTTP_MP4_STBL_ATOM 11
+#define NGX_HTTP_MP4_STSD_ATOM 12
+#define NGX_HTTP_MP4_STTS_ATOM 13
+#define NGX_HTTP_MP4_STTS_DATA 14
+#define NGX_HTTP_MP4_STSS_ATOM 15
+#define NGX_HTTP_MP4_STSS_DATA 16
+#define NGX_HTTP_MP4_CTTS_ATOM 17
+#define NGX_HTTP_MP4_CTTS_DATA 18
+#define NGX_HTTP_MP4_STSC_ATOM 19
+#define NGX_HTTP_MP4_STSC_START 20
+#define NGX_HTTP_MP4_STSC_DATA 21
+#define NGX_HTTP_MP4_STSC_END 22
+#define NGX_HTTP_MP4_STSZ_ATOM 23
+#define NGX_HTTP_MP4_STSZ_DATA 24
+#define NGX_HTTP_MP4_STCO_ATOM 25
+#define NGX_HTTP_MP4_STCO_DATA 26
+#define NGX_HTTP_MP4_CO64_ATOM 27
+#define NGX_HTTP_MP4_CO64_DATA 28
#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
@@ -43,6 +45,7 @@
typedef struct {
size_t buffer_size;
size_t max_buffer_size;
+ ngx_flag_t start_key_frame;
} ngx_http_mp4_conf_t;
@@ -54,6 +57,25 @@ typedef struct {
typedef struct {
+ u_char size[4];
+ u_char name[4];
+} ngx_mp4_edts_atom_t;
+
+
+typedef struct {
+ u_char size[4];
+ u_char name[4];
+ u_char version[1];
+ u_char flags[3];
+ u_char entries[4];
+ u_char duration[8];
+ u_char media_time[8];
+ u_char media_rate[2];
+ u_char reserved[2];
+} ngx_mp4_elst_atom_t;
+
+
+typedef struct {
uint32_t timescale;
uint32_t time_to_sample_entries;
uint32_t sample_to_chunk_entries;
@@ -70,6 +92,9 @@ typedef struct {
ngx_uint_t end_chunk_samples;
uint64_t start_chunk_samples_size;
uint64_t end_chunk_samples_size;
+ uint64_t duration;
+ uint64_t prefix;
+ uint64_t movie_duration;
off_t start_offset;
off_t end_offset;
@@ -85,6 +110,8 @@ typedef struct {
ngx_buf_t trak_atom_buf;
ngx_buf_t tkhd_atom_buf;
+ ngx_buf_t edts_atom_buf;
+ ngx_buf_t elst_atom_buf;
ngx_buf_t mdia_atom_buf;
ngx_buf_t mdhd_atom_buf;
ngx_buf_t hdlr_atom_buf;
@@ -111,6 +138,8 @@ typedef struct {
ngx_buf_t co64_atom_buf;
ngx_buf_t co64_data_buf;
+ ngx_mp4_edts_atom_t edts_atom;
+ ngx_mp4_elst_atom_t elst_atom;
ngx_mp4_stsc_entry_t stsc_start_chunk_entry;
ngx_mp4_stsc_entry_t stsc_end_chunk_entry;
} ngx_http_mp4_trak_t;
@@ -186,6 +215,14 @@ typedef struct {
((u_char *) (p))[6] = n3; \
((u_char *) (p))[7] = n4
+#define ngx_mp4_get_16value(p) \
+ ( ((uint16_t) ((u_char *) (p))[0] << 8) \
+ + ( ((u_char *) (p))[1]) )
+
+#define ngx_mp4_set_16value(p, n) \
+ ((u_char *) (p))[0] = (u_char) ((n) >> 8); \
+ ((u_char *) (p))[1] = (u_char) (n)
+
#define ngx_mp4_get_32value(p) \
( ((uint32_t) ((u_char *) (p))[0] << 24) \
+ ( ((u_char *) (p))[1] << 16) \
@@ -253,6 +290,8 @@ static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
+static void ngx_http_mp4_update_mdhd_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4,
@@ -267,6 +306,8 @@ static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
+static void ngx_http_mp4_update_edts_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak);
static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4,
@@ -277,6 +318,8 @@ static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak);
static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start);
+static uint32_t ngx_http_mp4_seek_key_frame(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak, uint32_t start_sample);
static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,
uint64_t atom_data_size);
static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
@@ -340,6 +383,13 @@ static ngx_command_t ngx_http_mp4_commands[] = {
offsetof(ngx_http_mp4_conf_t, max_buffer_size),
NULL },
+ { ngx_string("mp4_start_key_frame"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_mp4_conf_t, start_key_frame),
+ NULL },
+
ngx_null_command
};
@@ -822,10 +872,11 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
ngx_http_mp4_update_minf_atom(mp4, &trak[i]);
- trak[i].size += trak[i].mdhd_size;
+ ngx_http_mp4_update_mdhd_atom(mp4, &trak[i]);
trak[i].size += trak[i].hdlr_size;
ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);
trak[i].size += trak[i].tkhd_size;
+ ngx_http_mp4_update_edts_atom(mp4, &trak[i]);
ngx_http_mp4_update_trak_atom(mp4, &trak[i]);
mp4->moov_size += trak[i].size;
@@ -1587,6 +1638,7 @@ ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
trak = ngx_mp4_last_trak(mp4);
trak->tkhd_size = atom_size;
+ trak->movie_duration = duration;
ngx_mp4_set_32value(tkhd_atom->size, atom_size);
@@ -1749,16 +1801,10 @@ ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
trak = ngx_mp4_last_trak(mp4);
trak->mdhd_size = atom_size;
trak->timescale = timescale;
+ trak->duration = duration;
ngx_mp4_set_32value(mdhd_atom->size, atom_size);
- if (mdhd_atom->version[0] == 0) {
- ngx_mp4_set_32value(mdhd_atom->duration, duration);
-
- } else {
- ngx_mp4_set_64value(mdhd64_atom->duration, duration);
- }
-
atom = &trak->mdhd_atom_buf;
atom->temporary = 1;
atom->pos = atom_header;
@@ -1772,6 +1818,33 @@ ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
}
+static void
+ngx_http_mp4_update_mdhd_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ ngx_buf_t *atom;
+ ngx_mp4_mdhd_atom_t *mdhd_atom;
+ ngx_mp4_mdhd64_atom_t *mdhd64_atom;
+
+ atom = trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf;
+ if (atom == NULL) {
+ return;
+ }
+
+ mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom->pos;
+ mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom->pos;
+
+ if (mdhd_atom->version[0] == 0) {
+ ngx_mp4_set_32value(mdhd_atom->duration, trak->duration);
+
+ } else {
+ ngx_mp4_set_64value(mdhd64_atom->duration, trak->duration);
+ }
+
+ trak->size += trak->mdhd_size;
+}
+
+
static ngx_int_t
ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
{
@@ -1962,6 +2035,59 @@ ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
static void
+ngx_http_mp4_update_edts_atom(ngx_http_mp4_file_t *mp4,
+ ngx_http_mp4_trak_t *trak)
+{
+ ngx_buf_t *atom;
+ ngx_mp4_elst_atom_t *elst_atom;
+ ngx_mp4_edts_atom_t *edts_atom;
+
+ if (trak->prefix == 0) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 edts atom update prefix:%uL", trak->prefix);
+
+ edts_atom = &trak->edts_atom;
+ ngx_mp4_set_32value(edts_atom->size, sizeof(ngx_mp4_edts_atom_t)
+ + sizeof(ngx_mp4_elst_atom_t));
+ ngx_mp4_set_atom_name(edts_atom, 'e', 'd', 't', 's');
+
+ atom = &trak->edts_atom_buf;
+ atom->temporary = 1;
+ atom->pos = (u_char *) edts_atom;
+ atom->last = (u_char *) edts_atom + sizeof(ngx_mp4_edts_atom_t);
+
+ trak->out[NGX_HTTP_MP4_EDTS_ATOM].buf = atom;
+
+ elst_atom = &trak->elst_atom;
+ ngx_mp4_set_32value(elst_atom->size, sizeof(ngx_mp4_elst_atom_t));
+ ngx_mp4_set_atom_name(elst_atom, 'e', 'l', 's', 't');
+
+ elst_atom->version[0] = 1;
+ elst_atom->flags[0] = 0;
+ elst_atom->flags[1] = 0;
+ elst_atom->flags[2] = 0;
+
+ ngx_mp4_set_32value(elst_atom->entries, 1);
+ ngx_mp4_set_64value(elst_atom->duration, trak->movie_duration);
+ ngx_mp4_set_64value(elst_atom->media_time, trak->prefix);
+ ngx_mp4_set_16value(elst_atom->media_rate, 1);
+ ngx_mp4_set_16value(elst_atom->reserved, 0);
+
+ atom = &trak->elst_atom_buf;
+ atom->temporary = 1;
+ atom->pos = (u_char *) elst_atom;
+ atom->last = (u_char *) elst_atom + sizeof(ngx_mp4_elst_atom_t);
+
+ trak->out[NGX_HTTP_MP4_ELST_ATOM].buf = atom;
+
+ trak->size += sizeof(ngx_mp4_edts_atom_t) + sizeof(ngx_mp4_elst_atom_t);
+}
+
+
+static void
ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak)
{
@@ -2159,7 +2285,7 @@ static ngx_int_t
ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
ngx_http_mp4_trak_t *trak, ngx_uint_t start)
{
- uint32_t count, duration, rest;
+ uint32_t count, duration, rest, key_prefix;
uint64_t start_time;
ngx_buf_t *data;
ngx_uint_t start_sample, entries, start_sec;
@@ -2183,7 +2309,7 @@ ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
- start_time = (uint64_t) start_sec * trak->timescale / 1000;
+ start_time = (uint64_t) start_sec * trak->timescale / 1000 + trak->prefix;
entries = trak->time_to_sample_entries;
start_sample = 0;
@@ -2229,6 +2355,26 @@ ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
found:
if (start) {
+ key_prefix = ngx_http_mp4_seek_key_frame(mp4, trak, start_sample);
+
+ start_sample -= key_prefix;
+
+ while (rest < key_prefix) {
+ trak->prefix += rest * duration;
+ key_prefix -= rest;
+
+ entry--;
+ entries++;
+
+ count = ngx_mp4_get_32value(entry->count);
+ duration = ngx_mp4_get_32value(entry->duration);
+ rest = count;
+ }
+
+ trak->prefix += key_prefix * duration;
+ trak->duration += trak->prefix;
+ rest -= key_prefix;
+
ngx_mp4_set_32value(entry->count, count - rest);
data->pos = (u_char *) entry;
trak->time_to_sample_entries = entries;
@@ -2253,6 +2399,49 @@ found:
}
+static uint32_t
+ngx_http_mp4_seek_key_frame(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak,
+ uint32_t start_sample)
+{
+ uint32_t key_prefix, sample, *entry, *end;
+ ngx_buf_t *data;
+ ngx_http_mp4_conf_t *conf;
+
+ conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
+ if (!conf->start_key_frame) {
+ return 0;
+ }
+
+ data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
+ if (data == NULL) {
+ return 0;
+ }
+
+ entry = (uint32_t *) data->pos;
+ end = (uint32_t *) data->last;
+
+ /* sync samples starts from 1 */
+ start_sample++;
+
+ key_prefix = 0;
+
+ while (entry < end) {
+ sample = ngx_mp4_get_32value(entry);
+ if (sample > start_sample) {
+ break;
+ }
+
+ key_prefix = start_sample - sample;
+ entry++;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
+ "mp4 key frame prefix:%uD", key_prefix);
+
+ return key_prefix;
+}
+
+
typedef struct {
u_char size[4];
u_char name[4];
@@ -3590,6 +3779,7 @@ ngx_http_mp4_create_conf(ngx_conf_t *cf)
conf->buffer_size = NGX_CONF_UNSET_SIZE;
conf->max_buffer_size = NGX_CONF_UNSET_SIZE;
+ conf->start_key_frame = NGX_CONF_UNSET;
return conf;
}
@@ -3604,6 +3794,7 @@ ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024);
ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size,
10 * 1024 * 1024);
+ ngx_conf_merge_value(conf->start_key_frame, prev->start_key_frame, 0);
return NGX_CONF_OK;
}