nginxtra 1.4.7.9 → 1.6.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/bin/nginxtra +1 -1
  3. data/bin/nginxtra_rails +1 -1
  4. data/lib/nginxtra/version.rb +1 -1
  5. data/vendor/nginx/CHANGES +286 -19
  6. data/vendor/nginx/CHANGES.ru +296 -22
  7. data/vendor/nginx/auto/cc/clang +4 -3
  8. data/vendor/nginx/auto/cc/conf +23 -0
  9. data/vendor/nginx/auto/cc/msvc +1 -0
  10. data/vendor/nginx/auto/cc/name +1 -1
  11. data/vendor/nginx/auto/cc/owc +4 -4
  12. data/vendor/nginx/auto/cc/sunc +1 -1
  13. data/vendor/nginx/auto/endianness +2 -2
  14. data/vendor/nginx/auto/feature +1 -1
  15. data/vendor/nginx/auto/include +1 -1
  16. data/vendor/nginx/auto/lib/libatomic/make +3 -1
  17. data/vendor/nginx/auto/lib/openssl/conf +4 -0
  18. data/vendor/nginx/auto/lib/openssl/make +1 -1
  19. data/vendor/nginx/auto/lib/pcre/conf +5 -0
  20. data/vendor/nginx/auto/lib/pcre/make +11 -11
  21. data/vendor/nginx/auto/lib/pcre/makefile.bcc +4 -3
  22. data/vendor/nginx/auto/lib/pcre/makefile.msvc +2 -1
  23. data/vendor/nginx/auto/lib/pcre/makefile.owc +2 -1
  24. data/vendor/nginx/auto/lib/perl/make +1 -0
  25. data/vendor/nginx/auto/lib/test +1 -1
  26. data/vendor/nginx/auto/lib/zlib/make +22 -1
  27. data/vendor/nginx/auto/modules +8 -0
  28. data/vendor/nginx/auto/options +3 -0
  29. data/vendor/nginx/auto/os/darwin +1 -1
  30. data/vendor/nginx/auto/os/linux +32 -0
  31. data/vendor/nginx/auto/os/win32 +12 -1
  32. data/vendor/nginx/auto/sources +8 -2
  33. data/vendor/nginx/auto/types/sizeof +1 -1
  34. data/vendor/nginx/auto/types/typedef +1 -1
  35. data/vendor/nginx/auto/types/uintptr_t +1 -1
  36. data/vendor/nginx/auto/unix +13 -1
  37. data/vendor/nginx/conf/mime.types +11 -2
  38. data/vendor/nginx/conf/nginx.conf +3 -4
  39. data/vendor/nginx/contrib/README +6 -0
  40. data/vendor/nginx/contrib/vim/ftdetect/nginx.vim +4 -0
  41. data/vendor/nginx/contrib/vim/indent/nginx.vim +11 -0
  42. data/vendor/nginx/contrib/vim/syntax/nginx.vim +703 -0
  43. data/vendor/nginx/src/core/nginx.c +2 -7
  44. data/vendor/nginx/src/core/nginx.h +2 -2
  45. data/vendor/nginx/src/core/ngx_conf_file.c +8 -88
  46. data/vendor/nginx/src/core/ngx_conf_file.h +3 -3
  47. data/vendor/nginx/src/core/ngx_config.h +2 -2
  48. data/vendor/nginx/src/core/ngx_connection.c +100 -29
  49. data/vendor/nginx/src/core/ngx_connection.h +11 -0
  50. data/vendor/nginx/src/core/ngx_core.h +1 -0
  51. data/vendor/nginx/src/core/ngx_cycle.c +23 -99
  52. data/vendor/nginx/src/core/ngx_cycle.h +2 -0
  53. data/vendor/nginx/src/core/ngx_file.c +100 -8
  54. data/vendor/nginx/src/core/ngx_file.h +3 -0
  55. data/vendor/nginx/src/core/ngx_hash.c +6 -9
  56. data/vendor/nginx/src/core/ngx_inet.c +93 -5
  57. data/vendor/nginx/src/core/ngx_inet.h +4 -2
  58. data/vendor/nginx/src/core/ngx_list.c +1 -9
  59. data/vendor/nginx/src/core/ngx_log.c +132 -30
  60. data/vendor/nginx/src/core/ngx_log.h +5 -2
  61. data/vendor/nginx/src/core/ngx_open_file_cache.c +67 -1
  62. data/vendor/nginx/src/core/ngx_palloc.c +5 -2
  63. data/vendor/nginx/src/core/ngx_proxy_protocol.c +91 -0
  64. data/vendor/nginx/src/core/ngx_proxy_protocol.h +23 -0
  65. data/vendor/nginx/src/core/ngx_resolver.c +1080 -285
  66. data/vendor/nginx/src/core/ngx_resolver.h +33 -3
  67. data/vendor/nginx/src/core/ngx_slab.c +7 -2
  68. data/vendor/nginx/src/core/ngx_slab.h +2 -0
  69. data/vendor/nginx/src/core/ngx_string.c +78 -13
  70. data/vendor/nginx/src/core/ngx_string.h +2 -0
  71. data/vendor/nginx/src/event/modules/ngx_devpoll_module.c +2 -2
  72. data/vendor/nginx/src/event/modules/ngx_epoll_module.c +13 -5
  73. data/vendor/nginx/src/event/modules/ngx_select_module.c +1 -1
  74. data/vendor/nginx/src/event/modules/ngx_win32_select_module.c +2 -2
  75. data/vendor/nginx/src/event/ngx_event.c +0 -1
  76. data/vendor/nginx/src/event/ngx_event.h +7 -6
  77. data/vendor/nginx/src/event/ngx_event_accept.c +6 -4
  78. data/vendor/nginx/src/event/ngx_event_connect.c +2 -2
  79. data/vendor/nginx/src/event/ngx_event_openssl.c +304 -13
  80. data/vendor/nginx/src/event/ngx_event_openssl.h +20 -1
  81. data/vendor/nginx/src/event/ngx_event_openssl_stapling.c +35 -23
  82. data/vendor/nginx/src/event/ngx_event_pipe.c +15 -30
  83. data/vendor/nginx/src/http/modules/ngx_http_access_module.c +115 -35
  84. data/vendor/nginx/src/http/modules/ngx_http_auth_basic_module.c +1 -1
  85. data/vendor/nginx/src/http/modules/ngx_http_auth_request_module.c +444 -0
  86. data/vendor/nginx/src/http/modules/ngx_http_autoindex_module.c +2 -1
  87. data/vendor/nginx/src/http/modules/ngx_http_charset_filter_module.c +1 -1
  88. data/vendor/nginx/src/http/modules/ngx_http_dav_module.c +1 -3
  89. data/vendor/nginx/src/http/modules/ngx_http_fastcgi_module.c +251 -36
  90. data/vendor/nginx/src/http/modules/ngx_http_gunzip_filter_module.c +9 -5
  91. data/vendor/nginx/src/http/modules/ngx_http_gzip_filter_module.c +5 -3
  92. data/vendor/nginx/src/http/modules/ngx_http_gzip_static_module.c +1 -1
  93. data/vendor/nginx/src/http/modules/ngx_http_headers_filter_module.c +4 -0
  94. data/vendor/nginx/src/http/modules/ngx_http_image_filter_module.c +8 -2
  95. data/vendor/nginx/src/http/modules/ngx_http_limit_req_module.c +5 -1
  96. data/vendor/nginx/src/http/modules/ngx_http_map_module.c +3 -3
  97. data/vendor/nginx/src/http/modules/ngx_http_memcached_module.c +21 -10
  98. data/vendor/nginx/src/http/modules/ngx_http_mp4_module.c +669 -197
  99. data/vendor/nginx/src/http/modules/ngx_http_proxy_module.c +93 -60
  100. data/vendor/nginx/src/http/modules/ngx_http_range_filter_module.c +13 -6
  101. data/vendor/nginx/src/http/modules/ngx_http_realip_module.c +20 -1
  102. data/vendor/nginx/src/http/modules/ngx_http_referer_module.c +132 -74
  103. data/vendor/nginx/src/http/modules/ngx_http_scgi_module.c +18 -12
  104. data/vendor/nginx/src/http/modules/ngx_http_ssi_filter_module.c +22 -20
  105. data/vendor/nginx/src/http/modules/ngx_http_ssl_module.c +121 -3
  106. data/vendor/nginx/src/http/modules/ngx_http_ssl_module.h +5 -0
  107. data/vendor/nginx/src/http/modules/ngx_http_stub_status_module.c +3 -0
  108. data/vendor/nginx/src/http/modules/ngx_http_sub_filter_module.c +123 -91
  109. data/vendor/nginx/src/http/modules/ngx_http_upstream_ip_hash_module.c +29 -19
  110. data/vendor/nginx/src/http/modules/ngx_http_upstream_keepalive_module.c +2 -5
  111. data/vendor/nginx/src/http/modules/ngx_http_uwsgi_module.c +215 -19
  112. data/vendor/nginx/src/http/modules/ngx_http_xslt_filter_module.c +32 -6
  113. data/vendor/nginx/src/http/modules/perl/nginx.xs +4 -7
  114. data/vendor/nginx/src/http/modules/perl/ngx_http_perl_module.c +2 -2
  115. data/vendor/nginx/src/http/ngx_http.c +17 -7
  116. data/vendor/nginx/src/http/ngx_http_cache.h +4 -2
  117. data/vendor/nginx/src/http/ngx_http_copy_filter_module.c +4 -2
  118. data/vendor/nginx/src/http/ngx_http_core_module.c +63 -50
  119. data/vendor/nginx/src/http/ngx_http_core_module.h +5 -0
  120. data/vendor/nginx/src/http/ngx_http_file_cache.c +115 -3
  121. data/vendor/nginx/src/http/ngx_http_header_filter_module.c +9 -6
  122. data/vendor/nginx/src/http/ngx_http_parse.c +88 -10
  123. data/vendor/nginx/src/http/ngx_http_postpone_filter_module.c +2 -4
  124. data/vendor/nginx/src/http/ngx_http_request.c +116 -8
  125. data/vendor/nginx/src/http/ngx_http_request.h +5 -1
  126. data/vendor/nginx/src/http/ngx_http_request_body.c +7 -7
  127. data/vendor/nginx/src/http/ngx_http_script.c +6 -5
  128. data/vendor/nginx/src/http/ngx_http_spdy.c +889 -271
  129. data/vendor/nginx/src/http/ngx_http_spdy.h +51 -28
  130. data/vendor/nginx/src/http/ngx_http_spdy_filter_module.c +382 -167
  131. data/vendor/nginx/src/http/ngx_http_spdy_module.c +65 -8
  132. data/vendor/nginx/src/http/ngx_http_spdy_module.h +5 -0
  133. data/vendor/nginx/src/http/ngx_http_special_response.c +1 -1
  134. data/vendor/nginx/src/http/ngx_http_upstream.c +290 -114
  135. data/vendor/nginx/src/http/ngx_http_upstream.h +9 -5
  136. data/vendor/nginx/src/http/ngx_http_upstream_round_robin.c +32 -24
  137. data/vendor/nginx/src/http/ngx_http_variables.c +40 -6
  138. data/vendor/nginx/src/http/ngx_http_write_filter_module.c +12 -5
  139. data/vendor/nginx/src/mail/ngx_mail.c +4 -2
  140. data/vendor/nginx/src/mail/ngx_mail.h +2 -0
  141. data/vendor/nginx/src/mail/ngx_mail_auth_http_module.c +0 -1
  142. data/vendor/nginx/src/mail/ngx_mail_core_module.c +2 -1
  143. data/vendor/nginx/src/mail/ngx_mail_handler.c +17 -4
  144. data/vendor/nginx/src/mail/ngx_mail_parse.c +32 -2
  145. data/vendor/nginx/src/mail/ngx_mail_proxy_module.c +54 -7
  146. data/vendor/nginx/src/mail/ngx_mail_smtp_handler.c +50 -78
  147. data/vendor/nginx/src/mail/ngx_mail_ssl_module.c +48 -11
  148. data/vendor/nginx/src/mail/ngx_mail_ssl_module.h +3 -0
  149. data/vendor/nginx/src/os/unix/ngx_channel.c +3 -1
  150. data/vendor/nginx/src/os/unix/ngx_darwin_config.h +1 -0
  151. data/vendor/nginx/src/os/unix/ngx_darwin_init.c +1 -1
  152. data/vendor/nginx/src/os/unix/ngx_darwin_sendfile_chain.c +14 -16
  153. data/vendor/nginx/src/os/unix/ngx_errno.h +3 -0
  154. data/vendor/nginx/src/os/unix/ngx_files.h +10 -16
  155. data/vendor/nginx/src/os/unix/ngx_freebsd_config.h +6 -0
  156. data/vendor/nginx/src/os/unix/ngx_freebsd_init.c +1 -1
  157. data/vendor/nginx/src/os/unix/ngx_freebsd_rfork_thread.c +1 -1
  158. data/vendor/nginx/src/os/unix/ngx_freebsd_rfork_thread.h +2 -2
  159. data/vendor/nginx/src/os/unix/ngx_freebsd_sendfile_chain.c +17 -19
  160. data/vendor/nginx/src/os/unix/ngx_linux_config.h +8 -2
  161. data/vendor/nginx/src/os/unix/ngx_linux_sendfile_chain.c +20 -22
  162. data/vendor/nginx/src/os/unix/ngx_posix_config.h +1 -0
  163. data/vendor/nginx/src/os/unix/ngx_process.c +5 -0
  164. data/vendor/nginx/src/os/unix/ngx_process_cycle.c +15 -3
  165. data/vendor/nginx/src/os/unix/ngx_readv_chain.c +2 -1
  166. data/vendor/nginx/src/os/unix/ngx_recv.c +4 -1
  167. data/vendor/nginx/src/os/unix/ngx_solaris_config.h +1 -0
  168. data/vendor/nginx/src/os/unix/ngx_solaris_sendfilev_chain.c +14 -16
  169. metadata +8 -2
