comparison src/http/modules/ngx_http_mp4_module.c @ 640:eb208e0cf44d NGINX_1_1_4

nginx 1.1.4 *) Feature: the ngx_http_upstream_keepalive module. *) Feature: the "proxy_http_version" directive. *) Feature: the "fastcgi_keep_conn" directive. *) Feature: the "worker_aio_requests" directive. *) Bugfix: if nginx was built --with-file-aio it could not be run on Linux kernel which did not support AIO. *) Bugfix: in Linux AIO error processing. Thanks to Hagai Avrahami. *) Bugfix: reduced memory consumption for long-lived requests. *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4 "co64" atom.
author Igor Sysoev <http://sysoev.ru>
date Tue, 20 Sep 2011 00:00:00 +0400
parents f5a8cf31a203
children d3cf6c6b0043
comparison
equal deleted inserted replaced
639:b516b4e38bc9 640:eb208e0cf44d
30 #define NGX_HTTP_MP4_STSC_DATA 19 30 #define NGX_HTTP_MP4_STSC_DATA 19
31 #define NGX_HTTP_MP4_STSZ_ATOM 20 31 #define NGX_HTTP_MP4_STSZ_ATOM 20
32 #define NGX_HTTP_MP4_STSZ_DATA 21 32 #define NGX_HTTP_MP4_STSZ_DATA 21
33 #define NGX_HTTP_MP4_STCO_ATOM 22 33 #define NGX_HTTP_MP4_STCO_ATOM 22
34 #define NGX_HTTP_MP4_STCO_DATA 23 34 #define NGX_HTTP_MP4_STCO_DATA 23
35 35 #define NGX_HTTP_MP4_CO64_ATOM 24
36 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_STCO_DATA 36 #define NGX_HTTP_MP4_CO64_DATA 25
37
38 #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
37 39
38 40
39 typedef struct { 41 typedef struct {
40 size_t buffer_size; 42 size_t buffer_size;
41 size_t max_buffer_size; 43 size_t max_buffer_size;
59 uint32_t chunks; 61 uint32_t chunks;
60 62
61 ngx_uint_t start_sample; 63 ngx_uint_t start_sample;
62 ngx_uint_t start_chunk; 64 ngx_uint_t start_chunk;
63 ngx_uint_t chunk_samples; 65 ngx_uint_t chunk_samples;
64 ngx_uint_t chunk_samples_size; 66 uint64_t chunk_samples_size;
65 off_t start_offset; 67 off_t start_offset;
66 68
67 size_t tkhd_size; 69 size_t tkhd_size;
68 size_t mdhd_size; 70 size_t mdhd_size;
69 size_t hdlr_size; 71 size_t hdlr_size;
94 ngx_buf_t stsc_atom_buf; 96 ngx_buf_t stsc_atom_buf;
95 ngx_buf_t stsc_chunk_buf; 97 ngx_buf_t stsc_chunk_buf;
96 ngx_buf_t stsc_data_buf; 98 ngx_buf_t stsc_data_buf;
97 ngx_buf_t stsz_atom_buf; 99 ngx_buf_t stsz_atom_buf;
98 ngx_buf_t stsz_data_buf; 100 ngx_buf_t stsz_data_buf;
99 ngx_buf_t tsco_atom_buf; 101 ngx_buf_t stco_atom_buf;
100 ngx_buf_t tsco_data_buf; 102 ngx_buf_t stco_data_buf;
103 ngx_buf_t co64_atom_buf;
104 ngx_buf_t co64_data_buf;
101 105
102 ngx_mp4_stsc_entry_t stsc_chunk_entry; 106 ngx_mp4_stsc_entry_t stsc_chunk_entry;
103 } ngx_http_mp4_trak_t; 107 } ngx_http_mp4_trak_t;
104 108
105 109
266 uint64_t atom_data_size); 270 uint64_t atom_data_size);
267 static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4, 271 static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
268 ngx_http_mp4_trak_t *trak); 272 ngx_http_mp4_trak_t *trak);
269 static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4, 273 static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
270 ngx_http_mp4_trak_t *trak, int32_t adjustment); 274 ngx_http_mp4_trak_t *trak, int32_t adjustment);
275 static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,
276 uint64_t atom_data_size);
277 static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
278 ngx_http_mp4_trak_t *trak);
279 static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
280 ngx_http_mp4_trak_t *trak, off_t adjustment);
271 static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 281 static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
272 static void *ngx_http_mp4_create_conf(ngx_conf_t *cf); 282 static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);
273 static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child); 283 static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);
274 284
275 static ngx_command_t ngx_http_mp4_commands[] = { 285 static ngx_command_t ngx_http_mp4_commands[] = {
371 { "stss", ngx_http_mp4_read_stss_atom }, 381 { "stss", ngx_http_mp4_read_stss_atom },
372 { "ctts", ngx_http_mp4_read_ctts_atom }, 382 { "ctts", ngx_http_mp4_read_ctts_atom },
373 { "stsc", ngx_http_mp4_read_stsc_atom }, 383 { "stsc", ngx_http_mp4_read_stsc_atom },
374 { "stsz", ngx_http_mp4_read_stsz_atom }, 384 { "stsz", ngx_http_mp4_read_stsz_atom },
375 { "stco", ngx_http_mp4_read_stco_atom }, 385 { "stco", ngx_http_mp4_read_stco_atom },
386 { "co64", ngx_http_mp4_read_co64_atom },
376 { NULL, NULL } 387 { NULL, NULL }
377 }; 388 };
378 389
379 390
380 static ngx_int_t 391 static ngx_int_t
674 return NGX_ERROR; 685 return NGX_ERROR;
675 } 686 }
676 687
677 ngx_http_mp4_update_stsz_atom(mp4, &trak[i]); 688 ngx_http_mp4_update_stsz_atom(mp4, &trak[i]);
678 689
679 if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) { 690 if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
680 return NGX_ERROR; 691 if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
692 return NGX_ERROR;
693 }
694
695 } else {
696 if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
697 return NGX_ERROR;
698 }
681 } 699 }
682 700
683 ngx_http_mp4_update_stbl_atom(mp4, &trak[i]); 701 ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
684 ngx_http_mp4_update_minf_atom(mp4, &trak[i]); 702 ngx_http_mp4_update_minf_atom(mp4, &trak[i]);
685 trak[i].size += trak[i].mdhd_size; 703 trak[i].size += trak[i].mdhd_size;
719 737
720 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 738 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
721 "mp4 adjustment:%D", adjustment); 739 "mp4 adjustment:%D", adjustment);
722 740
723 for (i = 0; i < mp4->trak.nelts; i++) { 741 for (i = 0; i < mp4->trak.nelts; i++) {
724 ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment); 742 if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
743 ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);
744 } else {
745 ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
746 }
725 } 747 }
726 748
727 return NGX_OK; 749 return NGX_OK;
728 } 750 }
729 751
1700 static ngx_int_t 1722 static ngx_int_t
1701 ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size) 1723 ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
1702 { 1724 {
1703 u_char *atom_header, *atom_table; 1725 u_char *atom_header, *atom_table;
1704 size_t atom_size; 1726 size_t atom_size;
1705 uint32_t entries;
1706 ngx_buf_t *atom; 1727 ngx_buf_t *atom;
1707 ngx_mp4_stsd_atom_t *stsd_atom; 1728 ngx_mp4_stsd_atom_t *stsd_atom;
1708 ngx_http_mp4_trak_t *trak; 1729 ngx_http_mp4_trak_t *trak;
1709 1730
1710 /* sample description atom */ 1731 /* sample description atom */
1716 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size; 1737 atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
1717 atom_table = atom_header + atom_size; 1738 atom_table = atom_header + atom_size;
1718 ngx_mp4_set_32value(stsd_atom->size, atom_size); 1739 ngx_mp4_set_32value(stsd_atom->size, atom_size);
1719 ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd'); 1740 ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');
1720 1741
1721 entries = ngx_mp4_get_32value(stsd_atom->entries);
1722
1723 if ((uint64_t) (sizeof(ngx_mp4_stsd_atom_t) - sizeof(ngx_mp4_atom_header_t)) 1742 if ((uint64_t) (sizeof(ngx_mp4_stsd_atom_t) - sizeof(ngx_mp4_atom_header_t))
1724 > atom_data_size) { 1743 > atom_data_size)
1744 {
1725 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, 1745 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
1726 "\"%s\" mp4 stsd atom too large", 1746 "\"%s\" mp4 stsd atom too large",
1727 mp4->file.name.data); 1747 mp4->file.name.data);
1728 return NGX_ERROR; 1748 return NGX_ERROR;
1729 } 1749 }
2495 for (pos = end - trak->chunk_samples; pos < end; pos++) { 2515 for (pos = end - trak->chunk_samples; pos < end; pos++) {
2496 trak->chunk_samples_size += ngx_mp4_get_32value(pos); 2516 trak->chunk_samples_size += ngx_mp4_get_32value(pos);
2497 } 2517 }
2498 2518
2499 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, 2519 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2500 "chunk samples sizes:%uD", trak->chunk_samples_size); 2520 "chunk samples sizes:%uL", trak->chunk_samples_size);
2501 2521
2502 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos); 2522 atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
2503 trak->size += atom_size; 2523 trak->size += atom_size;
2504 2524
2505 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf; 2525 atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
2552 } 2572 }
2553 2573
2554 trak = ngx_mp4_last_trak(mp4); 2574 trak = ngx_mp4_last_trak(mp4);
2555 trak->chunks = entries; 2575 trak->chunks = entries;
2556 2576
2557 atom = &trak->tsco_atom_buf; 2577 atom = &trak->stco_atom_buf;
2558 atom->temporary = 1; 2578 atom->temporary = 1;
2559 atom->pos = atom_header; 2579 atom->pos = atom_header;
2560 atom->last = atom_table; 2580 atom->last = atom_table;
2561 2581
2562 data = &trak->tsco_data_buf; 2582 data = &trak->stco_data_buf;
2563 data->temporary = 1; 2583 data->temporary = 1;
2564 data->pos = atom_table; 2584 data->pos = atom_table;
2565 data->last = atom_end; 2585 data->last = atom_end;
2566 2586
2567 trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom; 2587 trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;
2646 entry++; 2666 entry++;
2647 } 2667 }
2648 } 2668 }
2649 2669
2650 2670
2671 typedef struct {
2672 u_char size[4];
2673 u_char name[4];
2674 u_char version[1];
2675 u_char flags[3];
2676 u_char entries[4];
2677 } ngx_mp4_co64_atom_t;
2678
2679
2680 static ngx_int_t
2681 ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
2682 {
2683 u_char *atom_header, *atom_table, *atom_end;
2684 uint32_t entries;
2685 ngx_buf_t *atom, *data;
2686 ngx_mp4_co64_atom_t *co64_atom;
2687 ngx_http_mp4_trak_t *trak;
2688
2689 /* chunk offsets atom */
2690
2691 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");
2692
2693 atom_header = ngx_mp4_atom_header(mp4);
2694 co64_atom = (ngx_mp4_co64_atom_t *) atom_header;
2695 ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');
2696
2697 entries = ngx_mp4_get_32value(co64_atom->entries);
2698
2699 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
2700
2701 atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);
2702 atom_end = atom_table + entries * sizeof(uint64_t);
2703
2704 if ((uint64_t) (atom_end - co64_atom->version) > atom_data_size) {
2705 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2706 "\"%s\" mp4 co64 atom too large", mp4->file.name.data);
2707 return NGX_ERROR;
2708 }
2709
2710 trak = ngx_mp4_last_trak(mp4);
2711 trak->chunks = entries;
2712
2713 atom = &trak->co64_atom_buf;
2714 atom->temporary = 1;
2715 atom->pos = atom_header;
2716 atom->last = atom_table;
2717
2718 data = &trak->co64_data_buf;
2719 data->temporary = 1;
2720 data->pos = atom_table;
2721 data->last = atom_end;
2722
2723 trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;
2724 trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;
2725
2726 ngx_mp4_atom_next(mp4, atom_data_size);
2727
2728 return NGX_OK;
2729 }
2730
2731
2732 static ngx_int_t
2733 ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
2734 ngx_http_mp4_trak_t *trak)
2735 {
2736 size_t atom_size;
2737 ngx_buf_t *atom, *data;
2738 ngx_mp4_co64_atom_t *co64_atom;
2739
2740 /*
2741 * mdia.minf.stbl.co64 updating requires trak->start_chunk
2742 * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
2743 * atom which may reside after mdia.minf
2744 */
2745
2746 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2747 "mp4 co64 atom update");
2748
2749 data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
2750
2751 if (data == NULL) {
2752 ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
2753 "no mp4 co64 atoms were found in \"%s\"",
2754 mp4->file.name.data);
2755 return NGX_ERROR;
2756 }
2757
2758 data->pos += trak->start_chunk * sizeof(uint64_t);
2759 atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);
2760 trak->size += atom_size;
2761
2762 trak->start_offset = ngx_mp4_get_64value(data->pos);
2763 trak->start_offset += trak->chunk_samples_size;
2764 ngx_mp4_set_64value(data->pos, trak->start_offset);
2765
2766 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2767 "start chunk offset:%uL", trak->start_offset);
2768
2769 atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
2770 co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
2771
2772 ngx_mp4_set_32value(co64_atom->size, atom_size);
2773 ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk);
2774
2775 return NGX_OK;
2776 }
2777
2778
2779 static void
2780 ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
2781 ngx_http_mp4_trak_t *trak, off_t adjustment)
2782 {
2783 uint64_t offset, *entry, *end;
2784 ngx_buf_t *data;
2785
2786 /*
2787 * moov.trak.mdia.minf.stbl.co64 adjustment requires
2788 * minimal start offset of all traks and new moov atom size
2789 */
2790
2791 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
2792 "mp4 co64 atom adjustment");
2793
2794 data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
2795 entry = (uint64_t *) data->pos;
2796 end = (uint64_t *) data->last;
2797
2798 while (entry < end) {
2799 offset = ngx_mp4_get_64value(entry);
2800 offset += adjustment;
2801 ngx_mp4_set_64value(entry, offset);
2802 entry++;
2803 }
2804 }
2805
2806
2651 static char * 2807 static char *
2652 ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2808 ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2653 { 2809 {
2654 ngx_http_core_loc_conf_t *clcf; 2810 ngx_http_core_loc_conf_t *clcf;
2655 2811