1111
1212#define NGX_HTTP_MP4_TRAK_ATOM 0
1313#define NGX_HTTP_MP4_TKHD_ATOM 1
14- #define NGX_HTTP_MP4_MDIA_ATOM 2
15- #define NGX_HTTP_MP4_MDHD_ATOM 3
16- #define NGX_HTTP_MP4_HDLR_ATOM 4
17- #define NGX_HTTP_MP4_MINF_ATOM 5
18- #define NGX_HTTP_MP4_VMHD_ATOM 6
19- #define NGX_HTTP_MP4_SMHD_ATOM 7
20- #define NGX_HTTP_MP4_DINF_ATOM 8
21- #define NGX_HTTP_MP4_STBL_ATOM 9
22- #define NGX_HTTP_MP4_STSD_ATOM 10
23- #define NGX_HTTP_MP4_STTS_ATOM 11
24- #define NGX_HTTP_MP4_STTS_DATA 12
25- #define NGX_HTTP_MP4_STSS_ATOM 13
26- #define NGX_HTTP_MP4_STSS_DATA 14
27- #define NGX_HTTP_MP4_CTTS_ATOM 15
28- #define NGX_HTTP_MP4_CTTS_DATA 16
29- #define NGX_HTTP_MP4_STSC_ATOM 17
30- #define NGX_HTTP_MP4_STSC_START 18
31- #define NGX_HTTP_MP4_STSC_DATA 19
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
14+ #define NGX_HTTP_MP4_EDTS_ATOM 2
15+ #define NGX_HTTP_MP4_ELST_ATOM 3
16+ #define NGX_HTTP_MP4_MDIA_ATOM 4
17+ #define NGX_HTTP_MP4_MDHD_ATOM 5
18+ #define NGX_HTTP_MP4_HDLR_ATOM 6
19+ #define NGX_HTTP_MP4_MINF_ATOM 7
20+ #define NGX_HTTP_MP4_VMHD_ATOM 8
21+ #define NGX_HTTP_MP4_SMHD_ATOM 9
22+ #define NGX_HTTP_MP4_DINF_ATOM 10
23+ #define NGX_HTTP_MP4_STBL_ATOM 11
24+ #define NGX_HTTP_MP4_STSD_ATOM 12
25+ #define NGX_HTTP_MP4_STTS_ATOM 13
26+ #define NGX_HTTP_MP4_STTS_DATA 14
27+ #define NGX_HTTP_MP4_STSS_ATOM 15
28+ #define NGX_HTTP_MP4_STSS_DATA 16
29+ #define NGX_HTTP_MP4_CTTS_ATOM 17
30+ #define NGX_HTTP_MP4_CTTS_DATA 18
31+ #define NGX_HTTP_MP4_STSC_ATOM 19
32+ #define NGX_HTTP_MP4_STSC_START 20
33+ #define NGX_HTTP_MP4_STSC_DATA 21
34+ #define NGX_HTTP_MP4_STSC_END 22
35+ #define NGX_HTTP_MP4_STSZ_ATOM 23
36+ #define NGX_HTTP_MP4_STSZ_DATA 24
37+ #define NGX_HTTP_MP4_STCO_ATOM 25
38+ #define NGX_HTTP_MP4_STCO_DATA 26
39+ #define NGX_HTTP_MP4_CO64_ATOM 27
40+ #define NGX_HTTP_MP4_CO64_DATA 28
3941
4042#define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
4143
4244
4345typedef struct {
4446 size_t buffer_size ;
4547 size_t max_buffer_size ;
48+ ngx_flag_t start_key_frame ;
4649} ngx_http_mp4_conf_t ;
4750
4851
@@ -53,6 +56,25 @@ typedef struct {
5356} ngx_mp4_stsc_entry_t ;
5457
5558
59+ typedef struct {
60+ u_char size [4 ];
61+ u_char name [4 ];
62+ } ngx_mp4_edts_atom_t ;
63+
64+
65+ typedef struct {
66+ u_char size [4 ];
67+ u_char name [4 ];
68+ u_char version [1 ];
69+ u_char flags [3 ];
70+ u_char entries [4 ];
71+ u_char duration [8 ];
72+ u_char media_time [8 ];
73+ u_char media_rate [2 ];
74+ u_char reserved [2 ];
75+ } ngx_mp4_elst_atom_t ;
76+
77+
5678typedef struct {
5779 uint32_t timescale ;
5880 uint32_t time_to_sample_entries ;
@@ -71,6 +93,8 @@ typedef struct {
7193 uint64_t start_chunk_samples_size ;
7294 uint64_t end_chunk_samples_size ;
7395 uint64_t duration ;
96+ uint64_t prefix ;
97+ uint64_t movie_duration ;
7498 off_t start_offset ;
7599 off_t end_offset ;
76100
@@ -86,6 +110,8 @@ typedef struct {
86110
87111 ngx_buf_t trak_atom_buf ;
88112 ngx_buf_t tkhd_atom_buf ;
113+ ngx_buf_t edts_atom_buf ;
114+ ngx_buf_t elst_atom_buf ;
89115 ngx_buf_t mdia_atom_buf ;
90116 ngx_buf_t mdhd_atom_buf ;
91117 ngx_buf_t hdlr_atom_buf ;
@@ -112,6 +138,8 @@ typedef struct {
112138 ngx_buf_t co64_atom_buf ;
113139 ngx_buf_t co64_data_buf ;
114140
141+ ngx_mp4_edts_atom_t edts_atom ;
142+ ngx_mp4_elst_atom_t elst_atom ;
115143 ngx_mp4_stsc_entry_t stsc_start_chunk_entry ;
116144 ngx_mp4_stsc_entry_t stsc_end_chunk_entry ;
117145} ngx_http_mp4_trak_t ;
@@ -187,6 +215,14 @@ typedef struct {
187215 ((u_char *) (p))[6] = n3; \
188216 ((u_char *) (p))[7] = n4
189217
218+ #define ngx_mp4_get_16value (p ) \
219+ ( ((uint16_t) ((u_char *) (p))[0] << 8) \
220+ + ( ((u_char *) (p))[1]) )
221+
222+ #define ngx_mp4_set_16value (p , n ) \
223+ ((u_char *) (p))[0] = (u_char) ((n) >> 8); \
224+ ((u_char *) (p))[1] = (u_char) (n)
225+
190226#define ngx_mp4_get_32value (p ) \
191227 ( ((uint32_t) ((u_char *) (p))[0] << 24) \
192228 + ( ((u_char *) (p))[1] << 16) \
@@ -270,6 +306,8 @@ static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,
270306 uint64_t atom_data_size );
271307static ngx_int_t ngx_http_mp4_read_stbl_atom (ngx_http_mp4_file_t * mp4 ,
272308 uint64_t atom_data_size );
309+ static void ngx_http_mp4_update_edts_atom (ngx_http_mp4_file_t * mp4 ,
310+ ngx_http_mp4_trak_t * trak );
273311static void ngx_http_mp4_update_stbl_atom (ngx_http_mp4_file_t * mp4 ,
274312 ngx_http_mp4_trak_t * trak );
275313static ngx_int_t ngx_http_mp4_read_stsd_atom (ngx_http_mp4_file_t * mp4 ,
@@ -280,6 +318,8 @@ static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
280318 ngx_http_mp4_trak_t * trak );
281319static ngx_int_t ngx_http_mp4_crop_stts_data (ngx_http_mp4_file_t * mp4 ,
282320 ngx_http_mp4_trak_t * trak , ngx_uint_t start );
321+ static uint32_t ngx_http_mp4_seek_key_frame (ngx_http_mp4_file_t * mp4 ,
322+ ngx_http_mp4_trak_t * trak , uint32_t start_sample );
283323static ngx_int_t ngx_http_mp4_read_stss_atom (ngx_http_mp4_file_t * mp4 ,
284324 uint64_t atom_data_size );
285325static ngx_int_t ngx_http_mp4_update_stss_atom (ngx_http_mp4_file_t * mp4 ,
@@ -343,6 +383,13 @@ static ngx_command_t ngx_http_mp4_commands[] = {
343383 offsetof(ngx_http_mp4_conf_t , max_buffer_size ),
344384 NULL },
345385
386+ { ngx_string ("mp4_start_key_frame" ),
387+ NGX_HTTP_MAIN_CONF |NGX_HTTP_SRV_CONF |NGX_HTTP_LOC_CONF |NGX_CONF_FLAG ,
388+ ngx_conf_set_flag_slot ,
389+ NGX_HTTP_LOC_CONF_OFFSET ,
390+ offsetof(ngx_http_mp4_conf_t , start_key_frame ),
391+ NULL },
392+
346393 ngx_null_command
347394};
348395
@@ -829,6 +876,7 @@ ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
829876 trak [i ].size += trak [i ].hdlr_size ;
830877 ngx_http_mp4_update_mdia_atom (mp4 , & trak [i ]);
831878 trak [i ].size += trak [i ].tkhd_size ;
879+ ngx_http_mp4_update_edts_atom (mp4 , & trak [i ]);
832880 ngx_http_mp4_update_trak_atom (mp4 , & trak [i ]);
833881
834882 mp4 -> moov_size += trak [i ].size ;
@@ -1590,6 +1638,7 @@ ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
15901638
15911639 trak = ngx_mp4_last_trak (mp4 );
15921640 trak -> tkhd_size = atom_size ;
1641+ trak -> movie_duration = duration ;
15931642
15941643 ngx_mp4_set_32value (tkhd_atom -> size , atom_size );
15951644
@@ -1985,6 +2034,59 @@ ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
19852034}
19862035
19872036
2037+ static void
2038+ ngx_http_mp4_update_edts_atom (ngx_http_mp4_file_t * mp4 ,
2039+ ngx_http_mp4_trak_t * trak )
2040+ {
2041+ ngx_buf_t * atom ;
2042+ ngx_mp4_elst_atom_t * elst_atom ;
2043+ ngx_mp4_edts_atom_t * edts_atom ;
2044+
2045+ if (trak -> prefix == 0 ) {
2046+ return ;
2047+ }
2048+
2049+ ngx_log_debug1 (NGX_LOG_DEBUG_HTTP , mp4 -> file .log , 0 ,
2050+ "mp4 edts atom update prefix:%uL" , trak -> prefix );
2051+
2052+ edts_atom = & trak -> edts_atom ;
2053+ ngx_mp4_set_32value (edts_atom -> size , sizeof (ngx_mp4_edts_atom_t )
2054+ + sizeof (ngx_mp4_elst_atom_t ));
2055+ ngx_mp4_set_atom_name (edts_atom , 'e' , 'd' , 't' , 's' );
2056+
2057+ atom = & trak -> edts_atom_buf ;
2058+ atom -> temporary = 1 ;
2059+ atom -> pos = (u_char * ) edts_atom ;
2060+ atom -> last = (u_char * ) edts_atom + sizeof (ngx_mp4_edts_atom_t );
2061+
2062+ trak -> out [NGX_HTTP_MP4_EDTS_ATOM ].buf = atom ;
2063+
2064+ elst_atom = & trak -> elst_atom ;
2065+ ngx_mp4_set_32value (elst_atom -> size , sizeof (ngx_mp4_elst_atom_t ));
2066+ ngx_mp4_set_atom_name (elst_atom , 'e' , 'l' , 's' , 't' );
2067+
2068+ elst_atom -> version [0 ] = 1 ;
2069+ elst_atom -> flags [0 ] = 0 ;
2070+ elst_atom -> flags [1 ] = 0 ;
2071+ elst_atom -> flags [2 ] = 0 ;
2072+
2073+ ngx_mp4_set_32value (elst_atom -> entries , 1 );
2074+ ngx_mp4_set_64value (elst_atom -> duration , trak -> movie_duration );
2075+ ngx_mp4_set_64value (elst_atom -> media_time , trak -> prefix );
2076+ ngx_mp4_set_16value (elst_atom -> media_rate , 1 );
2077+ ngx_mp4_set_16value (elst_atom -> reserved , 0 );
2078+
2079+ atom = & trak -> elst_atom_buf ;
2080+ atom -> temporary = 1 ;
2081+ atom -> pos = (u_char * ) elst_atom ;
2082+ atom -> last = (u_char * ) elst_atom + sizeof (ngx_mp4_elst_atom_t );
2083+
2084+ trak -> out [NGX_HTTP_MP4_ELST_ATOM ].buf = atom ;
2085+
2086+ trak -> size += sizeof (ngx_mp4_edts_atom_t ) + sizeof (ngx_mp4_elst_atom_t );
2087+ }
2088+
2089+
19882090static void
19892091ngx_http_mp4_update_stbl_atom (ngx_http_mp4_file_t * mp4 ,
19902092 ngx_http_mp4_trak_t * trak )
@@ -2183,7 +2285,7 @@ static ngx_int_t
21832285ngx_http_mp4_crop_stts_data (ngx_http_mp4_file_t * mp4 ,
21842286 ngx_http_mp4_trak_t * trak , ngx_uint_t start )
21852287{
2186- uint32_t count , duration , rest ;
2288+ uint32_t count , duration , rest , key_prefix ;
21872289 uint64_t start_time ;
21882290 ngx_buf_t * data ;
21892291 ngx_uint_t start_sample , entries , start_sec ;
@@ -2207,7 +2309,7 @@ ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
22072309
22082310 data = trak -> out [NGX_HTTP_MP4_STTS_DATA ].buf ;
22092311
2210- start_time = (uint64_t ) start_sec * trak -> timescale / 1000 ;
2312+ start_time = (uint64_t ) start_sec * trak -> timescale / 1000 + trak -> prefix ;
22112313
22122314 entries = trak -> time_to_sample_entries ;
22132315 start_sample = 0 ;
@@ -2253,6 +2355,26 @@ ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
22532355found :
22542356
22552357 if (start ) {
2358+ key_prefix = ngx_http_mp4_seek_key_frame (mp4 , trak , start_sample );
2359+
2360+ start_sample -= key_prefix ;
2361+
2362+ while (rest < key_prefix ) {
2363+ trak -> prefix += rest * duration ;
2364+ key_prefix -= rest ;
2365+
2366+ entry -- ;
2367+ entries ++ ;
2368+
2369+ count = ngx_mp4_get_32value (entry -> count );
2370+ duration = ngx_mp4_get_32value (entry -> duration );
2371+ rest = count ;
2372+ }
2373+
2374+ trak -> prefix += key_prefix * duration ;
2375+ trak -> duration += trak -> prefix ;
2376+ rest -= key_prefix ;
2377+
22562378 ngx_mp4_set_32value (entry -> count , count - rest );
22572379 data -> pos = (u_char * ) entry ;
22582380 trak -> time_to_sample_entries = entries ;
@@ -2277,6 +2399,49 @@ ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,
22772399}
22782400
22792401
2402+ static uint32_t
2403+ ngx_http_mp4_seek_key_frame (ngx_http_mp4_file_t * mp4 , ngx_http_mp4_trak_t * trak ,
2404+ uint32_t start_sample )
2405+ {
2406+ uint32_t key_prefix , sample , * entry , * end ;
2407+ ngx_buf_t * data ;
2408+ ngx_http_mp4_conf_t * conf ;
2409+
2410+ conf = ngx_http_get_module_loc_conf (mp4 -> request , ngx_http_mp4_module );
2411+ if (!conf -> start_key_frame ) {
2412+ return 0 ;
2413+ }
2414+
2415+ data = trak -> out [NGX_HTTP_MP4_STSS_DATA ].buf ;
2416+ if (data == NULL ) {
2417+ return 0 ;
2418+ }
2419+
2420+ entry = (uint32_t * ) data -> pos ;
2421+ end = (uint32_t * ) data -> last ;
2422+
2423+ /* sync samples starts from 1 */
2424+ start_sample ++ ;
2425+
2426+ key_prefix = 0 ;
2427+
2428+ while (entry < end ) {
2429+ sample = ngx_mp4_get_32value (entry );
2430+ if (sample > start_sample ) {
2431+ break ;
2432+ }
2433+
2434+ key_prefix = start_sample - sample ;
2435+ entry ++ ;
2436+ }
2437+
2438+ ngx_log_debug1 (NGX_LOG_DEBUG_HTTP , mp4 -> file .log , 0 ,
2439+ "mp4 key frame prefix:%uD" , key_prefix );
2440+
2441+ return key_prefix ;
2442+ }
2443+
2444+
22802445typedef struct {
22812446 u_char size [4 ];
22822447 u_char name [4 ];
@@ -3614,6 +3779,7 @@ ngx_http_mp4_create_conf(ngx_conf_t *cf)
36143779
36153780 conf -> buffer_size = NGX_CONF_UNSET_SIZE ;
36163781 conf -> max_buffer_size = NGX_CONF_UNSET_SIZE ;
3782+ conf -> start_key_frame = NGX_CONF_UNSET ;
36173783
36183784 return conf ;
36193785}
@@ -3628,6 +3794,7 @@ ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)
36283794 ngx_conf_merge_size_value (conf -> buffer_size , prev -> buffer_size , 512 * 1024 );
36293795 ngx_conf_merge_size_value (conf -> max_buffer_size , prev -> max_buffer_size ,
36303796 10 * 1024 * 1024 );
3797+ ngx_conf_merge_value (conf -> start_key_frame , prev -> start_key_frame , 0 );
36313798
36323799 return NGX_CONF_OK ;
36333800}
0 commit comments