@@ -368,9 +368,11 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
368
368
  if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
369
369
  goto failed;
370
370
  }
371
+
372
+ r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
371
373
  }
372
374
 
373
- if (ctx->nomem) {
375
+ if (ctx->nomem || in == NULL) {
374
376
 
375
377
  /* flush busy buffers */
376
378
 
@@ -620,8 +622,6 @@ ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
620
622
  return NGX_ERROR;
621
623
  }
622
624
 
623
- r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
624
-
625
625
  ctx->last_out = &ctx->out;
626
626
  ctx->crc32 = crc32(0L, Z_NULL, 0);
627
627
  ctx->flush = Z_NO_FLUSH;
@@ -854,6 +854,8 @@ ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
854
854
  *ctx->last_out = cl;
855
855
  ctx->last_out = &cl->next;
856
856
 
857
+ r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
858
+
857
859
  return NGX_OK;
858
860
  }
859
861
 
@@ -38,7 +38,7 @@ static ngx_conf_enum_t ngx_http_gzip_static[] = {
38
38
  static ngx_command_t ngx_http_gzip_static_commands[] = {
39
39
 
40
40
  { ngx_string("gzip_static"),
41
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
41
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
42
42
  ngx_conf_set_enum_slot,
43
43
  NGX_HTTP_LOC_CONF_OFFSET,
44
44
  offsetof(ngx_http_gzip_static_conf_t, enable),
@@ -339,6 +339,10 @@ ngx_http_add_cache_control(ngx_http_request_t *r, ngx_http_header_val_t *hv,
339
339
  {
340
340
  ngx_table_elt_t *cc, **ccp;
341
341
 
342
+ if (value->len == 0) {
343
+ return NGX_OK;
344
+ }
345
+
342
346
  ccp = r->headers_out.cache_control.elts;
343
347
 
344
348
  if (ccp == NULL) {
@@ -478,7 +478,12 @@ ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)
478
478
  "image buf: %uz", size);
479
479
 
480
480
  rest = ctx->image + ctx->length - p;
481
- size = (rest < size) ? rest : size;
481
+
482
+ if (size > rest) {
483
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
484
+ "image filter: too big response");
485
+ return NGX_ERROR;
486
+ }
482
487
 
483
488
  p = ngx_cpymem(p, b->pos, size);
484
489
  b->pos += size;
@@ -567,7 +572,8 @@ ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
567
572
  ngx_http_clean_header(r);
568
573
 
569
574
  r->headers_out.status = NGX_HTTP_OK;
570
- ngx_str_set(&r->headers_out.content_type, "text/plain");
575
+ r->headers_out.content_type_len = sizeof("application/json") - 1;
576
+ ngx_str_set(&r->headers_out.content_type, "application/json");
571
577
  r->headers_out.content_type_lowcase = NULL;
572
578
 
573
579
  if (ctx == NULL) {
@@ -451,6 +451,8 @@ ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
451
451
 
452
452
  node = ngx_slab_alloc_locked(ctx->shpool, size);
453
453
  if (node == NULL) {
454
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
455
+ "could not allocate node%s", ctx->shpool->log_ctx);
454
456
  return NGX_ERROR;
455
457
  }
456
458
  }
@@ -674,6 +676,8 @@ ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)
674
676
  ngx_sprintf(ctx->shpool->log_ctx, " in limit_req zone \"%V\"%Z",
675
677
  &shm_zone->shm.name);
676
678
 
679
+ ctx->shpool->log_nomem = 0;
680
+
677
681
  return NGX_OK;
678
682
  }
679
683
 
@@ -912,7 +916,7 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
912
916
  continue;
913
917
  }
914
918
 
915
- if (ngx_strncmp(value[i].data, "nodelay", 7) == 0) {
919
+ if (ngx_strcmp(value[i].data, "nodelay") == 0) {
916
920
  nodelay = 1;
917
921
  continue;
918
922
  }
@@ -131,7 +131,7 @@ ngx_http_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
131
131
  }
132
132
 
