comparison src/http/modules/ngx_http_mp4_module.c @ 5620:0a567878254b

Mp4: added "end" argument support.
author Roman Arutyunyan <arut@nginx.com>
date Thu, 20 Mar 2014 16:05:19 +0400
parents 517b5b599e3f
children 345e4fd4bb64
comparison
equal deleted inserted replaced
5619:517b5b599e3f 5620:0a567878254b
25 #define NGX_HTTP_MP4_STSS_ATOM 13 25 #define NGX_HTTP_MP4_STSS_ATOM 13
26 #define NGX_HTTP_MP4_STSS_DATA 14 26 #define NGX_HTTP_MP4_STSS_DATA 14
27 #define NGX_HTTP_MP4_CTTS_ATOM 15 27 #define NGX_HTTP_MP4_CTTS_ATOM 15
28 #define NGX_HTTP_MP4_CTTS_DATA 16 28 #define NGX_HTTP_MP4_CTTS_DATA 16
29 #define NGX_HTTP_MP4_STSC_ATOM 17 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 #define NGX_HTTP_MP4_STSC_DATA 19 31 #define NGX_HTTP_MP4_STSC_DATA 19
32 #define NGX_HTTP_MP4_STSZ_ATOM 20 32 #define NGX_HTTP_MP4_STSC_END 20
33 #define NGX_HTTP_MP4_STSZ_DATA 21 33 #define NGX_HTTP_MP4_STSZ_ATOM 21
34 #define NGX_HTTP_MP4_STCO_ATOM 22 34 #define NGX_HTTP_MP4_STSZ_DATA 22
35 #define NGX_HTTP_MP4_STCO_DATA 23 35 #define NGX_HTTP_MP4_STCO_ATOM 23
36 #define NGX_HTTP_MP4_CO64_ATOM 24 36 #define NGX_HTTP_MP4_STCO_DATA 24
37 #define NGX_HTTP_MP4_CO64_DATA 25 37 #define NGX_HTTP_MP4_CO64_ATOM 25
38 #define NGX_HTTP_MP4_CO64_DATA 26
38 39
39 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA 40 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
40 41
41 42
42 typedef struct { 43 typedef struct {
60 uint32_t composition_offset_entries; 61 uint32_t composition_offset_entries;
61 uint32_t sample_sizes_entries; 62 uint32_t sample_sizes_entries;
62 uint32_t chunks; 63 uint32_t chunks;
63 64
64 ngx_uint_t start_sample; 65 ngx_uint_t start_sample;
66 ngx_uint_t end_sample;
65 ngx_uint_t start_chunk; 67 ngx_uint_t start_chunk;
66 ngx_uint_t chunk_samples; 68 ngx_uint_t end_chunk;
67 uint64_t chunk_samples_size; 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 off_t start_offset; 73 off_t start_offset;
74 off_t end_offset;
69 75
70 size_t tkhd_size; 76 size_t tkhd_size;
71 size_t mdhd_size; 77 size_t mdhd_size;
72 size_t hdlr_size; 78 size_t hdlr_size;
73 size_t vmhd_size; 79 size_t vmhd_size;
93 ngx_buf_t stss_atom_buf; 99 ngx_buf_t stss_atom_buf;
94 ngx_buf_t stss_data_buf; 100 ngx_buf_t stss_data_buf;
95 ngx_buf_t ctts_atom_buf; 101 ngx_buf_t ctts_atom_buf;
96 ngx_buf_t ctts_data_buf; 102 ngx_buf_t ctts_data_buf;
97 ngx_buf_t stsc_atom_buf; 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 ngx_buf_t stsc_data_buf; 106 ngx_buf_t stsc_data_buf;
100 ngx_buf_t stsz_atom_buf; 107 ngx_buf_t stsz_atom_buf;
101 ngx_buf_t stsz_data_buf; 108 ngx_buf_t stsz_data_buf;
102 ngx_buf_t stco_atom_buf; 109 ngx_buf_t stco_atom_buf;
103 ngx_buf_t stco_data_buf; 110 ngx_buf_t stco_data_buf;
104 ngx_buf_t co64_atom_buf; 111 ngx_buf_t co64_atom_buf;
105 ngx_buf_t co64_data_buf; 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 } ngx_http_mp4_trak_t; 116 } ngx_http_mp4_trak_t;
109 117
110 118
111 typedef struct { 119 typedef struct {
112 ngx_file_t file; 120 ngx_file_t file;
119 127
120 off_t offset; 128 off_t offset;
121 off_t end; 129 off_t end;
122 off_t content_length; 130 off_t content_length;
123 ngx_uint_t start; 131 ngx_uint_t start;
132 ngx_uint_t length;
124 uint32_t timescale; 133 uint32_t timescale;
125 ngx_http_request_t *request; 134 ngx_http_request_t *request;
126 ngx_array_t trak; 135 ngx_array_t trak;
127 ngx_http_mp4_trak_t traks[2]; 136 ngx_http_mp4_trak_t traks[2];
128 137
217 static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, 226 static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,
218 uint64_t atom_data_size); 227 uint64_t atom_data_size);
219 static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, 228 static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,
220 uint64_t atom_data_size); 229 uint64_t atom_data_size);
221 static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, 230 static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,
222 off_t start_offset); 231 off_t start_offset, off_t end_offset);
223 static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, 232 static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,
224 uint64_t atom_data_size); 233 uint64_t atom_data_size);
225 static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, 234 static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,
226 uint64_t atom_data_size); 235 uint64_t atom_data_size);
227 static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4, 236 static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
257 static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, 266 static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,
258 uint64_t atom_data_size); 267 uint64_t atom_data_size);
259 static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4, 268 static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
260 ngx_http_mp4_trak_t *trak); 269 ngx_http_mp4_trak_t *trak);
261 static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4, 270 static ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
262 ngx_http_mp4_trak_t *trak); 271 ngx_http_mp4_trak_t *trak, ngx_uint_t start);
263 static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, 272 static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,
264 uint64_t atom_data_size); 273 uint64_t atom_data_size);
265 static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4, 274 static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
266 ngx_http_mp4_trak_t *trak); 275 ngx_http_mp4_trak_t *trak);
267 static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4, 276 static void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
268 ngx_http_mp4_trak_t *trak); 277 ngx_http_mp4_trak_t *trak, ngx_uint_t start);
269 static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, 278 static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,
270 uint64_t atom_data_size); 279 uint64_t atom_data_size);
271 static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4, 280 static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
272 ngx_http_mp4_trak_t *trak); 281 ngx_http_mp4_trak_t *trak);
273 static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4, 282 static void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
274 ngx_http_mp4_trak_t *trak); 283 ngx_http_mp4_trak_t *trak, ngx_uint_t start);
275 static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, 284 static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,
276 uint64_t atom_data_size); 285 uint64_t atom_data_size);
277 static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4, 286 static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
278 ngx_http_mp4_trak_t *trak); 287 ngx_http_mp4_trak_t *trak);
279 static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4, 288 static ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
280 ngx_http_mp4_trak_t *trak); 289 ngx_http_mp4_trak_t *trak, ngx_uint_t start);
281 static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, 290 static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,
282 uint64_t atom_data_size); 291 uint64_t atom_data_size);
283 static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, 292 static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
284 ngx_http_mp4_trak_t *trak); 293 ngx_http_mp4_trak_t *trak);
285 static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, 294 static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4,
409 static ngx_int_t 418 static ngx_int_t
410 ngx_http_mp4_handler(ngx_http_request_t *r) 419 ngx_http_mp4_handler(ngx_http_request_t *r)
411 { 420 {
412 u_char *last; 421 u_char *last;
413 size_t root; 422 size_t root;
414 ngx_int_t rc, start; 423 ngx_int_t rc, start, end;
415 ngx_uint_t level; 424 ngx_uint_t level, length;
416 ngx_str_t path, value; 425 ngx_str_t path, value;
417 ngx_log_t *log; 426 ngx_log_t *log;
418 ngx_buf_t *b; 427 ngx_buf_t *b;
419 ngx_chain_t out; 428 ngx_chain_t out;
420 ngx_http_mp4_file_t *mp4; 429 ngx_http_mp4_file_t *mp4;
515 524
516 r->root_tested = !r->error_page; 525 r->root_tested = !r->error_page;
517 r->allow_ranges = 1; 526 r->allow_ranges = 1;
518 527
519 start = -1; 528 start = -1;
529 length = 0;
520 r->headers_out.content_length_n = of.size; 530 r->headers_out.content_length_n = of.size;
521 mp4 = NULL; 531 mp4 = NULL;
522 b = NULL; 532 b = NULL;
523 533
524 if (r->args.len) { 534 if (r->args.len) {
536 546
537 if (ngx_errno != 0) { 547 if (ngx_errno != 0) {
538 start = -1; 548 start = -1;
539 } 549 }
540 } 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 }
565
566 if (end > start) {
567 length = end - start;
568 }
569 }
570 }
541 } 571 }
542 572
543 if (start >= 0) { 573 if (start >= 0) {
544 r->allow_ranges = 0; 574 r->allow_ranges = 0;
545 575
551 mp4->file.fd = of.fd; 581 mp4->file.fd = of.fd;
552 mp4->file.name = path; 582 mp4->file.name = path;
553 mp4->file.log = r->connection->log; 583 mp4->file.log = r->connection->log;
554 mp4->end = of.size; 584 mp4->end = of.size;
555 mp4->start = (ngx_uint_t) start; 585 mp4->start = (ngx_uint_t) start;
586 mp4->length = length;
556 mp4->request = r; 587 mp4->request = r;
557 588
558 switch (ngx_http_mp4_process(mp4)) { 589 switch (ngx_http_mp4_process(mp4)) {
559 590
560 case NGX_DECLINED: 591 case NGX_DECLINED:
656 687
657 688
658 static ngx_int_t 689 static ngx_int_t
659 ngx_http_mp4_process(ngx_http_mp4_file_t *mp4) 690 ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
660 { 691 {
661 off_t start_offset, adjustment; 692 off_t start_offset, end_offset, adjustment;
662 ngx_int_t rc; 693 ngx_int_t rc;
663 ngx_uint_t i, j; 694 ngx_uint_t i, j;
664 ngx_chain_t **prev; 695 ngx_chain_t **prev;
665 ngx_http_mp4_trak_t *trak; 696 ngx_http_mp4_trak_t *trak;
666 ngx_http_mp4_conf_t *conf; 697 ngx_http_mp4_conf_t *conf;
667 698
668 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 699 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
669 "mp4 start:%ui", mp4->start); 700 "mp4 start:%ui, length:%ui", mp4->start, mp4->length);
670 701
671 conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module); 702 conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
672 703
673 mp4->buffer_size = conf->buffer_size; 704 mp4->buffer_size = conf->buffer_size;
674 705
706 *prev = &mp4->mvhd_atom; 737 *prev = &mp4->mvhd_atom;
707 prev = &mp4->mvhd_atom.next; 738 prev = &mp4->mvhd_atom.next;
708 } 739 }
709 740
710 start_offset = mp4->end; 741 start_offset = mp4->end;
742 end_offset = 0;
711 trak = mp4->trak.elts; 743 trak = mp4->trak.elts;
712 744
713 for (i = 0; i < mp4->trak.nelts; i++) { 745 for (i = 0; i < mp4->trak.nelts; i++) {
714 746
715 if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) { 747 if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {
753 785
754 if (start_offset > trak[i].start_offset) { 786 if (start_offset > trak[i].start_offset) {
755 start_offset = trak[i].start_offset; 787 start_offset = trak[i].start_offset;
756 } 788 }
757 789
790 if (end_offset < trak[i].end_offset) {
791 end_offset = trak[i].end_offset;
792 }
793
758 *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM]; 794 *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
759 prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next; 795 prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;
760 796
761 for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) { 797 for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {
762 if (trak[i].out[j].buf) { 798 if (trak[i].out[j].buf) {
764 prev = &trak[i].out[j].next; 800 prev = &trak[i].out[j].next;
765 } 801 }
766 } 802 }
767 } 803 }
768 804
805 if (end_offset < start_offset) {
806 end_offset = start_offset;
807 }
808
769 mp4->moov_size += 8; 809 mp4->moov_size += 8;
770 810
771 ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size); 811 ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);
772 ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v'); 812 ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v');
773 mp4->content_length += mp4->moov_size; 813 mp4->content_length += mp4->moov_size;
780 mp4->file.name.data); 820 mp4->file.name.data);
781 return NGX_ERROR; 821 return NGX_ERROR;
782 } 822 }
783 823
784 adjustment = mp4->ftyp_size + mp4->moov_size 824 adjustment = mp4->ftyp_size + mp4->moov_size
785 + ngx_http_mp4_update_mdat_atom(mp4, start_offset) 825 + ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset)
786 - start_offset; 826 - start_offset;
787 827
788 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 828 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
789 "mp4 adjustment:%O", adjustment); 829 "mp4 adjustment:%O", adjustment);
790 830
1024 1064
1025 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom"); 1065 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom");
1026 1066
1027 no_mdat = (mp4->mdat_atom.buf == NULL); 1067 no_mdat = (mp4->mdat_atom.buf == NULL);
1028 1068
1029 if (no_mdat && mp4->start == 0) { 1069 if (no_mdat && mp4->start == 0 && mp4->length == 0) {
1030 /* 1070 /*
1031 * send original file if moov atom resides before 1071 * send original file if moov atom resides before
1032 * mdat atom and client requests integral file 1072 * mdat atom and client requests integral file
1033 */ 1073 */
1034 return NGX_DECLINED; 1074 return NGX_DECLINED;
1123 return NGX_OK; 1163 return NGX_OK;
1124 } 1164 }
1125 1165
1126 1166
1127 static size_t 1167 static size_t
1128 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)
1129 { 1170 {
1130 off_t atom_data_size; 1171 off_t atom_data_size;
1131 u_char *atom_header; 1172 u_char *atom_header;
1132 uint32_t atom_header_size; 1173 uint32_t atom_header_size;
1133 uint64_t atom_size; 1174 uint64_t atom_size;
1134 ngx_buf_t *atom; 1175 ngx_buf_t *atom;
1135 1176
1136 atom_data_size = mp4->mdat_data.buf->file_last - start_offset; 1177 atom_data_size = end_offset - start_offset;
1137 mp4->mdat_data.buf->file_pos = start_offset; 1178 mp4->mdat_data.buf->file_pos = start_offset;
1179 mp4->mdat_data.buf->file_last = end_offset;
1138 1180
1139 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 1181 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1140 "mdat new offset @%O:%O", start_offset, atom_data_size); 1182 "mdat new offset @%O:%O", start_offset, atom_data_size);
1141 1183
1142 atom_header = mp4->mdat_atom_header; 1184 atom_header = mp4->mdat_atom_header;
1214 ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) 1256 ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1215 { 1257 {
1216 u_char *atom_header; 1258 u_char *atom_header;
1217 size_t atom_size; 1259 size_t atom_size;
1218 uint32_t timescale; 1260 uint32_t timescale;
1219 uint64_t duration, start_time; 1261 uint64_t duration, start_time, length_time;
1220 ngx_buf_t *atom; 1262 ngx_buf_t *atom;
1221 ngx_mp4_mvhd_atom_t *mvhd_atom; 1263 ngx_mp4_mvhd_atom_t *mvhd_atom;
1222 ngx_mp4_mvhd64_atom_t *mvhd64_atom; 1264 ngx_mp4_mvhd64_atom_t *mvhd64_atom;
1223 1265
1224 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom"); 1266 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom");
1267 mp4->file.name.data); 1309 mp4->file.name.data);
1268 return NGX_ERROR; 1310 return NGX_ERROR;
1269 } 1311 }
1270 1312
1271 duration -= start_time; 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 }
1272 1322
1273 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 1323 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1274 "mvhd new duration:%uL, time:%.3fs", 1324 "mvhd new duration:%uL, time:%.3fs",
1275 duration, (double) duration / timescale); 1325 duration, (double) duration / timescale);
1276 1326
1413 static ngx_int_t 1463 static ngx_int_t
1414 ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) 1464 ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1415 { 1465 {
1416 u_char *atom_header; 1466 u_char *atom_header;
1417 size_t atom_size; 1467 size_t atom_size;
1418 uint64_t duration, start_time; 1468 uint64_t duration, start_time, length_time;
1419 ngx_buf_t *atom; 1469 ngx_buf_t *atom;
1420 ngx_http_mp4_trak_t *trak; 1470 ngx_http_mp4_trak_t *trak;
1421 ngx_mp4_tkhd_atom_t *tkhd_atom; 1471 ngx_mp4_tkhd_atom_t *tkhd_atom;
1422 ngx_mp4_tkhd64_atom_t *tkhd64_atom; 1472 ngx_mp4_tkhd64_atom_t *tkhd64_atom;
1423 1473
1462 "tkhd duration is less than start time"); 1512 "tkhd duration is less than start time");
1463 return NGX_DECLINED; 1513 return NGX_DECLINED;
1464 } 1514 }
1465 1515
1466 duration -= start_time; 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 }
1467 1525
1468 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 1526 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1469 "tkhd new duration:%uL, time:%.3fs", 1527 "tkhd new duration:%uL, time:%.3fs",
1470 duration, (double) duration / mp4->timescale); 1528 duration, (double) duration / mp4->timescale);
1471 1529
1564 ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) 1622 ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1565 { 1623 {
1566 u_char *atom_header; 1624 u_char *atom_header;
1567 size_t atom_size; 1625 size_t atom_size;
1568 uint32_t timescale; 1626 uint32_t timescale;
1569 uint64_t duration, start_time; 1627 uint64_t duration, start_time, length_time;
1570 ngx_buf_t *atom; 1628 ngx_buf_t *atom;
1571 ngx_http_mp4_trak_t *trak; 1629 ngx_http_mp4_trak_t *trak;
1572 ngx_mp4_mdhd_atom_t *mdhd_atom; 1630 ngx_mp4_mdhd_atom_t *mdhd_atom;
1573 ngx_mp4_mdhd64_atom_t *mdhd64_atom; 1631 ngx_mp4_mdhd64_atom_t *mdhd64_atom;
1574 1632
1615 "mdhd duration is less than start time"); 1673 "mdhd duration is less than start time");
1616 return NGX_DECLINED; 1674 return NGX_DECLINED;
1617 } 1675 }
1618 1676
1619 duration -= start_time; 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 }
1620 1686
1621 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 1687 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
1622 "mdhd new duration:%uL, time:%.3fs", 1688 "mdhd new duration:%uL, time:%.3fs",
1623 duration, (double) duration / timescale); 1689 duration, (double) duration / timescale);
1624 1690
2008 "no mp4 stts atoms were found in \"%s\"", 2074 "no mp4 stts atoms were found in \"%s\"",
2009 mp4->file.name.data); 2075 mp4->file.name.data);
2010 return NGX_ERROR; 2076 return NGX_ERROR;
2011 } 2077 }
2012 2078
2013 if (ngx_http_mp4_crop_stts_data(mp4, trak) != NGX_OK) { 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) {
2014 return NGX_ERROR; 2084 return NGX_ERROR;
2015 } 2085 }
2016 2086
2017 atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos); 2087 atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
2018 trak->size += atom_size; 2088 trak->size += atom_size;
2026 } 2096 }
2027 2097
2028 2098
2029 static ngx_int_t 2099 static ngx_int_t
2030 ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4, 2100 ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
2031 ngx_http_mp4_trak_t *trak) 2101 ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2032 { 2102 {
2033 uint32_t count, duration; 2103 uint32_t count, duration, rest;
2034 uint64_t start_time; 2104 uint64_t start_time;
2035 ngx_buf_t *data; 2105 ngx_buf_t *data;
2036 ngx_uint_t start_sample, entries; 2106 ngx_uint_t start_sample, entries, start_sec;
2037 ngx_mp4_stts_entry_t *entry, *end; 2107 ngx_mp4_stts_entry_t *entry, *end;
2038 2108
2109 if (start) {
2110 start_sec = mp4->start;
2111
2112 } else if (mp4->length) {
2113 start_sec = mp4->length;
2114
2115 } else {
2116 return NGX_OK;
2117 }
2118
2039 data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf; 2119 data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
2040 2120
2041 start_time = (uint64_t) mp4->start * trak->timescale / 1000; 2121 start_time = (uint64_t) start_sec * trak->timescale / 1000;
2042 2122
2043 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2123 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2044 "time-to-sample start_time:%uL", start_time); 2124 "time-to-sample start_time:%uL", start_time);
2045 2125
2046 entries = trak->time_to_sample_entries; 2126 entries = trak->time_to_sample_entries;
2055 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2135 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2056 "count:%uD, duration:%uD", count, duration); 2136 "count:%uD, duration:%uD", count, duration);
2057 2137
2058 if (start_time < (uint64_t) count * duration) { 2138 if (start_time < (uint64_t) count * duration) {
2059 start_sample += (ngx_uint_t) (start_time / duration); 2139 start_sample += (ngx_uint_t) (start_time / duration);
2060 count -= (uint32_t) (start_time / duration); 2140 rest = (uint32_t) (start_time / duration);
2061 ngx_mp4_set_32value(entry->count, count);
2062 goto found; 2141 goto found;
2063 } 2142 }
2064 2143
2065 start_sample += count; 2144 start_sample += count;
2066 start_time -= count * duration; 2145 start_time -= count * duration;
2077 found: 2156 found:
2078 2157
2079 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2158 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2080 "start_sample:%ui, new count:%uD", start_sample, count); 2159 "start_sample:%ui, new count:%uD", start_sample, count);
2081 2160
2082 trak->time_to_sample_entries = entries; 2161 if (start) {
2083 trak->start_sample = start_sample; 2162 ngx_mp4_set_32value(entry->count, count - rest);
2084 data->pos = (u_char *) entry; 2163 data->pos = (u_char *) entry;
2164 trak->time_to_sample_entries = entries;
2165 trak->start_sample = start_sample;
2166
2167 } else {
2168 ngx_mp4_set_32value(entry->count, rest);
2169 data->last = (u_char *) (entry + 1);
2170 trak->time_to_sample_entries -= entries - 1;
2171 trak->end_sample = trak->start_sample + start_sample;
2172 }
2085 2173
2086 return NGX_OK; 2174 return NGX_OK;
2087 } 2175 }
2088 2176
2089 2177
2180 2268
2181 if (data == NULL) { 2269 if (data == NULL) {
2182 return NGX_OK; 2270 return NGX_OK;
2183 } 2271 }
2184 2272
2185 ngx_http_mp4_crop_stss_data(mp4, trak); 2273 ngx_http_mp4_crop_stss_data(mp4, trak, 1);
2274 ngx_http_mp4_crop_stss_data(mp4, trak, 0);
2186 2275
2187 entry = (uint32_t *) data->pos; 2276 entry = (uint32_t *) data->pos;
2188 end = (uint32_t *) data->last; 2277 end = (uint32_t *) data->last;
2189 2278
2190 start_sample = trak->start_sample; 2279 start_sample = trak->start_sample;
2209 } 2298 }
2210 2299
2211 2300
2212 static void 2301 static void
2213 ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4, 2302 ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,
2214 ngx_http_mp4_trak_t *trak) 2303 ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2215 { 2304 {
2216 uint32_t sample, start_sample, *entry, *end; 2305 uint32_t sample, start_sample, *entry, *end;
2217 ngx_buf_t *data; 2306 ngx_buf_t *data;
2218 ngx_uint_t entries; 2307 ngx_uint_t entries;
2219 2308
2309 /* sync samples starts from 1 */
2310
2311 if (start) {
2312 start_sample = trak->start_sample + 1;
2313
2314 } else if (mp4->length) {
2315 start_sample = trak->end_sample + 1;
2316
2317 } else {
2318 return;
2319 }
2320
2220 data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf; 2321 data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
2221 2322
2222 /* sync samples starts from 1 */
2223 start_sample = trak->start_sample + 1;
2224 entries = trak->sync_samples_entries; 2323 entries = trak->sync_samples_entries;
2225
2226 entry = (uint32_t *) data->pos; 2324 entry = (uint32_t *) data->pos;
2227 end = (uint32_t *) data->last; 2325 end = (uint32_t *) data->last;
2228 2326
2229 while (entry < end) { 2327 while (entry < end) {
2230 sample = ngx_mp4_get_32value(entry); 2328 sample = ngx_mp4_get_32value(entry);
2243 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2341 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2244 "start sample is out of mp4 stss atom"); 2342 "start sample is out of mp4 stss atom");
2245 2343
2246 found: 2344 found:
2247 2345
2248 data->pos = (u_char *) entry; 2346 if (start) {
2249 trak->sync_samples_entries = entries; 2347 data->pos = (u_char *) entry;
2348 trak->sync_samples_entries = entries;
2349
2350 } else {
2351 data->last = (u_char *) entry;
2352 trak->sync_samples_entries -= entries;
2353 }
2250 } 2354 }
2251 2355
2252 2356
2253 typedef struct { 2357 typedef struct {
2254 u_char size[4]; 2358 u_char size[4];
2347 2451
2348 if (data == NULL) { 2452 if (data == NULL) {
2349 return; 2453 return;
2350 } 2454 }
2351 2455
2352 ngx_http_mp4_crop_ctts_data(mp4, trak); 2456 ngx_http_mp4_crop_ctts_data(mp4, trak, 1);
2457 ngx_http_mp4_crop_ctts_data(mp4, trak, 0);
2353 2458
2354 if (trak->composition_offset_entries == 0) { 2459 if (trak->composition_offset_entries == 0) {
2355 trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL; 2460 trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
2356 trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL; 2461 trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
2357 return; 2462 return;
2370 } 2475 }
2371 2476
2372 2477
2373 static void 2478 static void
2374 ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4, 2479 ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,
2375 ngx_http_mp4_trak_t *trak) 2480 ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2376 { 2481 {
2377 uint32_t count, start_sample; 2482 uint32_t count, start_sample, rest;
2378 ngx_buf_t *data; 2483 ngx_buf_t *data;
2379 ngx_uint_t entries; 2484 ngx_uint_t entries;
2380 ngx_mp4_ctts_entry_t *entry, *end; 2485 ngx_mp4_ctts_entry_t *entry, *end;
2381 2486
2487 /* sync samples starts from 1 */
2488
2489 if (start) {
2490 start_sample = trak->start_sample + 1;
2491
2492 } else if (mp4->length) {
2493 start_sample = trak->end_sample - trak->start_sample + 1;
2494
2495 } else {
2496 return;
2497 }
2498
2382 data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf; 2499 data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
2383 2500
2384 /* sync samples starts from 1 */
2385 start_sample = trak->start_sample + 1;
2386 entries = trak->composition_offset_entries; 2501 entries = trak->composition_offset_entries;
2387 entry = (ngx_mp4_ctts_entry_t *) data->pos; 2502 entry = (ngx_mp4_ctts_entry_t *) data->pos;
2388 end = (ngx_mp4_ctts_entry_t *) data->last; 2503 end = (ngx_mp4_ctts_entry_t *) data->last;
2389 2504
2390 while (entry < end) { 2505 while (entry < end) {
2393 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2508 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2394 "start:%uD, count:%uD, offset:%uD", 2509 "start:%uD, count:%uD, offset:%uD",
2395 start_sample, count, ngx_mp4_get_32value(entry->offset)); 2510 start_sample, count, ngx_mp4_get_32value(entry->offset));
2396 2511
2397 if (start_sample <= count) { 2512 if (start_sample <= count) {
2398 count -= (start_sample - 1); 2513 rest = start_sample - 1;
2399 ngx_mp4_set_32value(entry->count, count);
2400 goto found; 2514 goto found;
2401 } 2515 }
2402 2516
2403 start_sample -= count; 2517 start_sample -= count;
2404 entries--; 2518 entries--;
2405 entry++; 2519 entry++;
2406 } 2520 }
2407 2521
2408 data->pos = (u_char *) end; 2522 if (start) {
2409 trak->composition_offset_entries = 0; 2523 data->pos = (u_char *) end;
2524 trak->composition_offset_entries = 0;
2525 }
2410 2526
2411 return; 2527 return;
2412 2528
2413 found: 2529 found:
2414 2530
2415 data->pos = (u_char *) entry; 2531 if (start) {
2416 trak->composition_offset_entries = entries; 2532 ngx_mp4_set_32value(entry->count, count - rest);
2533 data->pos = (u_char *) entry;
2534 trak->composition_offset_entries = entries;
2535
2536 } else {
2537 ngx_mp4_set_32value(entry->count, rest);
2538 data->last = (u_char *) (entry + 1);
2539 trak->composition_offset_entries -= entries - 1;
2540 }
2417 } 2541 }
2418 2542
2419 2543
2420 typedef struct { 2544 typedef struct {
2421 u_char size[4]; 2545 u_char size[4];
2520 "zero number of entries in stsc atom in \"%s\"", 2644 "zero number of entries in stsc atom in \"%s\"",
2521 mp4->file.name.data); 2645 mp4->file.name.data);
2522 return NGX_ERROR; 2646 return NGX_ERROR;
2523 } 2647 }
2524 2648
2525 if (ngx_http_mp4_crop_stsc_data(mp4, trak) != NGX_OK) { 2649 if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) {
2650 return NGX_ERROR;
2651 }
2652
2653 if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) {
2526 return NGX_ERROR; 2654 return NGX_ERROR;
2527 } 2655 }
2528 2656
2529 entry = (ngx_mp4_stsc_entry_t *) data->pos; 2657 entry = (ngx_mp4_stsc_entry_t *) data->pos;
2530 end = (ngx_mp4_stsc_entry_t *) data->last; 2658 end = (ngx_mp4_stsc_entry_t *) data->last;
2551 } 2679 }
2552 2680
2553 2681
2554 static ngx_int_t 2682 static ngx_int_t
2555 ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4, 2683 ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
2556 ngx_http_mp4_trak_t *trak) 2684 ngx_http_mp4_trak_t *trak, ngx_uint_t start)
2557 { 2685 {
2558 uint32_t start_sample, chunk, samples, id, next_chunk, n; 2686 uint32_t start_sample, chunk, samples, id, next_chunk, n,
2687 prev_samples;
2559 ngx_buf_t *data, *buf; 2688 ngx_buf_t *data, *buf;
2560 ngx_uint_t entries, target_chunk, chunk_samples; 2689 ngx_uint_t entries, target_chunk, chunk_samples;
2561 ngx_mp4_stsc_entry_t *entry, *end, *first; 2690 ngx_mp4_stsc_entry_t *entry, *end, *first;
2562 2691
2692 entries = trak->sample_to_chunk_entries - 1;
2693
2694 if (start) {
2695 start_sample = (uint32_t) trak->start_sample;
2696
2697 } else if (mp4->length) {
2698 start_sample = (uint32_t) (trak->end_sample - trak->start_sample);
2699
2700 data = trak->out[NGX_HTTP_MP4_STSC_START].buf;
2701
2702 if (data) {
2703 entry = (ngx_mp4_stsc_entry_t *) data->pos;
2704 samples = ngx_mp4_get_32value(entry->samples);
2705 entries--;
2706
2707 if (samples > start_sample) {
2708 samples = start_sample;
2709 ngx_mp4_set_32value(entry->samples, samples);
2710 }
2711
2712 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2713 "mp4 stsc using %uD start samples", samples);
2714
2715 start_sample -= samples;
2716 }
2717
2718 } else {
2719 return NGX_OK;
2720 }
2721
2563 data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf; 2722 data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
2564
2565 start_sample = (uint32_t) trak->start_sample;
2566 entries = trak->sample_to_chunk_entries - 1;
2567 2723
2568 entry = (ngx_mp4_stsc_entry_t *) data->pos; 2724 entry = (ngx_mp4_stsc_entry_t *) data->pos;
2569 end = (ngx_mp4_stsc_entry_t *) data->last; 2725 end = (ngx_mp4_stsc_entry_t *) data->last;
2570 2726
2571 chunk = ngx_mp4_get_32value(entry->chunk); 2727 chunk = ngx_mp4_get_32value(entry->chunk);
2572 samples = ngx_mp4_get_32value(entry->samples); 2728 samples = ngx_mp4_get_32value(entry->samples);
2573 id = ngx_mp4_get_32value(entry->id); 2729 id = ngx_mp4_get_32value(entry->id);
2730 prev_samples = 0;
2574 entry++; 2731 entry++;
2575 2732
2576 while (entry < end) { 2733 while (entry < end) {
2577 2734
2578 next_chunk = ngx_mp4_get_32value(entry->chunk); 2735 next_chunk = ngx_mp4_get_32value(entry->chunk);
2588 goto found; 2745 goto found;
2589 } 2746 }
2590 2747
2591 start_sample -= n; 2748 start_sample -= n;
2592 2749
2750 prev_samples = samples;
2593 chunk = next_chunk; 2751 chunk = next_chunk;
2594 samples = ngx_mp4_get_32value(entry->samples); 2752 samples = ngx_mp4_get_32value(entry->samples);
2595 id = ngx_mp4_get_32value(entry->id); 2753 id = ngx_mp4_get_32value(entry->id);
2596 entries--; 2754 entries--;
2597 entry++; 2755 entry++;
2630 2788
2631 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2789 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2632 "start chunk:%ui, samples:%uD", 2790 "start chunk:%ui, samples:%uD",
2633 target_chunk, chunk_samples); 2791 target_chunk, chunk_samples);
2634 2792
2635 data->pos = (u_char *) entry; 2793 if (start) {
2636 2794 data->pos = (u_char *) entry;
2637 trak->start_chunk = target_chunk; 2795
2638 trak->chunk_samples = chunk_samples; 2796 trak->sample_to_chunk_entries = entries;
2639 2797 trak->start_chunk = target_chunk;
2640 ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1); 2798 trak->start_chunk_samples = chunk_samples;
2641 2799
2642 samples -= chunk_samples; 2800 ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1);
2801
2802 samples -= chunk_samples;
2803
2804 } else {
2805 if (start_sample) {
2806 data->last = (u_char *) (entry + 1);
2807 trak->sample_to_chunk_entries -= entries - 1;
2808 trak->end_chunk_samples = samples;
2809
2810 } else {
2811 data->last = (u_char *) entry;
2812 trak->sample_to_chunk_entries -= entries;
2813 trak->end_chunk_samples = prev_samples;
2814 }
2815
2816 if (chunk_samples) {
2817 trak->end_chunk = target_chunk + 1;
2818 trak->end_chunk_samples = chunk_samples;
2819
2820 } else {
2821 trak->end_chunk = target_chunk;
2822 }
2823
2824 samples = chunk_samples;
2825 next_chunk = chunk + 1;
2826 }
2643 2827
2644 if (chunk_samples && next_chunk - target_chunk == 2) { 2828 if (chunk_samples && next_chunk - target_chunk == 2) {
2645 2829
2646 ngx_mp4_set_32value(entry->samples, samples); 2830 ngx_mp4_set_32value(entry->samples, samples);
2647 2831
2648 } else if (chunk_samples) { 2832 } else if (chunk_samples && start) {
2649 2833
2650 first = &trak->stsc_chunk_entry; 2834 first = &trak->stsc_start_chunk_entry;
2651 ngx_mp4_set_32value(first->chunk, 1); 2835 ngx_mp4_set_32value(first->chunk, 1);
2652 ngx_mp4_set_32value(first->samples, samples); 2836 ngx_mp4_set_32value(first->samples, samples);
2653 ngx_mp4_set_32value(first->id, id); 2837 ngx_mp4_set_32value(first->id, id);
2654 2838
2655 buf = &trak->stsc_chunk_buf; 2839 buf = &trak->stsc_start_chunk_buf;
2656 buf->temporary = 1; 2840 buf->temporary = 1;
2657 buf->pos = (u_char *) first; 2841 buf->pos = (u_char *) first;
2658 buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t); 2842 buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2659 2843
2660 trak->out[NGX_HTTP_MP4_STSC_CHUNK].buf = buf; 2844 trak->out[NGX_HTTP_MP4_STSC_START].buf = buf;
2661 2845
2662 ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2); 2846 ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2);
2663 2847
2664 entries++; 2848 trak->sample_to_chunk_entries++;
2665 } 2849
2666 2850 } else if (chunk_samples) {
2667 trak->sample_to_chunk_entries = entries; 2851
2852 first = &trak->stsc_end_chunk_entry;
2853 ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk);
2854 ngx_mp4_set_32value(first->samples, samples);
2855 ngx_mp4_set_32value(first->id, id);
2856
2857 buf = &trak->stsc_end_chunk_buf;
2858 buf->temporary = 1;
2859 buf->pos = (u_char *) first;
2860 buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
2861
2862 trak->out[NGX_HTTP_MP4_STSC_END].buf = buf;
2863
2864 trak->sample_to_chunk_entries++;
2865 }
2668 2866
2669 return NGX_OK; 2867 return NGX_OK;
2670 } 2868 }
2671 2869
2672 2870
2758 static ngx_int_t 2956 static ngx_int_t
2759 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4, 2957 ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
2760 ngx_http_mp4_trak_t *trak) 2958 ngx_http_mp4_trak_t *trak)
2761 { 2959 {
2762 size_t atom_size; 2960 size_t atom_size;
2763 uint32_t *pos, *end; 2961 uint32_t *pos, *end, entries;
2764 ngx_buf_t *atom, *data; 2962 ngx_buf_t *atom, *data;
2765 ngx_mp4_stsz_atom_t *stsz_atom; 2963 ngx_mp4_stsz_atom_t *stsz_atom;
2766 2964
2767 /* 2965 /*
2768 * mdia.minf.stbl.stsz updating requires trak->start_sample 2966 * mdia.minf.stbl.stsz updating requires trak->start_sample
2774 "mp4 stsz atom update"); 2972 "mp4 stsz atom update");
2775 2973
2776 data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf; 2974 data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
2777 2975
2778 if (data) { 2976 if (data) {
2779 if (trak->start_sample > trak->sample_sizes_entries) { 2977 entries = trak->sample_sizes_entries;
2978
2979 if (trak->start_sample > entries) {
2780 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, 2980 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2781 "start time is out mp4 stsz samples in \"%s\"", 2981 "start time is out mp4 stsz samples in \"%s\"",
2782 mp4->file.name.data); 2982 mp4->file.name.data);
2783 return NGX_ERROR; 2983 return NGX_ERROR;
2784 } 2984 }
2785 2985
2986 entries -= trak->start_sample;
2786 data->pos += trak->start_sample * sizeof(uint32_t); 2987 data->pos += trak->start_sample * sizeof(uint32_t);
2787 end = (uint32_t *) data->pos; 2988 end = (uint32_t *) data->pos;
2788 2989
2789 for (pos = end - trak->chunk_samples; pos < end; pos++) { 2990 for (pos = end - trak->start_chunk_samples; pos < end; pos++) {
2790 trak->chunk_samples_size += ngx_mp4_get_32value(pos); 2991 trak->start_chunk_samples_size += ngx_mp4_get_32value(pos);
2791 } 2992 }
2792 2993
2793 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2994 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2794 "chunk samples sizes:%uL", trak->chunk_samples_size); 2995 "chunk samples sizes:%uL",
2996 trak->start_chunk_samples_size);
2997
2998 if (mp4->length) {
2999 if (trak->end_sample - trak->start_sample > entries) {
3000 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3001 "end time is out mp4 stsz samples in \"%s\"",
3002 mp4->file.name.data);
3003 return NGX_ERROR;
3004 }
3005
3006 entries = trak->end_sample - trak->start_sample;
3007 data->last = data->pos + entries * sizeof(uint32_t);
3008 end = (uint32_t *) data->last;
3009
3010 for (pos = end - trak->end_chunk_samples; pos < end; pos++) {
3011 trak->end_chunk_samples_size += ngx_mp4_get_32value(pos);
3012 }
3013
3014 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3015 "mp4 stsz end_chunk_samples_size:%uL",
3016 trak->end_chunk_samples_size);
3017 }
2795 3018
2796 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos); 3019 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
2797 trak->size += atom_size; 3020 trak->size += atom_size;
2798 3021
2799 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf; 3022 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
2800 stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos; 3023 stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
2801 3024
2802 ngx_mp4_set_32value(stsz_atom->size, atom_size); 3025 ngx_mp4_set_32value(stsz_atom->size, atom_size);
2803 ngx_mp4_set_32value(stsz_atom->entries, 3026 ngx_mp4_set_32value(stsz_atom->entries, entries);
2804 trak->sample_sizes_entries - trak->start_sample);
2805 } 3027 }
2806 3028
2807 return NGX_OK; 3029 return NGX_OK;
2808 } 3030 }
2809 3031
2880 static ngx_int_t 3102 static ngx_int_t
2881 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, 3103 ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
2882 ngx_http_mp4_trak_t *trak) 3104 ngx_http_mp4_trak_t *trak)
2883 { 3105 {
2884 size_t atom_size; 3106 size_t atom_size;
3107 uint32_t entries;
2885 ngx_buf_t *atom, *data; 3108 ngx_buf_t *atom, *data;
2886 ngx_mp4_stco_atom_t *stco_atom; 3109 ngx_mp4_stco_atom_t *stco_atom;
2887 3110
2888 /* 3111 /*
2889 * mdia.minf.stbl.stco updating requires trak->start_chunk 3112 * mdia.minf.stbl.stco updating requires trak->start_chunk
2909 mp4->file.name.data); 3132 mp4->file.name.data);
2910 return NGX_ERROR; 3133 return NGX_ERROR;
2911 } 3134 }
2912 3135
2913 data->pos += trak->start_chunk * sizeof(uint32_t); 3136 data->pos += trak->start_chunk * sizeof(uint32_t);
3137
3138 trak->start_offset = ngx_mp4_get_32value(data->pos);
3139 trak->start_offset += trak->start_chunk_samples_size;
3140 ngx_mp4_set_32value(data->pos, trak->start_offset);
3141
3142 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3143 "start chunk offset:%uD", trak->start_offset);
3144
3145 if (mp4->length) {
3146
3147 if (trak->end_chunk > trak->chunks) {
3148 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3149 "end time is out mp4 stco chunks in \"%s\"",
3150 mp4->file.name.data);
3151 return NGX_ERROR;
3152 }
3153
3154 entries = trak->end_chunk - trak->start_chunk;
3155 data->last = data->pos + entries * sizeof(uint32_t);
3156
3157 if (entries) {
3158 trak->end_offset =
3159 ngx_mp4_get_32value(data->last - sizeof(uint32_t));
3160 trak->end_offset += trak->end_chunk_samples_size;
3161
3162 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3163 "end chunk offset:%O", trak->end_offset);
3164 }
3165
3166 } else {
3167 entries = trak->chunks - trak->start_chunk;
3168 trak->end_offset = mp4->mdat_data.buf->file_last;
3169 }
3170
3171 if (entries == 0) {
3172 trak->start_offset = mp4->end;
3173 trak->end_offset = 0;
3174 }
3175
2914 atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos); 3176 atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);
2915 trak->size += atom_size; 3177 trak->size += atom_size;
2916 3178
2917 trak->start_offset = ngx_mp4_get_32value(data->pos);
2918 trak->start_offset += trak->chunk_samples_size;
2919 ngx_mp4_set_32value(data->pos, trak->start_offset);
2920
2921 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2922 "start chunk offset:%uD", trak->start_offset);
2923
2924 atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf; 3179 atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
2925 stco_atom = (ngx_mp4_stco_atom_t *) atom->pos; 3180 stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
2926 3181
2927 ngx_mp4_set_32value(stco_atom->size, atom_size); 3182 ngx_mp4_set_32value(stco_atom->size, atom_size);
2928 ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk); 3183 ngx_mp4_set_32value(stco_atom->entries, entries);
2929 3184
2930 return NGX_OK; 3185 return NGX_OK;
2931 } 3186 }
2932 3187
2933 3188
3031 static ngx_int_t 3286 static ngx_int_t
3032 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4, 3287 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
3033 ngx_http_mp4_trak_t *trak) 3288 ngx_http_mp4_trak_t *trak)
3034 { 3289 {
3035 size_t atom_size; 3290 size_t atom_size;
3291 uint64_t entries;
3036 ngx_buf_t *atom, *data; 3292 ngx_buf_t *atom, *data;
3037 ngx_mp4_co64_atom_t *co64_atom; 3293 ngx_mp4_co64_atom_t *co64_atom;
3038 3294
3039 /* 3295 /*
3040 * mdia.minf.stbl.co64 updating requires trak->start_chunk 3296 * mdia.minf.stbl.co64 updating requires trak->start_chunk
3060 mp4->file.name.data); 3316 mp4->file.name.data);
3061 return NGX_ERROR; 3317 return NGX_ERROR;
3062 } 3318 }
3063 3319
3064 data->pos += trak->start_chunk * sizeof(uint64_t); 3320 data->pos += trak->start_chunk * sizeof(uint64_t);
3321
3322 trak->start_offset = ngx_mp4_get_64value(data->pos);
3323 trak->start_offset += trak->start_chunk_samples_size;
3324 ngx_mp4_set_64value(data->pos, trak->start_offset);
3325
3326 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3327 "start chunk offset:%uL", trak->start_offset);
3328
3329 if (mp4->length) {
3330
3331 if (trak->end_chunk > trak->chunks) {
3332 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
3333 "end time is out mp4 co64 chunks in \"%s\"",
3334 mp4->file.name.data);
3335 return NGX_ERROR;
3336 }
3337
3338 entries = trak->end_chunk - trak->start_chunk;
3339 data->last = data->pos + entries * sizeof(uint64_t);
3340
3341 if (entries) {
3342 trak->end_offset =
3343 ngx_mp4_get_64value(data->last - sizeof(uint64_t));
3344 trak->end_offset += trak->end_chunk_samples_size;
3345
3346 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3347 "end chunk offset:%O", trak->end_offset);
3348 }
3349
3350 } else {
3351 entries = trak->chunks - trak->start_chunk;
3352 trak->end_offset = mp4->mdat_data.buf->file_last;
3353 }
3354
3355 if (entries == 0) {
3356 trak->start_offset = mp4->end;
3357 trak->end_offset = 0;
3358 }
3359
3065 atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos); 3360 atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
3066 trak->size += atom_size; 3361 trak->size += atom_size;
3067 3362
3068 trak->start_offset = ngx_mp4_get_64value(data->pos);
3069 trak->start_offset += trak->chunk_samples_size;
3070 ngx_mp4_set_64value(data->pos, trak->start_offset);
3071
3072 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
3073 "start chunk offset:%uL", trak->start_offset);
3074
3075 atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf; 3363 atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
3076 co64_atom = (ngx_mp4_co64_atom_t *) atom->pos; 3364 co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
3077 3365
3078 ngx_mp4_set_32value(co64_atom->size, atom_size); 3366 ngx_mp4_set_32value(co64_atom->size, atom_size);
3079 ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk); 3367 ngx_mp4_set_32value(co64_atom->entries, entries);
3080 3368
3081 return NGX_OK; 3369 return NGX_OK;
3082 } 3370 }
3083 3371
3084 3372