133
133
  if (!value->valid) {
134
- value = ngx_http_get_flushed_variable(r, (ngx_uint_t) value->data);
134
+ value = ngx_http_get_flushed_variable(r, (uintptr_t) value->data);
135
135
 
136
136
  if (value == NULL || value->not_found) {
137
137
  value = &ngx_http_variable_null_value;
@@ -414,7 +414,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
414
414
  var = ctx->var_values.elts;
415
415
 
416
416
  for (i = 0; i < ctx->var_values.nelts; i++) {
417
- if (index == (ngx_int_t) var[i].data) {
417
+ if (index == (intptr_t) var[i].data) {
418
418
  var = &var[i];
419
419
  goto found;
420
420
  }
@@ -429,7 +429,7 @@ ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
429
429
  var->no_cacheable = 0;
430
430
  var->not_found = 0;
431
431
  var->len = 0;
432
- var->data = (u_char *) index;
432
+ var->data = (u_char *) (intptr_t) index;
433
433
 
434
434
  goto found;
435
435
  }
@@ -197,7 +197,6 @@ ngx_http_memcached_handler(ngx_http_request_t *r)
197
197
  return NGX_HTTP_INTERNAL_SERVER_ERROR;
198
198
  }
199
199
 
200
- ctx->rest = NGX_HTTP_MEMCACHED_END;
201
200
  ctx->request = r;
202
201
 
203
202
  ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
@@ -309,10 +308,15 @@ ngx_http_memcached_process_header(ngx_http_request_t *r)
309
308
 
310
309
  found:
311
310
 
312
- *p = '\0';
313
-
314
- line.len = p - u->buffer.pos - 1;
315
311
  line.data = u->buffer.pos;
312
+ line.len = p - u->buffer.pos;
313
+
314
+ if (line.len == 0 || *(p - 1) != CR) {
315
+ goto no_valid;
316
+ }
317
+
318
+ *p = '\0';
319
+ line.len--;
316
320
 
317
321
  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
318
322
  "memcached: \"%V\"", &line);
@@ -387,10 +391,9 @@ found:
387
391
  length:
388
392
 
389
393
  start = p;
394
+ p = line.data + line.len;
390
395
 
391
- while (*p && *p++ != CR) { /* void */ }
392
-
393
- u->headers_in.content_length_n = ngx_atoof(start, p - start - 1);
396
+ u->headers_in.content_length_n = ngx_atoof(start, p - start);
394
397
  if (u->headers_in.content_length_n == -1) {
395
398
  ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
396
399
  "memcached sent invalid length in response \"%V\" "
@@ -401,7 +404,7 @@ found:
401
404
 
402
405
  u->headers_in.status_n = 200;
403
406
  u->state->status = 200;
404
- u->buffer.pos = p + 1;
407
+ u->buffer.pos = p + sizeof(CRLF) - 1;
405
408
 
406
409
  return NGX_OK;
407
410
  }
@@ -410,8 +413,10 @@ found:
410
413
  ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
411
414
  "key: \"%V\" was not found by memcached", &ctx->key);
412
415
 
416
+ u->headers_in.content_length_n = 0;
413
417
  u->headers_in.status_n = 404;
414
418
  u->state->status = 404;
419
+ u->buffer.pos = p + sizeof("END" CRLF) - 1;
415
420
  u->keepalive = 1;
416
421
 
417
422
  return NGX_OK;
@@ -435,7 +440,13 @@ ngx_http_memcached_filter_init(void *data)
435
440
 
436
441
  u = ctx->request->upstream;
437
442
 
438
- u->length += NGX_HTTP_MEMCACHED_END;
443
+ if (u->headers_in.status_n != 404) {
444
+ u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;
445
+ ctx->rest = NGX_HTTP_MEMCACHED_END;
446
+
447
+ } else {
448
+ u->length = 0;
449
+ }
439
450
 
440
451
  return NGX_OK;
441
452
  }
@@ -509,7 +520,7 @@ ngx_http_memcached_filter(void *data, ssize_t bytes)
509
520
  return NGX_OK;
510
521
  }
511
522
 
512
- last += u->length - NGX_HTTP_MEMCACHED_END;
523
+ last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);
513
524
 
514
525
  if (ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0) {
515
526
  ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
@@ -27,14 +27,15 @@
27
27
  #define NGX_HTTP_MP4_CTTS_ATOM 15
28
28
  #define NGX_HTTP_MP4_CTTS_DATA 16
29
29
  #define NGX_HTTP_MP4_STSC_ATOM 17
30
- #define NGX_HTTP_MP4_STSC_CHUNK 18
30
+ #define NGX_HTTP_MP4_STSC_START 18
31
31
  #define NGX_HTTP_MP4_STSC_DATA 19
32
- #define NGX_HTTP_MP4_STSZ_ATOM 20
33
- #define NGX_HTTP_MP4_STSZ_DATA 21
34
- #define NGX_HTTP_MP4_STCO_ATOM 22
35
- #define NGX_HTTP_MP4_STCO_DATA 23
36
- #define NGX_HTTP_MP4_CO64_ATOM 24
37
- #define NGX_HTTP_MP4_CO64_DATA 25
32
+ #define NGX_HTTP_MP4_STSC_END 20
33
+ #define NGX_HTTP_MP4_STSZ_ATOM 21
34
+ #define NGX_HTTP_MP4_STSZ_DATA 22
35
+ #define NGX_HTTP_MP4_STCO_ATOM 23
36
+ #define NGX_HTTP_MP4_STCO_DATA 24
37
+ #define NGX_HTTP_MP4_CO64_ATOM 25
38
+ #define NGX_HTTP_MP4_CO64_DATA 26
38
39
 
39
40
  #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
40
41
 
@@ -62,10 +63,15 @@ typedef struct {
62
63
  uint32_t chunks;
63
64
 
64
65
  ngx_uint_t start_sample;
66
+ ngx_uint_t end_sample;
65
67
  ngx_uint_t start_chunk;
66
- ngx_uint_t chunk_samples;
67
- uint64_t chunk_samples_size;
68
+ ngx_uint_t end_chunk;
69
+ ngx_uint_t start_chunk_samples;
70
+ ngx_uint_t end_chunk_samples;
71
+ uint64_t start_chunk_samples_size;
72
+ uint64_t end_chunk_samples_size;
68
73
  off_t start_offset;
74
+ off_t end_offset;
69
75
 
70
76
  size_t tkhd_size;
71
77
  size_t mdhd_size;
@@ -95,7 +101,8 @@ typedef struct {
95
101
  ngx_buf_t ctts_atom_buf;
96
102
  ngx_buf_t ctts_data_buf;
97
103
  ngx_buf_t stsc_atom_buf;
98
- ngx_buf_t stsc_chunk_buf;
104
+ ngx_buf_t stsc_start_chunk_buf;
105
+ ngx_buf_t stsc_end_chunk_buf;
99
106
  ngx_buf_t stsc_data_buf;
100
107
  ngx_buf_t stsz_atom_buf;
101
108
  ngx_buf_t stsz_data_buf;
@@ -104,7 +111,8 @@ typedef struct {
104
111
  ngx_buf_t co64_atom_buf;
105
112
  ngx_buf_t co64_data_buf;
106
113
 
107
- ngx_mp4_stsc_entry_t stsc_chunk_entry;
114
+ ngx_mp4_stsc_entry_t stsc_start_chunk_entry;
115
+ ngx_mp4_stsc_entry_t stsc_end_chunk_entry;
108
116
  } ngx_http_mp4_trak_t;
109
117
 
110
118
 
@@ -121,6 +129,7 @@ typedef struct {
121
129
  off_t end;
122
130
  off_t content_length;
123
131
  ngx_uint_t start;
132
+ ngx_uint_t length;
124
133
  uint32_t timescale;
125
134
  ngx_http_request_t *request;
126
135
  ngx_array_t trak;
@@ -157,7 +166,11 @@ typedef struct {
157
166
  #define ngx_mp4_atom_header(mp4) (mp4->buffer_pos - 8)
158
167
  #define ngx_mp4_atom_data(mp4) mp4->buffer_pos
159
168
  #define ngx_mp4_atom_data_size(t) (uint64_t) (sizeof(t) - 8)
160
- #define ngx_mp4_atom_next(mp4, n) mp4->buffer_pos += n; mp4->offset += n
169
+
170
+
171
+ #define ngx_mp4_atom_next(mp4, n) \
172
+ mp4->buffer_pos += (size_t) n; \
173
+ mp4->offset += n
161
174
 
162
175
 
163
176
  #define ngx_mp4_set_atom_name(p, n1, n2, n3, n4) \
@@ -202,6 +215,8 @@ typedef struct {
202
215
  &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]
203
216
 
204
217
 
218
+ static ngx_int_t ngx_http_mp4_handler(ngx_http_request_t *r);
219
+
205
220
  static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4);
206
221
  static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
207
222
  ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size);
@@ -213,7 +228,7 @@ static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,
213
228
  static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,
214
229
  uint64_t atom_data_size);
215
230
  static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,
216
- off_t start_offset);
231
+ off_t start_offset, off_t end_offset);
217
232
  static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,
218
233
  uint64_t atom_data_size);
219
234
  static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,
@@ -252,18 +267,26 @@ static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,
252
267
  uint64_t atom_data_size);
253
268
  static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
254
269
  ngx_http_mp4_trak_t *trak);
270
+ static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
271
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
255
272
  static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,
256
273
  uint64_t atom_data_size);
257
274
  static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
258
275
  ngx_http_mp4_trak_t *trak);
276
+ static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
277
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
259
278
  static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,
260
279
  uint64_t atom_data_size);
261
280
  static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
262
281
  ngx_http_mp4_trak_t *trak);
282
+ static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
283
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
263
284
  static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,
264
285
  uint64_t atom_data_size);
265
286
  static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
266
287
  ngx_http_mp4_trak_t *trak);
288
+ static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
289
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start);
267
290
  static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,
268
291
  uint64_t atom_data_size);
269
292
  static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
@@ -280,10 +303,12 @@ static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
280
303
  ngx_http_mp4_trak_t *trak);
281
304
  static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
282
305
  ngx_http_mp4_trak_t *trak, off_t adjustment);
306
+
283
307
  static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
284
308
  static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);
285
309
  static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);
286
310
 
311
+
287
312
  static ngx_command_t ngx_http_mp4_commands[] = {
288
313
 
289
314
  { ngx_string("mp4"),
@@ -395,8 +420,8 @@ ngx_http_mp4_handler(ngx_http_request_t *r)
395
420
  {
396
421
  u_char *last;
397
422
  size_t root;
398
- ngx_int_t rc, start;
399
- ngx_uint_t level;
423
+ ngx_int_t rc, start, end;
424
+ ngx_uint_t level, length;
400
425
  ngx_str_t path, value;
401
426
  ngx_log_t *log;
402
427
  ngx_buf_t *b;
@@ -501,6 +526,7 @@ ngx_http_mp4_handler(ngx_http_request_t *r)
501
526
  r->allow_ranges = 1;
502
527
 
503
528
  start = -1;
529
+ length = 0;
504
530
  r->headers_out.content_length_n = of.size;
505
531
  mp4 = NULL;
506
532
  b = NULL;
@@ -518,47 +544,72 @@ ngx_http_mp4_handler(ngx_http_request_t *r)
518
544
  ngx_set_errno(0);
519
545
  start = (int) (strtod((char *) value.data, NULL) * 1000);
520
546
 
521
- if (ngx_errno == 0 && start >= 0) {
522
- r->allow_ranges = 0;
547
+ if (ngx_errno != 0) {
548
+ start = -1;
549
+ }
550
+ }
551
+
552
+ if (ngx_http_arg(r, (u_char *) "end", 3, &value) == NGX_OK) {
553
+
554
+ ngx_set_errno(0);
555
+ end = (int) (strtod((char *) value.data, NULL) * 1000);
556
+
557
+ if (ngx_errno != 0) {
558
+ end = -1;
559
+ }
560
+
561
+ if (end > 0) {
562
+ if (start < 0) {
563
+ start = 0;
564
+ }
523
565
 
524
- mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
525
- if (mp4 == NULL) {
526
- return NGX_HTTP_INTERNAL_SERVER_ERROR;
566
+ if (end > start) {
567
+ length = end - start;
527
568
  }
569
+ }
570
+ }
571
+ }
528
572
 
529
- mp4->file.fd = of.fd;
530
- mp4->file.name = path;
531
- mp4->file.log = r->connection->log;;
532
- mp4->end = of.size;
533
- mp4->start = (ngx_uint_t) start;
534
- mp4->request = r;
573
+ if (start >= 0) {
574
+ r->single_range = 1;
535
575
 
536
- switch (ngx_http_mp4_process(mp4)) {
576
+ mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
577
+ if (mp4 == NULL) {
578
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
579
+ }
537
580
 
538
- case NGX_DECLINED:
539
- if (mp4->buffer) {
540
- ngx_pfree(r->pool, mp4->buffer);
541
- }
581
+ mp4->file.fd = of.fd;
582
+ mp4->file.name = path;
583
+ mp4->file.log = r->connection->log;
584
+ mp4->end = of.size;
585
+ mp4->start = (ngx_uint_t) start;
586
+ mp4->length = length;
587
+ mp4->request = r;
542
588
 
543
- ngx_pfree(r->pool, mp4);
544
- mp4 = NULL;
589
+ switch (ngx_http_mp4_process(mp4)) {
545
590
 
546
- break;
591
+ case NGX_DECLINED:
592
+ if (mp4->buffer) {
593
+ ngx_pfree(r->pool, mp4->buffer);
594
+ }
547
595
 
548
- case NGX_OK:
549
- r->headers_out.content_length_n = mp4->content_length;
550
- break;
596
+ ngx_pfree(r->pool, mp4);
597
+ mp4 = NULL;
551
598
 
552
- default: /* NGX_ERROR */
553
- if (mp4->buffer) {
554
- ngx_pfree(r->pool, mp4->buffer);
555
- }
599
+ break;
556
600
 
557
- ngx_pfree(r->pool, mp4);
601
+ case NGX_OK:
602
+ r->headers_out.content_length_n = mp4->content_length;
603
+ break;
558
604
 
559
- return NGX_HTTP_INTERNAL_SERVER_ERROR;
560
- }
605
+ default: /* NGX_ERROR */
606
+ if (mp4->buffer) {
607
+ ngx_pfree(r->pool, mp4->buffer);
561
608
  }
609
+
610
+ ngx_pfree(r->pool, mp4);
611
+
612
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
562
613
  }
563
614
  }
564
615
 
@@ -638,15 +689,15 @@ ngx_http_mp4_handler(ngx_http_request_t *r)
638
689
  static ngx_int_t
639
690
  ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
640
691
  {
641
- off_t start_offset, adjustment;
692
+ off_t start_offset, end_offset, adjustment;
642
693
  ngx_int_t rc;
643
694
  ngx_uint_t i, j;
644
695
  ngx_chain_t **prev;
645
696
  ngx_http_mp4_trak_t *trak;
646
697
  ngx_http_mp4_conf_t *conf;
647
698
 
648
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
649
- "mp4 start:%ui", mp4->start);
699
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
700
+ "mp4 start:%ui, length:%ui", mp4->start, mp4->length);
650
701
 
651
702
  conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
652
703
 
@@ -688,6 +739,7 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
688
739
  }
689
740
 
690
741
  start_offset = mp4->end;
742
+ end_offset = 0;
691
743
  trak = mp4->trak.elts;
692
744
 
693
745
  for (i = 0; i < mp4->trak.nelts; i++) {
@@ -735,6 +787,10 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
735
787
  start_offset = trak[i].start_offset;
736
788
  }
737
789
 
790
+ if (end_offset < trak[i].end_offset) {
791
+ end_offset = trak[i].end_offset;
792
+ }
793
+
738
794
  *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
739
795
  prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;
740
796
 
@@ -746,6 +802,10 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
746
802
  }
747
803
  }
748
804
 
805
+ if (end_offset < start_offset) {
806
+ end_offset = start_offset;
807
+ }
808
+
749
809
  mp4->moov_size += 8;
750
810
 
751
811
  ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);
@@ -762,7 +822,7 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
762
822
  }
763
823
 
764
824
  adjustment = mp4->ftyp_size + mp4->moov_size
765
- + ngx_http_mp4_update_mdat_atom(mp4, start_offset)
825
+ + ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset)
766
826
  - start_offset;
767
827
 
768
828
  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
@@ -948,7 +1008,7 @@ ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
948
1008
  ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom");
949
1009
 
950
1010
  if (atom_data_size > 1024
951
- || ngx_mp4_atom_data(mp4) + atom_data_size > mp4->buffer_end)
1011
+ || ngx_mp4_atom_data(mp4) + (size_t) atom_data_size > mp4->buffer_end)
952
1012
  {
953
1013
  ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
954
1014
  "\"%s\" mp4 ftyp atom is too large:%uL",
@@ -1006,7 +1066,7 @@ ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1006
1066
 
1007
1067
  no_mdat = (mp4->mdat_atom.buf == NULL);
1008
1068
 
1009
- if (no_mdat && mp4->start == 0) {
1069
+ if (no_mdat && mp4->start == 0 && mp4->length == 0) {
1010
1070
  /*
1011
1071
  * send original file if moov atom resides before
1012
1072
  * mdat atom and client requests integral file
@@ -1105,7 +1165,8 @@ ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1105
1165
 
1106
1166
 
1107
1167
  static size_t
1108
- ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset)
1168
+ ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset,
1169
+ off_t end_offset)
1109
1170
  {
1110
1171
  off_t atom_data_size;
1111
1172
  u_char *atom_header;
@@ -1113,15 +1174,16 @@ ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset)
1113
1174
  uint64_t atom_size;
1114
1175
  ngx_buf_t *atom;
1115
1176
 
1116
- atom_data_size = mp4->mdat_data.buf->file_last - start_offset;
1177
+ atom_data_size = end_offset - start_offset;
1117
1178
  mp4->mdat_data.buf->file_pos = start_offset;
1179
+ mp4->mdat_data.buf->file_last = end_offset;
1118
1180
 
1119
1181
  ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1120
1182
  "mdat new offset @%O:%O", start_offset, atom_data_size);
1121
1183
 
1122
1184
  atom_header = mp4->mdat_atom_header;
1123
1185
 
1124
- if ((uint64_t) atom_data_size > 0xffffffff) {
1186
+ if ((uint64_t) atom_data_size > (uint64_t) 0xffffffff) {
1125
1187
  atom_size = 1;
1126
1188
  atom_header_size = sizeof(ngx_mp4_atom_header64_t);
1127
1189
  ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),
@@ -1196,7 +1258,7 @@ ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1196
1258
  u_char *atom_header;
1197
1259
  size_t atom_size;
1198
1260
  uint32_t timescale;
1199
- uint64_t duration;
1261
+ uint64_t duration, start_time, length_time;
1200
1262
  ngx_buf_t *atom;
1201
1263
  ngx_mp4_mvhd_atom_t *mvhd_atom;
1202
1264
  ngx_mp4_mvhd64_atom_t *mvhd64_atom;
@@ -1239,7 +1301,24 @@ ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1239
1301
  "mvhd timescale:%uD, duration:%uL, time:%.3fs",
1240
1302
  timescale, duration, (double) duration / timescale);
1241
1303
 
1242
- duration -= (uint64_t) mp4->start * timescale / 1000;
1304
+ start_time = (uint64_t) mp4->start * timescale / 1000;
1305
+
1306
+ if (duration < start_time) {
1307
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1308
+ "\"%s\" mp4 start time exceeds file duration",
1309
+ mp4->file.name.data);
1310
+ return NGX_ERROR;
1311
+ }
1312
+
1313
+ duration -= start_time;
1314
+
1315
+ if (mp4->length) {
1316
+ length_time = (uint64_t) mp4->length * timescale / 1000;
1317
+
1318
+ if (duration > length_time) {
1319
+ duration = length_time;
1320
+ }
1321
+ }
1243
1322
 
1244
1323
  ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1245
1324
  "mvhd new duration:%uL, time:%.3fs",
@@ -1296,7 +1375,7 @@ ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1296
1375
 
1297
1376
  trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;
1298
1377
 
1299
- atom_end = mp4->buffer_pos + atom_data_size;
1378
+ atom_end = mp4->buffer_pos + (size_t) atom_data_size;
1300
1379
  atom_file_end = mp4->offset + atom_data_size;
1301
1380
 
1302
1381
  rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);
@@ -1386,7 +1465,7 @@ ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1386
1465
  {
1387
1466
  u_char *atom_header;
1388
1467
  size_t atom_size;
1389
- uint64_t duration;
1468
+ uint64_t duration, start_time, length_time;
1390
1469
  ngx_buf_t *atom;
1391
1470
  ngx_http_mp4_trak_t *trak;
1392
1471
  ngx_mp4_tkhd_atom_t *tkhd_atom;
@@ -1426,7 +1505,23 @@ ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1426
1505
  "tkhd duration:%uL, time:%.3fs",
1427
1506
  duration, (double) duration / mp4->timescale);
1428
1507
 
1429
- duration -= (uint64_t) mp4->start * mp4->timescale / 1000;
1508
+ start_time = (uint64_t) mp4->start * mp4->timescale / 1000;
1509
+
1510
+ if (duration <= start_time) {
1511
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1512
+ "tkhd duration is less than start time");
1513
+ return NGX_DECLINED;
1514
+ }
1515
+
1516
+ duration -= start_time;
1517
+
1518
+ if (mp4->length) {
1519
+ length_time = (uint64_t) mp4->length * mp4->timescale / 1000;
1520
+
1521
+ if (duration > length_time) {
1522
+ duration = length_time;
1523
+ }
1524
+ }
1430
1525
 
1431
1526
  ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1432
1527
  "tkhd new duration:%uL, time:%.3fs",
@@ -1529,7 +1624,7 @@ ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1529
1624
  u_char *atom_header;
1530
1625
  size_t atom_size;
1531
1626
  uint32_t timescale;
1532
- uint64_t duration;
1627
+ uint64_t duration, start_time, length_time;
1533
1628
  ngx_buf_t *atom;
1534
1629
  ngx_http_mp4_trak_t *trak;
1535
1630
  ngx_mp4_mdhd_atom_t *mdhd_atom;
@@ -1571,7 +1666,23 @@ ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1571
1666
  "mdhd timescale:%uD, duration:%uL, time:%.3fs",
1572
1667
  timescale, duration, (double) duration / timescale);
1573
1668
 
1574
- duration -= (uint64_t) mp4->start * timescale / 1000;
1669
+ start_time = (uint64_t) mp4->start * timescale / 1000;
1670
+
1671
+ if (duration <= start_time) {
1672
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1673
+ "mdhd duration is less than start time");
1674
+ return NGX_DECLINED;
1675
+ }
1676
+
1677
+ duration -= start_time;
1678
+
1679
+ if (mp4->length) {
1680
+ length_time = (uint64_t) mp4->length * timescale / 1000;
1681
+
1682
+ if (duration > length_time) {
1683
+ duration = length_time;
1684
+ }
1685
+ }
1575
1686
 
1576
1687
  ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1577
1688
  "mdhd new duration:%uL, time:%.3fs",
@@ -1944,13 +2055,9 @@ static ngx_int_t
1944
2055
  ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
1945
2056
  ngx_http_mp4_trak_t *trak)
1946
2057
  {
1947
- size_t atom_size;
1948
- uint32_t entries, count, duration;
1949
- uint64_t start_time;
1950
- ngx_buf_t *atom, *data;
1951
- ngx_uint_t start_sample;
1952
- ngx_mp4_stts_atom_t *stts_atom;
1953
- ngx_mp4_stts_entry_t *entry, *end;
2058
+ size_t atom_size;
2059
+ ngx_buf_t *atom, *data;
2060
+ ngx_mp4_stts_atom_t *stts_atom;
1954
2061
 
1955
2062
  /*
1956
2063
  * mdia.minf.stbl.stts updating requires trak->timescale
@@ -1969,12 +2076,60 @@ ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
1969
2076
  return NGX_ERROR;
1970
2077
  }
1971
2078
 
1972
- entries = trak->time_to_sample_entries;
1973
- start_time = (uint64_t) mp4->start * trak->timescale / 1000;
2079
+ if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) {
2080
+ return NGX_ERROR;
2081
+ }
2082
+
2083
+ if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) {
2084
+ return NGX_ERROR;
2085
+ }
1974
2086
 
1975
2087
  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1976
- "time-to-sample start_time:%uL", start_time);
2088
+ "time-to-sample entries:%uD", trak->time_to_sample_entries);
1977
2089
 
2090
+ atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
2091
+ trak->size += atom_size;
2092
+
2093
+ atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;
2094
+ stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;
2095
+ ngx_mp4_set_32value(stts_atom->size, atom_size);
2096
+ ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries);
2097
+
2098
+ return NGX_OK;
2099
+ }
2100
+
2101
+
2102
+ static ngx_int_t
2103
+ ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
2104
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2105
+ {
2106
+ uint32_t count, duration, rest;
2107
+ uint64_t start_time;
2108
+ ngx_buf_t *data;
2109
+ ngx_uint_t start_sample, entries, start_sec;
2110
+ ngx_mp4_stts_entry_t *entry, *end;
2111
+
2112
+ if (start) {
2113
+ start_sec = mp4->start;
2114
+
2115
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2116
+ "mp4 stts crop start_time:%ui", start_sec);
2117
+
2118
+ } else if (mp4->length) {
2119
+ start_sec = mp4->length;
2120
+
2121
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2122
+ "mp4 stts crop end_time:%ui", start_sec);
2123
+
2124
+ } else {
2125
+ return NGX_OK;
2126
+ }
2127
+
2128
+ data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
2129
+
2130
+ start_time = (uint64_t) start_sec * trak->timescale / 1000;
2131
+
2132
+ entries = trak->time_to_sample_entries;
1978
2133
  start_sample = 0;
1979
2134
  entry = (ngx_mp4_stts_entry_t *) data->pos;
1980
2135
  end = (ngx_mp4_stts_entry_t *) data->last;
@@ -1983,13 +2138,13 @@ ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
1983
2138
  count = ngx_mp4_get_32value(entry->count);
1984
2139
  duration = ngx_mp4_get_32value(entry->duration);
1985
2140
 
1986
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1987
- "count:%uD, duration:%uD", count, duration);
2141
+ ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2142
+ "time:%uL, count:%uD, duration:%uD",
2143
+ start_time, count, duration);
1988
2144
 
1989
2145
  if (start_time < (uint64_t) count * duration) {
1990
2146
  start_sample += (ngx_uint_t) (start_time / duration);
1991
- count -= (uint32_t) (start_time / duration);
1992
- ngx_mp4_set_32value(entry->count, count);
2147
+ rest = (uint32_t) (start_time / duration);
1993
2148
  goto found;
1994
2149
  }
1995
2150
 
@@ -1999,27 +2154,44 @@ ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
1999
2154
  entry++;
2000
2155
  }
2001
2156
 
2002
- ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2003
- "start time is out mp4 stts samples in \"%s\"",
2004
- mp4->file.name.data);
2157
+ if (start) {
2158
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2159
+ "start time is out mp4 stts samples in \"%s\"",
2160
+ mp4->file.name.data);
2005
2161
 
2006
- return NGX_ERROR;
2162
+ return NGX_ERROR;
2163
+
2164
+ } else {
2165
+ trak->end_sample = trak->start_sample + start_sample;
2166
+
2167
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2168
+ "end_sample:%ui", trak->end_sample);
2169
+
2170
+ return NGX_OK;
2171
+ }
2007
2172
 
2008
2173
  found:
2009
2174
 
2010
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2011
- "start_sample:%ui, new count:%uD", start_sample, count);
2175
+ if (start) {
2176
+ ngx_mp4_set_32value(entry->count, count - rest);
2177
+ data->pos = (u_char *) entry;
2178
+ trak->time_to_sample_entries = entries;
2179
+ trak->start_sample = start_sample;
2012
2180
 
2013
- trak->start_sample = start_sample;
2181
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2182
+ "start_sample:%ui, new count:%uD",
2183
+ trak->start_sample, count - rest);
2014
2184
 
2015
- data->pos = (u_char *) entry;
2016
- atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
2017
- trak->size += atom_size;
2185
+ } else {
2186
+ ngx_mp4_set_32value(entry->count, rest);
2187
+ data->last = (u_char *) (entry + 1);
2188
+ trak->time_to_sample_entries -= entries - 1;
2189
+ trak->end_sample = trak->start_sample + start_sample;
2018
2190
 
2019
- atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;
2020
- stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;
2021
- ngx_mp4_set_32value(stts_atom->size, atom_size);
2022
- ngx_mp4_set_32value(stts_atom->entries, entries);
2191
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2192
+ "end_sample:%ui, new count:%uD",
2193
+ trak->end_sample, rest);
2194
+ }
2023
2195
 
2024
2196
  return NGX_OK;
2025
2197
  }
@@ -2101,7 +2273,7 @@ ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
2101
2273
  ngx_http_mp4_trak_t *trak)
2102
2274
  {
2103
2275
  size_t atom_size;
2104
- uint32_t entries, sample, start_sample, *entry, *end;
2276
+ uint32_t sample, start_sample, *entry, *end;
2105
2277
  ngx_buf_t *atom, *data;
2106
2278
  ngx_http_mp4_stss_atom_t *stss_atom;
2107
2279
 
@@ -2120,18 +2292,79 @@ ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
2120
2292
  return NGX_OK;
2121
2293
  }
2122
2294
 
2295
+ ngx_http_mp4_crop_stss_data(mp4, trak, 1);
2296
+ ngx_http_mp4_crop_stss_data(mp4, trak, 0);
2297
+
2298
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2299
+ "sync sample entries:%uD", trak->sync_samples_entries);
2300
+
2301
+ if (trak->sync_samples_entries) {
2302
+ entry = (uint32_t *) data->pos;
2303
+ end = (uint32_t *) data->last;
2304
+
2305
+ start_sample = trak->start_sample;
2306
+
2307
+ while (entry < end) {
2308
+ sample = ngx_mp4_get_32value(entry);
2309
+ sample -= start_sample;
2310
+ ngx_mp4_set_32value(entry, sample);
2311
+ entry++;
2312
+ }
2313
+
2314
+ } else {
2315
+ trak->out[NGX_HTTP_MP4_STSS_DATA].buf = NULL;
2316
+ }
2317
+
2318
+ atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);
2319
+ trak->size += atom_size;
2320
+
2321
+ atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;
2322
+ stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;
2323
+
2324
+ ngx_mp4_set_32value(stss_atom->size, atom_size);
2325
+ ngx_mp4_set_32value(stss_atom->entries, trak->sync_samples_entries);
2326
+
2327
+ return NGX_OK;
2328
+ }
2329
+
2330
+
2331
+ static void
2332
+ ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
2333
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2334
+ {
2335
+ uint32_t sample, start_sample, *entry, *end;
2336
+ ngx_buf_t *data;
2337
+ ngx_uint_t entries;
2338
+
2123
2339
  /* sync samples starts from 1 */
2124
- start_sample = trak->start_sample + 1;
2125
- entries = trak->sync_samples_entries;
2126
2340
 
2341
+ if (start) {
2342
+ start_sample = trak->start_sample + 1;
2343
+
2344
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2345
+ "mp4 stss crop start_sample:%uD", start_sample);
2346
+
2347
+ } else if (mp4->length) {
2348
+ start_sample = trak->end_sample + 1;
2349
+
2350
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2351
+ "mp4 stss crop end_sample:%uD", start_sample);
2352
+
2353
+ } else {
2354
+ return;
2355
+ }
2356
+
2357
+ data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
2358
+
2359
+ entries = trak->sync_samples_entries;
2127
2360
  entry = (uint32_t *) data->pos;
2128
2361
  end = (uint32_t *) data->last;
2129
2362
 
2130
2363
  while (entry < end) {
2131
2364
  sample = ngx_mp4_get_32value(entry);
2132
2365
 
2133
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2134
- "start:%uD, sync:%uD", start_sample, sample);
2366
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2367
+ "sync:%uD", sample);
2135
2368
 
2136
2369
  if (sample >= start_sample) {
2137
2370
  goto found;
@@ -2141,35 +2374,19 @@ ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
2141
2374
  entry++;
2142
2375
  }
2143
2376
 
2144
- ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2145
- "start sample is out of mp4 stss atom in \"%s\"",
2146
- mp4->file.name.data);
2147
-
2148
- return NGX_ERROR;
2377
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2378
+ "sample is out of mp4 stss atom");
2149
2379
 
2150
2380
  found:
2151
2381
 
2152
- data->pos = (u_char *) entry;
2153
-
2154
- start_sample = trak->start_sample;
2382
+ if (start) {
2383
+ data->pos = (u_char *) entry;
2384
+ trak->sync_samples_entries = entries;
2155
2385
 
2156
- while (entry < end) {
2157
- sample = ngx_mp4_get_32value(entry);
2158
- sample -= start_sample;
2159
- ngx_mp4_set_32value(entry, sample);
2160
- entry++;
2386
+ } else {
2387
+ data->last = (u_char *) entry;
2388
+ trak->sync_samples_entries -= entries;
2161
2389
  }
2162
-
2163
- atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);
2164
- trak->size += atom_size;
2165
-
2166
- atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;
2167
- stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;
2168
-
2169
- ngx_mp4_set_32value(stss_atom->size, atom_size);
2170
- ngx_mp4_set_32value(stss_atom->entries, entries);
2171
-
2172
- return NGX_OK;
2173
2390
  }
2174
2391
 
2175
2392
 
@@ -2253,11 +2470,9 @@ static void
2253
2470
  ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
2254
2471
  ngx_http_mp4_trak_t *trak)
2255
2472
  {
2256
- size_t atom_size;
2257
- uint32_t entries, count, start_sample;
2258
- ngx_buf_t *atom, *data;
2259
- ngx_mp4_ctts_atom_t *ctts_atom;
2260
- ngx_mp4_ctts_entry_t *entry, *end;
2473
+ size_t atom_size;
2474
+ ngx_buf_t *atom, *data;
2475
+ ngx_mp4_ctts_atom_t *ctts_atom;
2261
2476
 
2262
2477
  /*
2263
2478
  * mdia.minf.stbl.ctts updating requires trak->start_sample
@@ -2274,8 +2489,61 @@ ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
2274
2489
  return;
2275
2490
  }
2276
2491
 
2492
+ ngx_http_mp4_crop_ctts_data(mp4, trak, 1);
2493
+ ngx_http_mp4_crop_ctts_data(mp4, trak, 0);
2494
+
2495
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2496
+ "composition offset entries:%uD",
2497
+ trak->composition_offset_entries);
2498
+
2499
+ if (trak->composition_offset_entries == 0) {
2500
+ trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
2501
+ trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
2502
+ return;
2503
+ }
2504
+
2505
+ atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);
2506
+ trak->size += atom_size;
2507
+
2508
+ atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;
2509
+ ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;
2510
+
2511
+ ngx_mp4_set_32value(ctts_atom->size, atom_size);
2512
+ ngx_mp4_set_32value(ctts_atom->entries, trak->composition_offset_entries);
2513
+
2514
+ return;
2515
+ }
2516
+
2517
+
2518
+ static void
2519
+ ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
2520
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2521
+ {
2522
+ uint32_t count, start_sample, rest;
2523
+ ngx_buf_t *data;
2524
+ ngx_uint_t entries;
2525
+ ngx_mp4_ctts_entry_t *entry, *end;
2526
+
2277
2527
  /* sync samples starts from 1 */
2278
- start_sample = trak->start_sample + 1;
2528
+
2529
+ if (start) {
2530
+ start_sample = trak->start_sample + 1;
2531
+
2532
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2533
+ "mp4 ctts crop start_sample:%uD", start_sample);
2534
+
2535
+ } else if (mp4->length) {
2536
+ start_sample = trak->end_sample - trak->start_sample + 1;
2537
+
2538
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2539
+ "mp4 ctts crop end_sample:%uD", start_sample);
2540
+
2541
+ } else {
2542
+ return;
2543
+ }
2544
+
2545
+ data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
2546
+
2279
2547
  entries = trak->composition_offset_entries;
2280
2548
  entry = (ngx_mp4_ctts_entry_t *) data->pos;
2281
2549
  end = (ngx_mp4_ctts_entry_t *) data->last;
@@ -2284,12 +2552,11 @@ ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
2284
2552
  count = ngx_mp4_get_32value(entry->count);
2285
2553
 
2286
2554
  ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2287
- "start:%uD, count:%uD, offset:%uD",
2555
+ "sample:%uD, count:%uD, offset:%uD",
2288
2556
  start_sample, count, ngx_mp4_get_32value(entry->offset));
2289
2557
 
2290
2558
  if (start_sample <= count) {
2291
- count -= (start_sample - 1);
2292
- ngx_mp4_set_32value(entry->count, count);
2559
+ rest = start_sample - 1;
2293
2560
  goto found;
2294
2561
  }
2295
2562
 
@@ -2298,24 +2565,25 @@ ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
2298
2565
  entry++;
2299
2566
  }
2300
2567
 
2301
- trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
2302
- trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
2568
+ if (start) {
2569
+ data->pos = (u_char *) end;
2570
+ trak->composition_offset_entries = 0;
2571
+ }
2303
2572
 
2304
2573
  return;
2305
2574
 
2306
2575
  found:
2307
2576
 
2308
- data->pos = (u_char *) entry;
2309
- atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);
2310
- trak->size += atom_size;
2311
-
2312
- atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;
2313
- ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;
2577
+ if (start) {
2578
+ ngx_mp4_set_32value(entry->count, count - rest);
2579
+ data->pos = (u_char *) entry;
2580
+ trak->composition_offset_entries = entries;
2314
2581
 
2315
- ngx_mp4_set_32value(ctts_atom->size, atom_size);
2316
- ngx_mp4_set_32value(ctts_atom->entries, entries);
2317
-
2318
- return;
2582
+ } else {
2583
+ ngx_mp4_set_32value(entry->count, rest);
2584
+ data->last = (u_char *) (entry + 1);
2585
+ trak->composition_offset_entries -= entries - 1;
2586
+ }
2319
2587
  }
2320
2588
 
2321
2589
 
@@ -2394,11 +2662,10 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
2394
2662
  ngx_http_mp4_trak_t *trak)
2395
2663
  {
2396
2664
  size_t atom_size;
2397
- uint32_t start_sample, entries, chunk, samples, id,
2398
- next_chunk, n;
2399
- ngx_buf_t *atom, *data, *buf;
2665
+ uint32_t chunk;
2666
+ ngx_buf_t *atom, *data;
2400
2667
  ngx_mp4_stsc_atom_t *stsc_atom;
2401
- ngx_mp4_stsc_entry_t *entry, *first, *end;
2668
+ ngx_mp4_stsc_entry_t *entry, *end;
2402
2669
 
2403
2670
  /*
2404
2671
  * mdia.minf.stbl.stsc updating requires trak->start_sample
@@ -2425,15 +2692,97 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
2425
2692
  return NGX_ERROR;
2426
2693
  }
2427
2694
 
2428
- start_sample = (uint32_t) trak->start_sample;
2695
+ if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) {
2696
+ return NGX_ERROR;
2697
+ }
2698
+
2699
+ if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) {
2700
+ return NGX_ERROR;
2701
+ }
2702
+
2703
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2704
+ "sample-to-chunk entries:%uD",
2705
+ trak->sample_to_chunk_entries);
2706
+
2707
+ entry = (ngx_mp4_stsc_entry_t *) data->pos;
2708
+ end = (ngx_mp4_stsc_entry_t *) data->last;
2709
+
2710
+ while (entry < end) {
2711
+ chunk = ngx_mp4_get_32value(entry->chunk);
2712
+ chunk -= trak->start_chunk;
2713
+ ngx_mp4_set_32value(entry->chunk, chunk);
2714
+ entry++;
2715
+ }
2716
+
2717
+ atom_size = sizeof(ngx_mp4_stsc_atom_t)
2718
+ + trak->sample_to_chunk_entries * sizeof(ngx_mp4_stsc_entry_t);
2719
+
2720
+ trak->size += atom_size;
2721
+
2722
+ atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
2723
+ stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
2724
+
2725
+ ngx_mp4_set_32value(stsc_atom->size, atom_size);
2726
+ ngx_mp4_set_32value(stsc_atom->entries, trak->sample_to_chunk_entries);
2727
+
2728
+ return NGX_OK;
2729
+ }
2730
+
2731
+
2732
+ static ngx_int_t
2733
+ ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
2734
+ ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2735
+ {
2736
+ uint32_t start_sample, chunk, samples, id, next_chunk, n,
2737
+ prev_samples;
2738
+ ngx_buf_t *data, *buf;
2739
+ ngx_uint_t entries, target_chunk, chunk_samples;
2740
+ ngx_mp4_stsc_entry_t *entry, *end, *first;
2741
+
2429
2742
  entries = trak->sample_to_chunk_entries - 1;
2430
2743
 
2744
+ if (start) {
2745
+ start_sample = (uint32_t) trak->start_sample;
2746
+
2747
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2748
+ "mp4 stsc crop start_sample:%uD", start_sample);
2749
+
2750
+ } else if (mp4->length) {
2751
+ start_sample = (uint32_t) (trak->end_sample - trak->start_sample);
2752
+ samples = 0;
2753
+
2754
+ data = trak->out[NGX_HTTP_MP4_STSC_START].buf;
2755
+
2756
+ if (data) {
2757
+ entry = (ngx_mp4_stsc_entry_t *) data->pos;
2758
+ samples = ngx_mp4_get_32value(entry->samples);
2759
+ entries--;
2760
+
2761
+ if (samples > start_sample) {
2762
+ samples = start_sample;
2763
+ ngx_mp4_set_32value(entry->samples, samples);
2764
+ }
2765
+
2766
+ start_sample -= samples;
2767
+ }
2768
+
2769
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2770
+ "mp4 stsc crop end_sample:%uD, ext_samples:%uD",
2771
+ start_sample, samples);
2772
+
2773
+ } else {
2774
+ return NGX_OK;
2775
+ }
2776
+
2777
+ data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
2778
+
2431
2779
  entry = (ngx_mp4_stsc_entry_t *) data->pos;
2432
2780
  end = (ngx_mp4_stsc_entry_t *) data->last;
2433
2781
 
2434
2782
  chunk = ngx_mp4_get_32value(entry->chunk);
2435
2783
  samples = ngx_mp4_get_32value(entry->samples);
2436
2784
  id = ngx_mp4_get_32value(entry->id);
2785
+ prev_samples = 0;
2437
2786
  entry++;
2438
2787
 
2439
2788
  while (entry < end) {
@@ -2441,18 +2790,19 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
2441
2790
  next_chunk = ngx_mp4_get_32value(entry->chunk);
2442
2791
 
2443
2792
  ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2444
- "start_sample:%uD, chunk:%uD, chunks:%uD, "
2793
+ "sample:%uD, chunk:%uD, chunks:%uD, "
2445
2794
  "samples:%uD, id:%uD",
2446
2795
  start_sample, chunk, next_chunk - chunk, samples, id);
2447
2796
 
2448
2797
  n = (next_chunk - chunk) * samples;
2449
2798
 
2450
- if (start_sample <= n) {
2799
+ if (start_sample < n) {
2451
2800
  goto found;
2452
2801
  }
2453
2802
 
2454
2803
  start_sample -= n;
2455
2804
 
2805
+ prev_samples = samples;
2456
2806
  chunk = next_chunk;
2457
2807
  samples = ngx_mp4_get_32value(entry->samples);
2458
2808
  id = ngx_mp4_get_32value(entry->id);
@@ -2460,18 +2810,18 @@ ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
2460
2810
  entry++;
2461
2811
  }
2462
2812
 
2463
- next_chunk = trak->chunks;
2813
+ next_chunk = trak->chunks + 1;
2464
2814
 
2465
2815
  ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2466
- "start_sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
2816
+ "sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
2467
2817
  start_sample, chunk, next_chunk - chunk, samples);
2468
2818
 
2469
2819
  n = (next_chunk - chunk) * samples;
2470
2820
 
2471
2821
  if (start_sample > n) {
2472
2822
  ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2473
- "start time is out mp4 stsc chunks in \"%s\"",
2474
- mp4->file.name.data);
2823
+ "%s time is out mp4 stsc chunks in \"%s\"",
2824
+ start ? "start" : "end", mp4->file.name.data);
2475
2825
  return NGX_ERROR;
2476
2826
  }
2477
2827
 
@@ -2487,59 +2837,91 @@ found:
2487
2837
  return NGX_ERROR;
2488
2838
  }
2489
2839
 
2490
- trak->start_chunk = chunk - 1;
2840
+ target_chunk = chunk - 1;
2841
+ target_chunk += start_sample / samples;
2842
+ chunk_samples = start_sample % samples;
2491
2843
 
2492
- trak->start_chunk += start_sample / samples;
2493
- trak->chunk_samples = start_sample % samples;
2844
+ if (start) {
2845
+ data->pos = (u_char *) entry;
2494
2846
 
2495
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2496
- "start chunk:%ui, samples:%uD",
2497
- trak->start_chunk, trak->chunk_samples);
2847
+ trak->sample_to_chunk_entries = entries;
2848
+ trak->start_chunk = target_chunk;
2849
+ trak->start_chunk_samples = chunk_samples;
2850
+
2851
+ ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1);
2852
+
2853
+ samples -= chunk_samples;
2498
2854
 
2499
- data->pos = (u_char *) entry;
2500
- atom_size = sizeof(ngx_mp4_stsc_atom_t) + (data->last - data->pos);
2855
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2856
+ "start_chunk:%ui, start_chunk_samples:%ui",
2857
+ trak->start_chunk, trak->start_chunk_samples);
2858
+
2859
+ } else {
2860
+ if (start_sample) {
2861
+ data->last = (u_char *) (entry + 1);
2862
+ trak->sample_to_chunk_entries -= entries - 1;
2863
+ trak->end_chunk_samples = samples;
2501
2864
 
2502
- ngx_mp4_set_32value(entry->chunk, 1);
2865
+ } else {
2866
+ data->last = (u_char *) entry;
2867
+ trak->sample_to_chunk_entries -= entries;
2868
+ trak->end_chunk_samples = prev_samples;
2869
+ }
2503
2870
 
2504
- if (trak->chunk_samples && next_chunk - trak->start_chunk == 2) {
2871
+ if (chunk_samples) {
2872
+ trak->end_chunk = target_chunk + 1;
2873
+ trak->end_chunk_samples = chunk_samples;
2505
2874
 
2506
- /* last chunk in the entry */
2875
+ } else {
2876
+ trak->end_chunk = target_chunk;
2877
+ }
2507
2878
 
2508
- ngx_mp4_set_32value(entry->samples, samples - trak->chunk_samples);
2879
+ samples = chunk_samples;
2880
+ next_chunk = chunk + 1;
2509
2881
 
2510
- } else if (trak->chunk_samples) {
2882
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2883
+ "end_chunk:%ui, end_chunk_samples:%ui",
2884
+ trak->end_chunk, trak->end_chunk_samples);
2885
+ }
2511
2886
 
2512
- first = &trak->stsc_chunk_entry;
2887
+ if (chunk_samples && next_chunk - target_chunk == 2) {
2888
+
2889
+ ngx_mp4_set_32value(entry->samples, samples);
2890
+
2891
+ } else if (chunk_samples && start) {
2892
+
2893
+ first = &trak->stsc_start_chunk_entry;
2513
2894
  ngx_mp4_set_32value(first->chunk, 1);
2514
- ngx_mp4_set_32value(first->samples, samples - trak->chunk_samples);
2895
+ ngx_mp4_set_32value(first->samples, samples);
2515
2896
  ngx_mp4_set_32value(first->id, id);
2516
2897
 
2517
- buf = &trak->stsc_chunk_buf;
2898
+ buf = &trak->stsc_start_chunk_buf;
2518
2899
  buf->temporary = 1;
2519
2900
  buf->pos = (u_char *) first;
2520
2901
  buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2521
2902
 
2522
- trak->out[NGX_HTTP_MP4_STSC_CHUNK].buf = buf;
2903
+ trak->out[NGX_HTTP_MP4_STSC_START].buf = buf;
2523
2904
 
2524
- ngx_mp4_set_32value(entry->chunk, 2);
2905
+ ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2);
2525
2906
 
2526
- entries++;
2527
- atom_size += sizeof(ngx_mp4_stsc_entry_t);
2528
- }
2907
+ trak->sample_to_chunk_entries++;
2529
2908
 
2530
- while (++entry < end) {
2531
- chunk = ngx_mp4_get_32value(entry->chunk);
2532
- chunk -= trak->start_chunk;
2533
- ngx_mp4_set_32value(entry->chunk, chunk);
2534
- }
2909
+ } else if (chunk_samples) {
2535
2910
 
2536
- trak->size += atom_size;
2911
+ first = &trak->stsc_end_chunk_entry;
2912
+ ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk);
2913
+ ngx_mp4_set_32value(first->samples, samples);
2914
+ ngx_mp4_set_32value(first->id, id);
2537
2915
 
2538
- atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
2539
- stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
2916
+ buf = &trak->stsc_end_chunk_buf;
2917
+ buf->temporary = 1;
2918
+ buf->pos = (u_char *) first;
2919
+ buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2540
2920
 
2541
- ngx_mp4_set_32value(stsc_atom->size, atom_size);
2542
- ngx_mp4_set_32value(stsc_atom->entries, entries);
2921
+ trak->out[NGX_HTTP_MP4_STSC_END].buf = buf;
2922
+
2923
+ trak->sample_to_chunk_entries++;
2924
+ }
2543
2925
 
2544
2926
  return NGX_OK;
2545
2927
  }
@@ -2635,7 +3017,7 @@ ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
2635
3017
  ngx_http_mp4_trak_t *trak)
2636
3018
  {
2637
3019
  size_t atom_size;
2638
- uint32_t *pos, *end;
3020
+ uint32_t *pos, *end, entries;
2639
3021
  ngx_buf_t *atom, *data;
2640
3022
  ngx_mp4_stsz_atom_t *stsz_atom;
2641
3023
 
@@ -2651,22 +3033,47 @@ ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
2651
3033
  data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
2652
3034
 
2653
3035
  if (data) {
2654
- if (trak->start_sample > trak->sample_sizes_entries) {
3036
+ entries = trak->sample_sizes_entries;
3037
+
3038
+ if (trak->start_sample > entries) {
2655
3039
  ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2656
3040
  "start time is out mp4 stsz samples in \"%s\"",
2657
3041
  mp4->file.name.data);
2658
3042
  return NGX_ERROR;
2659
3043
  }
2660
3044
 
3045
+ entries -= trak->start_sample;
2661
3046
  data->pos += trak->start_sample * sizeof(uint32_t);
2662
3047
  end = (uint32_t *) data->pos;
2663
3048
 
2664
- for (pos = end - trak->chunk_samples; pos < end; pos++) {
2665
- trak->chunk_samples_size += ngx_mp4_get_32value(pos);
3049
+ for (pos = end - trak->start_chunk_samples; pos < end; pos++) {
3050
+ trak->start_chunk_samples_size += ngx_mp4_get_32value(pos);
2666
3051
  }
2667
3052
 
2668
3053
  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2669
- "chunk samples sizes:%uL", trak->chunk_samples_size);
3054
+ "chunk samples sizes:%uL",
3055
+ trak->start_chunk_samples_size);
3056
+
3057
+ if (mp4->length) {
3058
+ if (trak->end_sample - trak->start_sample > entries) {
3059
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3060
+ "end time is out mp4 stsz samples in \"%s\"",
3061
+ mp4->file.name.data);
3062
+ return NGX_ERROR;
3063
+ }
3064
+
3065
+ entries = trak->end_sample - trak->start_sample;
3066
+ data->last = data->pos + entries * sizeof(uint32_t);
3067
+ end = (uint32_t *) data->last;
3068
+
3069
+ for (pos = end - trak->end_chunk_samples; pos < end; pos++) {
3070
+ trak->end_chunk_samples_size += ngx_mp4_get_32value(pos);
3071
+ }
3072
+
3073
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3074
+ "mp4 stsz end_chunk_samples_size:%uL",
3075
+ trak->end_chunk_samples_size);
3076
+ }
2670
3077
 
2671
3078
  atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
2672
3079
  trak->size += atom_size;
@@ -2675,8 +3082,7 @@ ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
2675
3082
  stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
2676
3083
 
2677
3084
  ngx_mp4_set_32value(stsz_atom->size, atom_size);
2678
- ngx_mp4_set_32value(stsz_atom->entries,
2679
- trak->sample_sizes_entries - trak->start_sample);
3085
+ ngx_mp4_set_32value(stsz_atom->entries, entries);
2680
3086
  }
2681
3087
 
2682
3088
  return NGX_OK;
@@ -2757,6 +3163,7 @@ ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
2757
3163
  ngx_http_mp4_trak_t *trak)
2758
3164
  {
2759
3165
  size_t atom_size;
3166
+ uint32_t entries;
2760
3167
  ngx_buf_t *atom, *data;
2761
3168
  ngx_mp4_stco_atom_t *stco_atom;
2762
3169
 
@@ -2786,21 +3193,53 @@ ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
2786
3193
  }
2787
3194
 
2788
3195
  data->pos += trak->start_chunk * sizeof(uint32_t);
2789
- atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);
2790
- trak->size += atom_size;
2791
3196
 
2792
3197
  trak->start_offset = ngx_mp4_get_32value(data->pos);
2793
- trak->start_offset += trak->chunk_samples_size;
3198
+ trak->start_offset += trak->start_chunk_samples_size;
2794
3199
  ngx_mp4_set_32value(data->pos, trak->start_offset);
2795
3200
 
2796
3201
  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2797
- "start chunk offset:%uD", trak->start_offset);
3202
+ "start chunk offset:%O", trak->start_offset);
3203
+
3204
+ if (mp4->length) {
3205
+
3206
+ if (trak->end_chunk > trak->chunks) {
3207
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3208
+ "end time is out mp4 stco chunks in \"%s\"",
3209
+ mp4->file.name.data);
3210
+ return NGX_ERROR;
3211
+ }
3212
+
3213
+ entries = trak->end_chunk - trak->start_chunk;
3214
+ data->last = data->pos + entries * sizeof(uint32_t);
3215
+
3216
+ if (entries) {
3217
+ trak->end_offset =
3218
+ ngx_mp4_get_32value(data->last - sizeof(uint32_t));
3219
+ trak->end_offset += trak->end_chunk_samples_size;
3220
+
3221
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3222
+ "end chunk offset:%O", trak->end_offset);
3223
+ }
3224
+
3225
+ } else {
3226
+ entries = trak->chunks - trak->start_chunk;
3227
+ trak->end_offset = mp4->mdat_data.buf->file_last;
3228
+ }
3229
+
3230
+ if (entries == 0) {
3231
+ trak->start_offset = mp4->end;
3232
+ trak->end_offset = 0;
3233
+ }
3234
+
3235
+ atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);
3236
+ trak->size += atom_size;
2798
3237
 
2799
3238
  atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
2800
3239
  stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
2801
3240
 
2802
3241
  ngx_mp4_set_32value(stco_atom->size, atom_size);
2803
- ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk);
3242
+ ngx_mp4_set_32value(stco_atom->entries, entries);
2804
3243
 
2805
3244
  return NGX_OK;
2806
3245
  }
@@ -2908,6 +3347,7 @@ ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
2908
3347
  ngx_http_mp4_trak_t *trak)
2909
3348
  {
2910
3349
  size_t atom_size;
3350
+ uint64_t entries;
2911
3351
  ngx_buf_t *atom, *data;
2912
3352
  ngx_mp4_co64_atom_t *co64_atom;
2913
3353
 
@@ -2937,21 +3377,53 @@ ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
2937
3377
  }
2938
3378
 
2939
3379
  data->pos += trak->start_chunk * sizeof(uint64_t);
2940
- atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
2941
- trak->size += atom_size;
2942
3380
 
2943
3381
  trak->start_offset = ngx_mp4_get_64value(data->pos);
2944
- trak->start_offset += trak->chunk_samples_size;
3382
+ trak->start_offset += trak->start_chunk_samples_size;
2945
3383
  ngx_mp4_set_64value(data->pos, trak->start_offset);
2946
3384
 
2947
3385
  ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2948
- "start chunk offset:%uL", trak->start_offset);
3386
+ "start chunk offset:%O", trak->start_offset);
3387
+
3388
+ if (mp4->length) {
3389
+
3390
+ if (trak->end_chunk > trak->chunks) {
3391
+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3392
+ "end time is out mp4 co64 chunks in \"%s\"",
3393
+ mp4->file.name.data);
3394
+ return NGX_ERROR;
3395
+ }
3396
+
3397
+ entries = trak->end_chunk - trak->start_chunk;
3398
+ data->last = data->pos + entries * sizeof(uint64_t);
3399
+
3400
+ if (entries) {
3401
+ trak->end_offset =
3402
+ ngx_mp4_get_64value(data->last - sizeof(uint64_t));
3403
+ trak->end_offset += trak->end_chunk_samples_size;
3404
+
3405
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3406
+ "end chunk offset:%O", trak->end_offset);
3407
+ }
3408
+
3409
+ } else {
3410
+ entries = trak->chunks - trak->start_chunk;
3411
+ trak->end_offset = mp4->mdat_data.buf->file_last;
3412
+ }
3413
+
3414
+ if (entries == 0) {
3415
+ trak->start_offset = mp4->end;
3416
+ trak->end_offset = 0;
3417
+ }
3418
+
3419
+ atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
3420
+ trak->size += atom_size;
2949
3421
 
2950
3422
  atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
2951
3423
  co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
2952
3424
 
2953
3425
  ngx_mp4_set_32value(co64_atom->size, atom_size);
2954
- ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk);
3426
+ ngx_mp4_set_32value(co64_atom->entries, entries);
2955
3427
 
2956
3428
  return NGX_OK;
2957
3429
  }