Annotation of embedaddon/nginx/src/http/modules/ngx_http_mp4_module.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (C) Igor Sysoev
! 4: * Copyright (C) Nginx, Inc.
! 5: */
! 6:
! 7: #include <ngx_config.h>
! 8: #include <ngx_core.h>
! 9: #include <ngx_http.h>
! 10:
! 11:
! 12: #define NGX_HTTP_MP4_TRAK_ATOM 0
! 13: #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_CHUNK 18
! 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
! 38:
! 39: #define NGX_HTTP_MP4_LAST_ATOM NGX_HTTP_MP4_CO64_DATA
! 40:
! 41:
! 42: typedef struct {
! 43: size_t buffer_size;
! 44: size_t max_buffer_size;
! 45: } ngx_http_mp4_conf_t;
! 46:
! 47:
! 48: typedef struct {
! 49: u_char chunk[4];
! 50: u_char samples[4];
! 51: u_char id[4];
! 52: } ngx_mp4_stsc_entry_t;
! 53:
! 54:
! 55: typedef struct {
! 56: uint32_t timescale;
! 57: uint32_t time_to_sample_entries;
! 58: uint32_t sample_to_chunk_entries;
! 59: uint32_t sync_samples_entries;
! 60: uint32_t composition_offset_entries;
! 61: uint32_t sample_sizes_entries;
! 62: uint32_t chunks;
! 63:
! 64: ngx_uint_t start_sample;
! 65: ngx_uint_t start_chunk;
! 66: ngx_uint_t chunk_samples;
! 67: uint64_t chunk_samples_size;
! 68: off_t start_offset;
! 69:
! 70: size_t tkhd_size;
! 71: size_t mdhd_size;
! 72: size_t hdlr_size;
! 73: size_t vmhd_size;
! 74: size_t smhd_size;
! 75: size_t dinf_size;
! 76: size_t size;
! 77:
! 78: ngx_chain_t out[NGX_HTTP_MP4_LAST_ATOM + 1];
! 79:
! 80: ngx_buf_t trak_atom_buf;
! 81: ngx_buf_t tkhd_atom_buf;
! 82: ngx_buf_t mdia_atom_buf;
! 83: ngx_buf_t mdhd_atom_buf;
! 84: ngx_buf_t hdlr_atom_buf;
! 85: ngx_buf_t minf_atom_buf;
! 86: ngx_buf_t vmhd_atom_buf;
! 87: ngx_buf_t smhd_atom_buf;
! 88: ngx_buf_t dinf_atom_buf;
! 89: ngx_buf_t stbl_atom_buf;
! 90: ngx_buf_t stsd_atom_buf;
! 91: ngx_buf_t stts_atom_buf;
! 92: ngx_buf_t stts_data_buf;
! 93: ngx_buf_t stss_atom_buf;
! 94: ngx_buf_t stss_data_buf;
! 95: ngx_buf_t ctts_atom_buf;
! 96: ngx_buf_t ctts_data_buf;
! 97: ngx_buf_t stsc_atom_buf;
! 98: ngx_buf_t stsc_chunk_buf;
! 99: ngx_buf_t stsc_data_buf;
! 100: ngx_buf_t stsz_atom_buf;
! 101: ngx_buf_t stsz_data_buf;
! 102: ngx_buf_t stco_atom_buf;
! 103: ngx_buf_t stco_data_buf;
! 104: ngx_buf_t co64_atom_buf;
! 105: ngx_buf_t co64_data_buf;
! 106:
! 107: ngx_mp4_stsc_entry_t stsc_chunk_entry;
! 108: } ngx_http_mp4_trak_t;
! 109:
! 110:
! 111: typedef struct {
! 112: ngx_file_t file;
! 113:
! 114: u_char *buffer;
! 115: u_char *buffer_start;
! 116: u_char *buffer_pos;
! 117: u_char *buffer_end;
! 118: size_t buffer_size;
! 119:
! 120: off_t offset;
! 121: off_t end;
! 122: off_t content_length;
! 123: ngx_uint_t start;
! 124: uint32_t timescale;
! 125: ngx_http_request_t *request;
! 126: ngx_array_t trak;
! 127: ngx_http_mp4_trak_t traks[2];
! 128:
! 129: size_t ftyp_size;
! 130: size_t moov_size;
! 131:
! 132: ngx_chain_t *out;
! 133: ngx_chain_t ftyp_atom;
! 134: ngx_chain_t moov_atom;
! 135: ngx_chain_t mvhd_atom;
! 136: ngx_chain_t mdat_atom;
! 137: ngx_chain_t mdat_data;
! 138:
! 139: ngx_buf_t ftyp_atom_buf;
! 140: ngx_buf_t moov_atom_buf;
! 141: ngx_buf_t mvhd_atom_buf;
! 142: ngx_buf_t mdat_atom_buf;
! 143: ngx_buf_t mdat_data_buf;
! 144:
! 145: u_char moov_atom_header[8];
! 146: u_char mdat_atom_header[16];
! 147: } ngx_http_mp4_file_t;
! 148:
! 149:
! 150: typedef struct {
! 151: char *name;
! 152: ngx_int_t (*handler)(ngx_http_mp4_file_t *mp4,
! 153: uint64_t atom_data_size);
! 154: } ngx_http_mp4_atom_handler_t;
! 155:
! 156:
! 157: #define ngx_mp4_atom_header(mp4) (mp4->buffer_pos - 8)
! 158: #define ngx_mp4_atom_data(mp4) mp4->buffer_pos
! 159: #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
! 161:
! 162:
! 163: #define ngx_mp4_set_atom_name(p, n1, n2, n3, n4) \
! 164: ((u_char *) (p))[4] = n1; \
! 165: ((u_char *) (p))[5] = n2; \
! 166: ((u_char *) (p))[6] = n3; \
! 167: ((u_char *) (p))[7] = n4
! 168:
! 169: #define ngx_mp4_get_32value(p) \
! 170: ( ((uint32_t) ((u_char *) (p))[0] << 24) \
! 171: + ( ((u_char *) (p))[1] << 16) \
! 172: + ( ((u_char *) (p))[2] << 8) \
! 173: + ( ((u_char *) (p))[3]) )
! 174:
! 175: #define ngx_mp4_set_32value(p, n) \
! 176: ((u_char *) (p))[0] = (u_char) ((n) >> 24); \
! 177: ((u_char *) (p))[1] = (u_char) ((n) >> 16); \
! 178: ((u_char *) (p))[2] = (u_char) ((n) >> 8); \
! 179: ((u_char *) (p))[3] = (u_char) (n)
! 180:
! 181: #define ngx_mp4_get_64value(p) \
! 182: ( ((uint64_t) ((u_char *) (p))[0] << 56) \
! 183: + ((uint64_t) ((u_char *) (p))[1] << 48) \
! 184: + ((uint64_t) ((u_char *) (p))[2] << 40) \
! 185: + ((uint64_t) ((u_char *) (p))[3] << 32) \
! 186: + ((uint64_t) ((u_char *) (p))[4] << 24) \
! 187: + ( ((u_char *) (p))[5] << 16) \
! 188: + ( ((u_char *) (p))[6] << 8) \
! 189: + ( ((u_char *) (p))[7]) )
! 190:
! 191: #define ngx_mp4_set_64value(p, n) \
! 192: ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56); \
! 193: ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48); \
! 194: ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40); \
! 195: ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32); \
! 196: ((u_char *) (p))[4] = (u_char) ( (n) >> 24); \
! 197: ((u_char *) (p))[5] = (u_char) ( (n) >> 16); \
! 198: ((u_char *) (p))[6] = (u_char) ( (n) >> 8); \
! 199: ((u_char *) (p))[7] = (u_char) (n)
! 200:
! 201: #define ngx_mp4_last_trak(mp4) \
! 202: &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]
! 203:
! 204:
! 205: static ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4);
! 206: static ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
! 207: ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size);
! 208: static ngx_int_t ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size);
! 209: static ngx_int_t ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4,
! 210: uint64_t atom_data_size);
! 211: static ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,
! 212: uint64_t atom_data_size);
! 213: static ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,
! 214: uint64_t atom_data_size);
! 215: static size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,
! 216: off_t start_offset);
! 217: static ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,
! 218: uint64_t atom_data_size);
! 219: static ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,
! 220: uint64_t atom_data_size);
! 221: static void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
! 222: ngx_http_mp4_trak_t *trak);
! 223: static ngx_int_t ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4,
! 224: uint64_t atom_data_size);
! 225: static ngx_int_t ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4,
! 226: uint64_t atom_data_size);
! 227: static ngx_int_t ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4,
! 228: uint64_t atom_data_size);
! 229: static void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
! 230: ngx_http_mp4_trak_t *trak);
! 231: static ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4,
! 232: uint64_t atom_data_size);
! 233: static ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4,
! 234: uint64_t atom_data_size);
! 235: static ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4,
! 236: uint64_t atom_data_size);
! 237: static void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
! 238: ngx_http_mp4_trak_t *trak);
! 239: static ngx_int_t ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4,
! 240: uint64_t atom_data_size);
! 241: static ngx_int_t ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4,
! 242: uint64_t atom_data_size);
! 243: static ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,
! 244: uint64_t atom_data_size);
! 245: static ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4,
! 246: uint64_t atom_data_size);
! 247: static void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
! 248: ngx_http_mp4_trak_t *trak);
! 249: static ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4,
! 250: uint64_t atom_data_size);
! 251: static ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,
! 252: uint64_t atom_data_size);
! 253: static ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
! 254: ngx_http_mp4_trak_t *trak);
! 255: static ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,
! 256: uint64_t atom_data_size);
! 257: static ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
! 258: ngx_http_mp4_trak_t *trak);
! 259: static ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,
! 260: uint64_t atom_data_size);
! 261: static void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
! 262: ngx_http_mp4_trak_t *trak);
! 263: static ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,
! 264: uint64_t atom_data_size);
! 265: static ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
! 266: ngx_http_mp4_trak_t *trak);
! 267: static ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,
! 268: uint64_t atom_data_size);
! 269: static ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
! 270: ngx_http_mp4_trak_t *trak);
! 271: static ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4,
! 272: uint64_t atom_data_size);
! 273: static ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
! 274: ngx_http_mp4_trak_t *trak);
! 275: static void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
! 276: ngx_http_mp4_trak_t *trak, int32_t adjustment);
! 277: static ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,
! 278: uint64_t atom_data_size);
! 279: static ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
! 280: ngx_http_mp4_trak_t *trak);
! 281: static void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
! 282: ngx_http_mp4_trak_t *trak, off_t adjustment);
! 283: static char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
! 284: static void *ngx_http_mp4_create_conf(ngx_conf_t *cf);
! 285: static char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);
! 286:
! 287: static ngx_command_t ngx_http_mp4_commands[] = {
! 288:
! 289: { ngx_string("mp4"),
! 290: NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
! 291: ngx_http_mp4,
! 292: 0,
! 293: 0,
! 294: NULL },
! 295:
! 296: { ngx_string("mp4_buffer_size"),
! 297: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
! 298: ngx_conf_set_size_slot,
! 299: NGX_HTTP_LOC_CONF_OFFSET,
! 300: offsetof(ngx_http_mp4_conf_t, buffer_size),
! 301: NULL },
! 302:
! 303: { ngx_string("mp4_max_buffer_size"),
! 304: NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
! 305: ngx_conf_set_size_slot,
! 306: NGX_HTTP_LOC_CONF_OFFSET,
! 307: offsetof(ngx_http_mp4_conf_t, max_buffer_size),
! 308: NULL },
! 309:
! 310: ngx_null_command
! 311: };
! 312:
! 313:
! 314: static ngx_http_module_t ngx_http_mp4_module_ctx = {
! 315: NULL, /* preconfiguration */
! 316: NULL, /* postconfiguration */
! 317:
! 318: NULL, /* create main configuration */
! 319: NULL, /* init main configuration */
! 320:
! 321: NULL, /* create server configuration */
! 322: NULL, /* merge server configuration */
! 323:
! 324: ngx_http_mp4_create_conf, /* create location configuration */
! 325: ngx_http_mp4_merge_conf /* merge location configuration */
! 326: };
! 327:
! 328:
! 329: ngx_module_t ngx_http_mp4_module = {
! 330: NGX_MODULE_V1,
! 331: &ngx_http_mp4_module_ctx, /* module context */
! 332: ngx_http_mp4_commands, /* module directives */
! 333: NGX_HTTP_MODULE, /* module type */
! 334: NULL, /* init master */
! 335: NULL, /* init module */
! 336: NULL, /* init process */
! 337: NULL, /* init thread */
! 338: NULL, /* exit thread */
! 339: NULL, /* exit process */
! 340: NULL, /* exit master */
! 341: NGX_MODULE_V1_PADDING
! 342: };
! 343:
! 344:
! 345: static ngx_http_mp4_atom_handler_t ngx_http_mp4_atoms[] = {
! 346: { "ftyp", ngx_http_mp4_read_ftyp_atom },
! 347: { "moov", ngx_http_mp4_read_moov_atom },
! 348: { "mdat", ngx_http_mp4_read_mdat_atom },
! 349: { NULL, NULL }
! 350: };
! 351:
! 352: static ngx_http_mp4_atom_handler_t ngx_http_mp4_moov_atoms[] = {
! 353: { "mvhd", ngx_http_mp4_read_mvhd_atom },
! 354: { "trak", ngx_http_mp4_read_trak_atom },
! 355: { "cmov", ngx_http_mp4_read_cmov_atom },
! 356: { NULL, NULL }
! 357: };
! 358:
! 359: static ngx_http_mp4_atom_handler_t ngx_http_mp4_trak_atoms[] = {
! 360: { "tkhd", ngx_http_mp4_read_tkhd_atom },
! 361: { "mdia", ngx_http_mp4_read_mdia_atom },
! 362: { NULL, NULL }
! 363: };
! 364:
! 365: static ngx_http_mp4_atom_handler_t ngx_http_mp4_mdia_atoms[] = {
! 366: { "mdhd", ngx_http_mp4_read_mdhd_atom },
! 367: { "hdlr", ngx_http_mp4_read_hdlr_atom },
! 368: { "minf", ngx_http_mp4_read_minf_atom },
! 369: { NULL, NULL }
! 370: };
! 371:
! 372: static ngx_http_mp4_atom_handler_t ngx_http_mp4_minf_atoms[] = {
! 373: { "vmhd", ngx_http_mp4_read_vmhd_atom },
! 374: { "smhd", ngx_http_mp4_read_smhd_atom },
! 375: { "dinf", ngx_http_mp4_read_dinf_atom },
! 376: { "stbl", ngx_http_mp4_read_stbl_atom },
! 377: { NULL, NULL }
! 378: };
! 379:
! 380: static ngx_http_mp4_atom_handler_t ngx_http_mp4_stbl_atoms[] = {
! 381: { "stsd", ngx_http_mp4_read_stsd_atom },
! 382: { "stts", ngx_http_mp4_read_stts_atom },
! 383: { "stss", ngx_http_mp4_read_stss_atom },
! 384: { "ctts", ngx_http_mp4_read_ctts_atom },
! 385: { "stsc", ngx_http_mp4_read_stsc_atom },
! 386: { "stsz", ngx_http_mp4_read_stsz_atom },
! 387: { "stco", ngx_http_mp4_read_stco_atom },
! 388: { "co64", ngx_http_mp4_read_co64_atom },
! 389: { NULL, NULL }
! 390: };
! 391:
! 392:
! 393: static ngx_int_t
! 394: ngx_http_mp4_handler(ngx_http_request_t *r)
! 395: {
! 396: u_char *last;
! 397: size_t root;
! 398: ngx_int_t rc, start;
! 399: ngx_uint_t level;
! 400: ngx_str_t path, value;
! 401: ngx_log_t *log;
! 402: ngx_buf_t *b;
! 403: ngx_chain_t out;
! 404: ngx_http_mp4_file_t *mp4;
! 405: ngx_open_file_info_t of;
! 406: ngx_http_core_loc_conf_t *clcf;
! 407:
! 408: if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
! 409: return NGX_HTTP_NOT_ALLOWED;
! 410: }
! 411:
! 412: if (r->uri.data[r->uri.len - 1] == '/') {
! 413: return NGX_DECLINED;
! 414: }
! 415:
! 416: rc = ngx_http_discard_request_body(r);
! 417:
! 418: if (rc != NGX_OK) {
! 419: return rc;
! 420: }
! 421:
! 422: last = ngx_http_map_uri_to_path(r, &path, &root, 0);
! 423: if (last == NULL) {
! 424: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 425: }
! 426:
! 427: log = r->connection->log;
! 428:
! 429: path.len = last - path.data;
! 430:
! 431: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
! 432: "http mp4 filename: \"%V\"", &path);
! 433:
! 434: clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
! 435:
! 436: ngx_memzero(&of, sizeof(ngx_open_file_info_t));
! 437:
! 438: of.read_ahead = clcf->read_ahead;
! 439: of.directio = NGX_MAX_OFF_T_VALUE;
! 440: of.valid = clcf->open_file_cache_valid;
! 441: of.min_uses = clcf->open_file_cache_min_uses;
! 442: of.errors = clcf->open_file_cache_errors;
! 443: of.events = clcf->open_file_cache_events;
! 444:
! 445: if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
! 446: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 447: }
! 448:
! 449: if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
! 450: != NGX_OK)
! 451: {
! 452: switch (of.err) {
! 453:
! 454: case 0:
! 455: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 456:
! 457: case NGX_ENOENT:
! 458: case NGX_ENOTDIR:
! 459: case NGX_ENAMETOOLONG:
! 460:
! 461: level = NGX_LOG_ERR;
! 462: rc = NGX_HTTP_NOT_FOUND;
! 463: break;
! 464:
! 465: case NGX_EACCES:
! 466: #if (NGX_HAVE_OPENAT)
! 467: case NGX_EMLINK:
! 468: case NGX_ELOOP:
! 469: #endif
! 470:
! 471: level = NGX_LOG_ERR;
! 472: rc = NGX_HTTP_FORBIDDEN;
! 473: break;
! 474:
! 475: default:
! 476:
! 477: level = NGX_LOG_CRIT;
! 478: rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
! 479: break;
! 480: }
! 481:
! 482: if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
! 483: ngx_log_error(level, log, of.err,
! 484: "%s \"%s\" failed", of.failed, path.data);
! 485: }
! 486:
! 487: return rc;
! 488: }
! 489:
! 490: if (!of.is_file) {
! 491:
! 492: if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {
! 493: ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
! 494: ngx_close_file_n " \"%s\" failed", path.data);
! 495: }
! 496:
! 497: return NGX_DECLINED;
! 498: }
! 499:
! 500: r->root_tested = !r->error_page;
! 501: r->allow_ranges = 1;
! 502:
! 503: start = -1;
! 504: r->headers_out.content_length_n = of.size;
! 505: mp4 = NULL;
! 506: b = NULL;
! 507:
! 508: if (r->args.len) {
! 509:
! 510: if (ngx_http_arg(r, (u_char *) "start", 5, &value) == NGX_OK) {
! 511:
! 512: /*
! 513: * A Flash player may send start value with a lot of digits
! 514: * after dot so strtod() is used instead of atofp(). NaNs and
! 515: * infinities become negative numbers after (int) conversion.
! 516: */
! 517:
! 518: ngx_set_errno(0);
! 519: start = (int) (strtod((char *) value.data, NULL) * 1000);
! 520:
! 521: if (ngx_errno == 0 && start >= 0) {
! 522: r->allow_ranges = 0;
! 523:
! 524: mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));
! 525: if (mp4 == NULL) {
! 526: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 527: }
! 528:
! 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;
! 535:
! 536: switch (ngx_http_mp4_process(mp4)) {
! 537:
! 538: case NGX_DECLINED:
! 539: if (mp4->buffer) {
! 540: ngx_pfree(r->pool, mp4->buffer);
! 541: }
! 542:
! 543: ngx_pfree(r->pool, mp4);
! 544: mp4 = NULL;
! 545:
! 546: break;
! 547:
! 548: case NGX_OK:
! 549: r->headers_out.content_length_n = mp4->content_length;
! 550: break;
! 551:
! 552: default: /* NGX_ERROR */
! 553: if (mp4->buffer) {
! 554: ngx_pfree(r->pool, mp4->buffer);
! 555: }
! 556:
! 557: ngx_pfree(r->pool, mp4);
! 558:
! 559: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 560: }
! 561: }
! 562: }
! 563: }
! 564:
! 565: log->action = "sending mp4 to client";
! 566:
! 567: if (clcf->directio <= of.size) {
! 568:
! 569: /*
! 570: * DIRECTIO is set on transfer only
! 571: * to allow kernel to cache "moov" atom
! 572: */
! 573:
! 574: if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) {
! 575: ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
! 576: ngx_directio_on_n " \"%s\" failed", path.data);
! 577: }
! 578:
! 579: of.is_directio = 1;
! 580:
! 581: if (mp4) {
! 582: mp4->file.directio = 1;
! 583: }
! 584: }
! 585:
! 586: r->headers_out.status = NGX_HTTP_OK;
! 587: r->headers_out.last_modified_time = of.mtime;
! 588:
! 589: if (ngx_http_set_etag(r) != NGX_OK) {
! 590: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 591: }
! 592:
! 593: if (ngx_http_set_content_type(r) != NGX_OK) {
! 594: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 595: }
! 596:
! 597: if (mp4 == NULL) {
! 598: b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
! 599: if (b == NULL) {
! 600: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 601: }
! 602:
! 603: b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
! 604: if (b->file == NULL) {
! 605: return NGX_HTTP_INTERNAL_SERVER_ERROR;
! 606: }
! 607: }
! 608:
! 609: rc = ngx_http_send_header(r);
! 610:
! 611: if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
! 612: return rc;
! 613: }
! 614:
! 615: if (mp4) {
! 616: return ngx_http_output_filter(r, mp4->out);
! 617: }
! 618:
! 619: b->file_pos = 0;
! 620: b->file_last = of.size;
! 621:
! 622: b->in_file = b->file_last ? 1 : 0;
! 623: b->last_buf = (r == r->main) ? 1 : 0;
! 624: b->last_in_chain = 1;
! 625:
! 626: b->file->fd = of.fd;
! 627: b->file->name = path;
! 628: b->file->log = log;
! 629: b->file->directio = of.is_directio;
! 630:
! 631: out.buf = b;
! 632: out.next = NULL;
! 633:
! 634: return ngx_http_output_filter(r, &out);
! 635: }
! 636:
! 637:
! 638: static ngx_int_t
! 639: ngx_http_mp4_process(ngx_http_mp4_file_t *mp4)
! 640: {
! 641: off_t start_offset, adjustment;
! 642: ngx_int_t rc;
! 643: ngx_uint_t i, j;
! 644: ngx_chain_t **prev;
! 645: ngx_http_mp4_trak_t *trak;
! 646: ngx_http_mp4_conf_t *conf;
! 647:
! 648: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 649: "mp4 start:%ui", mp4->start);
! 650:
! 651: conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
! 652:
! 653: mp4->buffer_size = conf->buffer_size;
! 654:
! 655: rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end);
! 656: if (rc != NGX_OK) {
! 657: return rc;
! 658: }
! 659:
! 660: if (mp4->trak.nelts == 0) {
! 661: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 662: "no mp4 trak atoms were found in \"%s\"",
! 663: mp4->file.name.data);
! 664: return NGX_ERROR;
! 665: }
! 666:
! 667: if (mp4->mdat_atom.buf == NULL) {
! 668: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 669: "no mp4 mdat atom was found in \"%s\"",
! 670: mp4->file.name.data);
! 671: return NGX_ERROR;
! 672: }
! 673:
! 674: prev = &mp4->out;
! 675:
! 676: if (mp4->ftyp_atom.buf) {
! 677: *prev = &mp4->ftyp_atom;
! 678: prev = &mp4->ftyp_atom.next;
! 679: }
! 680:
! 681: *prev = &mp4->moov_atom;
! 682: prev = &mp4->moov_atom.next;
! 683:
! 684: if (mp4->mvhd_atom.buf) {
! 685: mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos;
! 686: *prev = &mp4->mvhd_atom;
! 687: prev = &mp4->mvhd_atom.next;
! 688: }
! 689:
! 690: start_offset = mp4->end;
! 691: trak = mp4->trak.elts;
! 692:
! 693: for (i = 0; i < mp4->trak.nelts; i++) {
! 694:
! 695: if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {
! 696: return NGX_ERROR;
! 697: }
! 698:
! 699: if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {
! 700: return NGX_ERROR;
! 701: }
! 702:
! 703: ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);
! 704:
! 705: if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {
! 706: return NGX_ERROR;
! 707: }
! 708:
! 709: if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {
! 710: return NGX_ERROR;
! 711: }
! 712:
! 713: if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
! 714: if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {
! 715: return NGX_ERROR;
! 716: }
! 717:
! 718: } else {
! 719: if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {
! 720: return NGX_ERROR;
! 721: }
! 722: }
! 723:
! 724: ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);
! 725: ngx_http_mp4_update_minf_atom(mp4, &trak[i]);
! 726: trak[i].size += trak[i].mdhd_size;
! 727: trak[i].size += trak[i].hdlr_size;
! 728: ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);
! 729: trak[i].size += trak[i].tkhd_size;
! 730: ngx_http_mp4_update_trak_atom(mp4, &trak[i]);
! 731:
! 732: mp4->moov_size += trak[i].size;
! 733:
! 734: if (start_offset > trak[i].start_offset) {
! 735: start_offset = trak[i].start_offset;
! 736: }
! 737:
! 738: *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];
! 739: prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;
! 740:
! 741: for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {
! 742: if (trak[i].out[j].buf) {
! 743: *prev = &trak[i].out[j];
! 744: prev = &trak[i].out[j].next;
! 745: }
! 746: }
! 747: }
! 748:
! 749: mp4->moov_size += 8;
! 750:
! 751: ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);
! 752: ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v');
! 753: mp4->content_length += mp4->moov_size;
! 754:
! 755: *prev = &mp4->mdat_atom;
! 756:
! 757: if (start_offset > mp4->mdat_data.buf->file_last) {
! 758: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 759: "start time is out mp4 mdat atom in \"%s\"",
! 760: mp4->file.name.data);
! 761: return NGX_ERROR;
! 762: }
! 763:
! 764: adjustment = mp4->ftyp_size + mp4->moov_size
! 765: + ngx_http_mp4_update_mdat_atom(mp4, start_offset)
! 766: - start_offset;
! 767:
! 768: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 769: "mp4 adjustment:%O", adjustment);
! 770:
! 771: for (i = 0; i < mp4->trak.nelts; i++) {
! 772: if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {
! 773: ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);
! 774: } else {
! 775: ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);
! 776: }
! 777: }
! 778:
! 779: return NGX_OK;
! 780: }
! 781:
! 782:
! 783: typedef struct {
! 784: u_char size[4];
! 785: u_char name[4];
! 786: } ngx_mp4_atom_header_t;
! 787:
! 788: typedef struct {
! 789: u_char size[4];
! 790: u_char name[4];
! 791: u_char size64[8];
! 792: } ngx_mp4_atom_header64_t;
! 793:
! 794:
! 795: static ngx_int_t
! 796: ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,
! 797: ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size)
! 798: {
! 799: off_t end;
! 800: size_t atom_header_size;
! 801: u_char *atom_header, *atom_name;
! 802: uint64_t atom_size;
! 803: ngx_int_t rc;
! 804: ngx_uint_t n;
! 805:
! 806: end = mp4->offset + atom_data_size;
! 807:
! 808: while (mp4->offset < end) {
! 809:
! 810: if (ngx_http_mp4_read(mp4, sizeof(uint32_t)) != NGX_OK) {
! 811: return NGX_ERROR;
! 812: }
! 813:
! 814: atom_header = mp4->buffer_pos;
! 815: atom_size = ngx_mp4_get_32value(atom_header);
! 816: atom_header_size = sizeof(ngx_mp4_atom_header_t);
! 817:
! 818: if (atom_size == 0) {
! 819: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 820: "mp4 atom end");
! 821: return NGX_OK;
! 822: }
! 823:
! 824: if (atom_size < sizeof(ngx_mp4_atom_header_t)) {
! 825:
! 826: if (atom_size == 1) {
! 827:
! 828: if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header64_t))
! 829: != NGX_OK)
! 830: {
! 831: return NGX_ERROR;
! 832: }
! 833:
! 834: /* 64-bit atom size */
! 835: atom_header = mp4->buffer_pos;
! 836: atom_size = ngx_mp4_get_64value(atom_header + 8);
! 837: atom_header_size = sizeof(ngx_mp4_atom_header64_t);
! 838:
! 839: } else {
! 840: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 841: "\"%s\" mp4 atom is too small:%uL",
! 842: mp4->file.name.data, atom_size);
! 843: return NGX_ERROR;
! 844: }
! 845: }
! 846:
! 847: if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header_t)) != NGX_OK) {
! 848: return NGX_ERROR;
! 849: }
! 850:
! 851: atom_header = mp4->buffer_pos;
! 852: atom_name = atom_header + sizeof(uint32_t);
! 853:
! 854: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 855: "mp4 atom: %*s @%O:%uL",
! 856: 4, atom_name, mp4->offset, atom_size);
! 857:
! 858: if (atom_size > (uint64_t) (NGX_MAX_OFF_T_VALUE - mp4->offset)
! 859: || mp4->offset + (off_t) atom_size > end)
! 860: {
! 861: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 862: "\"%s\" mp4 atom too large:%uL",
! 863: mp4->file.name.data, atom_size);
! 864: return NGX_ERROR;
! 865: }
! 866:
! 867: for (n = 0; atom[n].name; n++) {
! 868:
! 869: if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) {
! 870:
! 871: ngx_mp4_atom_next(mp4, atom_header_size);
! 872:
! 873: rc = atom[n].handler(mp4, atom_size - atom_header_size);
! 874: if (rc != NGX_OK) {
! 875: return rc;
! 876: }
! 877:
! 878: goto next;
! 879: }
! 880: }
! 881:
! 882: ngx_mp4_atom_next(mp4, atom_size);
! 883:
! 884: next:
! 885: continue;
! 886: }
! 887:
! 888: return NGX_OK;
! 889: }
! 890:
! 891:
! 892: static ngx_int_t
! 893: ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size)
! 894: {
! 895: ssize_t n;
! 896:
! 897: if (mp4->buffer_pos + size <= mp4->buffer_end) {
! 898: return NGX_OK;
! 899: }
! 900:
! 901: if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) {
! 902: mp4->buffer_size = (size_t) (mp4->end - mp4->offset);
! 903: }
! 904:
! 905: if (mp4->buffer_size < size) {
! 906: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 907: "\"%s\" mp4 file truncated", mp4->file.name.data);
! 908: return NGX_ERROR;
! 909: }
! 910:
! 911: if (mp4->buffer == NULL) {
! 912: mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);
! 913: if (mp4->buffer == NULL) {
! 914: return NGX_ERROR;
! 915: }
! 916:
! 917: mp4->buffer_start = mp4->buffer;
! 918: }
! 919:
! 920: n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,
! 921: mp4->offset);
! 922:
! 923: if (n == NGX_ERROR) {
! 924: return NGX_ERROR;
! 925: }
! 926:
! 927: if ((size_t) n != mp4->buffer_size) {
! 928: ngx_log_error(NGX_LOG_CRIT, mp4->file.log, 0,
! 929: ngx_read_file_n " read only %z of %z from \"%s\"",
! 930: n, mp4->buffer_size, mp4->file.name.data);
! 931: return NGX_ERROR;
! 932: }
! 933:
! 934: mp4->buffer_pos = mp4->buffer_start;
! 935: mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;
! 936:
! 937: return NGX_OK;
! 938: }
! 939:
! 940:
! 941: static ngx_int_t
! 942: ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 943: {
! 944: u_char *ftyp_atom;
! 945: size_t atom_size;
! 946: ngx_buf_t *atom;
! 947:
! 948: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ftyp atom");
! 949:
! 950: if (atom_data_size > 1024
! 951: || ngx_mp4_atom_data(mp4) + atom_data_size > mp4->buffer_end)
! 952: {
! 953: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 954: "\"%s\" mp4 ftyp atom is too large:%uL",
! 955: mp4->file.name.data, atom_data_size);
! 956: return NGX_ERROR;
! 957: }
! 958:
! 959: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 960:
! 961: ftyp_atom = ngx_palloc(mp4->request->pool, atom_size);
! 962: if (ftyp_atom == NULL) {
! 963: return NGX_ERROR;
! 964: }
! 965:
! 966: ngx_mp4_set_32value(ftyp_atom, atom_size);
! 967: ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p');
! 968:
! 969: /*
! 970: * only moov atom content is guaranteed to be in mp4->buffer
! 971: * during sending response, so ftyp atom content should be copied
! 972: */
! 973: ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t),
! 974: ngx_mp4_atom_data(mp4), (size_t) atom_data_size);
! 975:
! 976: atom = &mp4->ftyp_atom_buf;
! 977: atom->temporary = 1;
! 978: atom->pos = ftyp_atom;
! 979: atom->last = ftyp_atom + atom_size;
! 980:
! 981: mp4->ftyp_atom.buf = atom;
! 982: mp4->ftyp_size = atom_size;
! 983: mp4->content_length = atom_size;
! 984:
! 985: ngx_mp4_atom_next(mp4, atom_data_size);
! 986:
! 987: return NGX_OK;
! 988: }
! 989:
! 990:
! 991: /*
! 992: * Small excess buffer to process atoms after moov atom, mp4->buffer_start
! 993: * will be set to this buffer part after moov atom processing.
! 994: */
! 995: #define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS (4 * 1024)
! 996:
! 997: static ngx_int_t
! 998: ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 999: {
! 1000: ngx_int_t rc;
! 1001: ngx_uint_t no_mdat;
! 1002: ngx_buf_t *atom;
! 1003: ngx_http_mp4_conf_t *conf;
! 1004:
! 1005: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom");
! 1006:
! 1007: no_mdat = (mp4->mdat_atom.buf == NULL);
! 1008:
! 1009: if (no_mdat && mp4->start == 0) {
! 1010: /*
! 1011: * send original file if moov atom resides before
! 1012: * mdat atom and client requests integral file
! 1013: */
! 1014: return NGX_DECLINED;
! 1015: }
! 1016:
! 1017: conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);
! 1018:
! 1019: if (atom_data_size > mp4->buffer_size) {
! 1020:
! 1021: if (atom_data_size > conf->max_buffer_size) {
! 1022: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1023: "\"%s\" mp4 moov atom is too large:%uL, "
! 1024: "you may want to increase mp4_max_buffer_size",
! 1025: mp4->file.name.data, atom_data_size);
! 1026: return NGX_ERROR;
! 1027: }
! 1028:
! 1029: ngx_pfree(mp4->request->pool, mp4->buffer);
! 1030: mp4->buffer = NULL;
! 1031: mp4->buffer_pos = NULL;
! 1032: mp4->buffer_end = NULL;
! 1033:
! 1034: mp4->buffer_size = (size_t) atom_data_size
! 1035: + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat;
! 1036: }
! 1037:
! 1038: if (ngx_http_mp4_read(mp4, (size_t) atom_data_size) != NGX_OK) {
! 1039: return NGX_ERROR;
! 1040: }
! 1041:
! 1042: mp4->trak.elts = &mp4->traks;
! 1043: mp4->trak.size = sizeof(ngx_http_mp4_trak_t);
! 1044: mp4->trak.nalloc = 2;
! 1045: mp4->trak.pool = mp4->request->pool;
! 1046:
! 1047: atom = &mp4->moov_atom_buf;
! 1048: atom->temporary = 1;
! 1049: atom->pos = mp4->moov_atom_header;
! 1050: atom->last = mp4->moov_atom_header + 8;
! 1051:
! 1052: mp4->moov_atom.buf = &mp4->moov_atom_buf;
! 1053:
! 1054: rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size);
! 1055:
! 1056: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 moov atom done");
! 1057:
! 1058: if (no_mdat) {
! 1059: mp4->buffer_start = mp4->buffer_pos;
! 1060: mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS;
! 1061:
! 1062: if (mp4->buffer_start + mp4->buffer_size > mp4->buffer_end) {
! 1063: mp4->buffer = NULL;
! 1064: mp4->buffer_pos = NULL;
! 1065: mp4->buffer_end = NULL;
! 1066: }
! 1067:
! 1068: } else {
! 1069: /* skip atoms after moov atom */
! 1070: mp4->offset = mp4->end;
! 1071: }
! 1072:
! 1073: return rc;
! 1074: }
! 1075:
! 1076:
! 1077: static ngx_int_t
! 1078: ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1079: {
! 1080: ngx_buf_t *data;
! 1081:
! 1082: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdat atom");
! 1083:
! 1084: data = &mp4->mdat_data_buf;
! 1085: data->file = &mp4->file;
! 1086: data->in_file = 1;
! 1087: data->last_buf = 1;
! 1088: data->last_in_chain = 1;
! 1089: data->file_last = mp4->offset + atom_data_size;
! 1090:
! 1091: mp4->mdat_atom.buf = &mp4->mdat_atom_buf;
! 1092: mp4->mdat_atom.next = &mp4->mdat_data;
! 1093: mp4->mdat_data.buf = data;
! 1094:
! 1095: if (mp4->trak.nelts) {
! 1096: /* skip atoms after mdat atom */
! 1097: mp4->offset = mp4->end;
! 1098:
! 1099: } else {
! 1100: ngx_mp4_atom_next(mp4, atom_data_size);
! 1101: }
! 1102:
! 1103: return NGX_OK;
! 1104: }
! 1105:
! 1106:
! 1107: static size_t
! 1108: ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset)
! 1109: {
! 1110: off_t atom_data_size;
! 1111: u_char *atom_header;
! 1112: uint32_t atom_header_size;
! 1113: uint64_t atom_size;
! 1114: ngx_buf_t *atom;
! 1115:
! 1116: atom_data_size = mp4->mdat_data.buf->file_last - start_offset;
! 1117: mp4->mdat_data.buf->file_pos = start_offset;
! 1118:
! 1119: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1120: "mdat new offset @%O:%O", start_offset, atom_data_size);
! 1121:
! 1122: atom_header = mp4->mdat_atom_header;
! 1123:
! 1124: if ((uint64_t) atom_data_size > 0xffffffff) {
! 1125: atom_size = 1;
! 1126: atom_header_size = sizeof(ngx_mp4_atom_header64_t);
! 1127: ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),
! 1128: sizeof(ngx_mp4_atom_header64_t) + atom_data_size);
! 1129: } else {
! 1130: atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size;
! 1131: atom_header_size = sizeof(ngx_mp4_atom_header_t);
! 1132: }
! 1133:
! 1134: mp4->content_length += atom_header_size + atom_data_size;
! 1135:
! 1136: ngx_mp4_set_32value(atom_header, atom_size);
! 1137: ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't');
! 1138:
! 1139: atom = &mp4->mdat_atom_buf;
! 1140: atom->temporary = 1;
! 1141: atom->pos = atom_header;
! 1142: atom->last = atom_header + atom_header_size;
! 1143:
! 1144: return atom_header_size;
! 1145: }
! 1146:
! 1147:
! 1148: typedef struct {
! 1149: u_char size[4];
! 1150: u_char name[4];
! 1151: u_char version[1];
! 1152: u_char flags[3];
! 1153: u_char creation_time[4];
! 1154: u_char modification_time[4];
! 1155: u_char timescale[4];
! 1156: u_char duration[4];
! 1157: u_char rate[4];
! 1158: u_char volume[2];
! 1159: u_char reserved[10];
! 1160: u_char matrix[36];
! 1161: u_char preview_time[4];
! 1162: u_char preview_duration[4];
! 1163: u_char poster_time[4];
! 1164: u_char selection_time[4];
! 1165: u_char selection_duration[4];
! 1166: u_char current_time[4];
! 1167: u_char next_track_id[4];
! 1168: } ngx_mp4_mvhd_atom_t;
! 1169:
! 1170: typedef struct {
! 1171: u_char size[4];
! 1172: u_char name[4];
! 1173: u_char version[1];
! 1174: u_char flags[3];
! 1175: u_char creation_time[8];
! 1176: u_char modification_time[8];
! 1177: u_char timescale[4];
! 1178: u_char duration[8];
! 1179: u_char rate[4];
! 1180: u_char volume[2];
! 1181: u_char reserved[10];
! 1182: u_char matrix[36];
! 1183: u_char preview_time[4];
! 1184: u_char preview_duration[4];
! 1185: u_char poster_time[4];
! 1186: u_char selection_time[4];
! 1187: u_char selection_duration[4];
! 1188: u_char current_time[4];
! 1189: u_char next_track_id[4];
! 1190: } ngx_mp4_mvhd64_atom_t;
! 1191:
! 1192:
! 1193: static ngx_int_t
! 1194: ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1195: {
! 1196: u_char *atom_header;
! 1197: size_t atom_size;
! 1198: uint32_t timescale;
! 1199: uint64_t duration;
! 1200: ngx_buf_t *atom;
! 1201: ngx_mp4_mvhd_atom_t *mvhd_atom;
! 1202: ngx_mp4_mvhd64_atom_t *mvhd64_atom;
! 1203:
! 1204: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mvhd atom");
! 1205:
! 1206: atom_header = ngx_mp4_atom_header(mp4);
! 1207: mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header;
! 1208: mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header;
! 1209: ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd');
! 1210:
! 1211: if (ngx_mp4_atom_data_size(ngx_mp4_mvhd_atom_t) > atom_data_size) {
! 1212: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1213: "\"%s\" mp4 mvhd atom too small", mp4->file.name.data);
! 1214: return NGX_ERROR;
! 1215: }
! 1216:
! 1217: if (mvhd_atom->version[0] == 0) {
! 1218: /* version 0: 32-bit duration */
! 1219: timescale = ngx_mp4_get_32value(mvhd_atom->timescale);
! 1220: duration = ngx_mp4_get_32value(mvhd_atom->duration);
! 1221:
! 1222: } else {
! 1223: /* version 1: 64-bit duration */
! 1224:
! 1225: if (ngx_mp4_atom_data_size(ngx_mp4_mvhd64_atom_t) > atom_data_size) {
! 1226: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1227: "\"%s\" mp4 mvhd atom too small",
! 1228: mp4->file.name.data);
! 1229: return NGX_ERROR;
! 1230: }
! 1231:
! 1232: timescale = ngx_mp4_get_32value(mvhd64_atom->timescale);
! 1233: duration = ngx_mp4_get_64value(mvhd64_atom->duration);
! 1234: }
! 1235:
! 1236: mp4->timescale = timescale;
! 1237:
! 1238: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1239: "mvhd timescale:%uD, duration:%uL, time:%.3fs",
! 1240: timescale, duration, (double) duration / timescale);
! 1241:
! 1242: duration -= (uint64_t) mp4->start * timescale / 1000;
! 1243:
! 1244: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1245: "mvhd new duration:%uL, time:%.3fs",
! 1246: duration, (double) duration / timescale);
! 1247:
! 1248: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1249: ngx_mp4_set_32value(mvhd_atom->size, atom_size);
! 1250:
! 1251: if (mvhd_atom->version[0] == 0) {
! 1252: ngx_mp4_set_32value(mvhd_atom->duration, duration);
! 1253:
! 1254: } else {
! 1255: ngx_mp4_set_64value(mvhd64_atom->duration, duration);
! 1256: }
! 1257:
! 1258: atom = &mp4->mvhd_atom_buf;
! 1259: atom->temporary = 1;
! 1260: atom->pos = atom_header;
! 1261: atom->last = atom_header + atom_size;
! 1262:
! 1263: mp4->mvhd_atom.buf = atom;
! 1264:
! 1265: ngx_mp4_atom_next(mp4, atom_data_size);
! 1266:
! 1267: return NGX_OK;
! 1268: }
! 1269:
! 1270:
! 1271: static ngx_int_t
! 1272: ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1273: {
! 1274: u_char *atom_header, *atom_end;
! 1275: off_t atom_file_end;
! 1276: ngx_int_t rc;
! 1277: ngx_buf_t *atom;
! 1278: ngx_http_mp4_trak_t *trak;
! 1279:
! 1280: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 trak atom");
! 1281:
! 1282: trak = ngx_array_push(&mp4->trak);
! 1283: if (trak == NULL) {
! 1284: return NGX_ERROR;
! 1285: }
! 1286:
! 1287: ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
! 1288:
! 1289: atom_header = ngx_mp4_atom_header(mp4);
! 1290: ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k');
! 1291:
! 1292: atom = &trak->trak_atom_buf;
! 1293: atom->temporary = 1;
! 1294: atom->pos = atom_header;
! 1295: atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
! 1296:
! 1297: trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;
! 1298:
! 1299: atom_end = mp4->buffer_pos + atom_data_size;
! 1300: atom_file_end = mp4->offset + atom_data_size;
! 1301:
! 1302: rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);
! 1303:
! 1304: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1305: "mp4 trak atom: %i", rc);
! 1306:
! 1307: if (rc == NGX_DECLINED) {
! 1308: /* skip this trak */
! 1309: ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));
! 1310: mp4->trak.nelts--;
! 1311: mp4->buffer_pos = atom_end;
! 1312: mp4->offset = atom_file_end;
! 1313: return NGX_OK;
! 1314: }
! 1315:
! 1316: return rc;
! 1317: }
! 1318:
! 1319:
! 1320: static void
! 1321: ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,
! 1322: ngx_http_mp4_trak_t *trak)
! 1323: {
! 1324: ngx_buf_t *atom;
! 1325:
! 1326: trak->size += sizeof(ngx_mp4_atom_header_t);
! 1327: atom = &trak->trak_atom_buf;
! 1328: ngx_mp4_set_32value(atom->pos, trak->size);
! 1329: }
! 1330:
! 1331:
! 1332: static ngx_int_t
! 1333: ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1334: {
! 1335: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1336: "\"%s\" mp4 compressed moov atom (cmov) is not supported",
! 1337: mp4->file.name.data);
! 1338:
! 1339: return NGX_ERROR;
! 1340: }
! 1341:
! 1342:
! 1343: typedef struct {
! 1344: u_char size[4];
! 1345: u_char name[4];
! 1346: u_char version[1];
! 1347: u_char flags[3];
! 1348: u_char creation_time[4];
! 1349: u_char modification_time[4];
! 1350: u_char track_id[4];
! 1351: u_char reserved1[4];
! 1352: u_char duration[4];
! 1353: u_char reserved2[8];
! 1354: u_char layer[2];
! 1355: u_char group[2];
! 1356: u_char volume[2];
! 1357: u_char reverved3[2];
! 1358: u_char matrix[36];
! 1359: u_char width[4];
! 1360: u_char heigth[4];
! 1361: } ngx_mp4_tkhd_atom_t;
! 1362:
! 1363: typedef struct {
! 1364: u_char size[4];
! 1365: u_char name[4];
! 1366: u_char version[1];
! 1367: u_char flags[3];
! 1368: u_char creation_time[8];
! 1369: u_char modification_time[8];
! 1370: u_char track_id[4];
! 1371: u_char reserved1[4];
! 1372: u_char duration[8];
! 1373: u_char reserved2[8];
! 1374: u_char layer[2];
! 1375: u_char group[2];
! 1376: u_char volume[2];
! 1377: u_char reverved3[2];
! 1378: u_char matrix[36];
! 1379: u_char width[4];
! 1380: u_char heigth[4];
! 1381: } ngx_mp4_tkhd64_atom_t;
! 1382:
! 1383:
! 1384: static ngx_int_t
! 1385: ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1386: {
! 1387: u_char *atom_header;
! 1388: size_t atom_size;
! 1389: uint64_t duration;
! 1390: ngx_buf_t *atom;
! 1391: ngx_http_mp4_trak_t *trak;
! 1392: ngx_mp4_tkhd_atom_t *tkhd_atom;
! 1393: ngx_mp4_tkhd64_atom_t *tkhd64_atom;
! 1394:
! 1395: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 tkhd atom");
! 1396:
! 1397: atom_header = ngx_mp4_atom_header(mp4);
! 1398: tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header;
! 1399: tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header;
! 1400: ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd');
! 1401:
! 1402: if (ngx_mp4_atom_data_size(ngx_mp4_tkhd_atom_t) > atom_data_size) {
! 1403: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1404: "\"%s\" mp4 tkhd atom too small", mp4->file.name.data);
! 1405: return NGX_ERROR;
! 1406: }
! 1407:
! 1408: if (tkhd_atom->version[0] == 0) {
! 1409: /* version 0: 32-bit duration */
! 1410: duration = ngx_mp4_get_32value(tkhd_atom->duration);
! 1411:
! 1412: } else {
! 1413: /* version 1: 64-bit duration */
! 1414:
! 1415: if (ngx_mp4_atom_data_size(ngx_mp4_tkhd64_atom_t) > atom_data_size) {
! 1416: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1417: "\"%s\" mp4 tkhd atom too small",
! 1418: mp4->file.name.data);
! 1419: return NGX_ERROR;
! 1420: }
! 1421:
! 1422: duration = ngx_mp4_get_64value(tkhd64_atom->duration);
! 1423: }
! 1424:
! 1425: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1426: "tkhd duration:%uL, time:%.3fs",
! 1427: duration, (double) duration / mp4->timescale);
! 1428:
! 1429: duration -= (uint64_t) mp4->start * mp4->timescale / 1000;
! 1430:
! 1431: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1432: "tkhd new duration:%uL, time:%.3fs",
! 1433: duration, (double) duration / mp4->timescale);
! 1434:
! 1435: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1436:
! 1437: trak = ngx_mp4_last_trak(mp4);
! 1438: trak->tkhd_size = atom_size;
! 1439:
! 1440: ngx_mp4_set_32value(tkhd_atom->size, atom_size);
! 1441:
! 1442: if (tkhd_atom->version[0] == 0) {
! 1443: ngx_mp4_set_32value(tkhd_atom->duration, duration);
! 1444:
! 1445: } else {
! 1446: ngx_mp4_set_64value(tkhd64_atom->duration, duration);
! 1447: }
! 1448:
! 1449: atom = &trak->tkhd_atom_buf;
! 1450: atom->temporary = 1;
! 1451: atom->pos = atom_header;
! 1452: atom->last = atom_header + atom_size;
! 1453:
! 1454: trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom;
! 1455:
! 1456: ngx_mp4_atom_next(mp4, atom_data_size);
! 1457:
! 1458: return NGX_OK;
! 1459: }
! 1460:
! 1461:
! 1462: static ngx_int_t
! 1463: ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1464: {
! 1465: u_char *atom_header;
! 1466: ngx_buf_t *atom;
! 1467: ngx_http_mp4_trak_t *trak;
! 1468:
! 1469: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process mdia atom");
! 1470:
! 1471: atom_header = ngx_mp4_atom_header(mp4);
! 1472: ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a');
! 1473:
! 1474: trak = ngx_mp4_last_trak(mp4);
! 1475:
! 1476: atom = &trak->mdia_atom_buf;
! 1477: atom->temporary = 1;
! 1478: atom->pos = atom_header;
! 1479: atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
! 1480:
! 1481: trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom;
! 1482:
! 1483: return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size);
! 1484: }
! 1485:
! 1486:
! 1487: static void
! 1488: ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,
! 1489: ngx_http_mp4_trak_t *trak)
! 1490: {
! 1491: ngx_buf_t *atom;
! 1492:
! 1493: trak->size += sizeof(ngx_mp4_atom_header_t);
! 1494: atom = &trak->mdia_atom_buf;
! 1495: ngx_mp4_set_32value(atom->pos, trak->size);
! 1496: }
! 1497:
! 1498:
! 1499: typedef struct {
! 1500: u_char size[4];
! 1501: u_char name[4];
! 1502: u_char version[1];
! 1503: u_char flags[3];
! 1504: u_char creation_time[4];
! 1505: u_char modification_time[4];
! 1506: u_char timescale[4];
! 1507: u_char duration[4];
! 1508: u_char language[2];
! 1509: u_char quality[2];
! 1510: } ngx_mp4_mdhd_atom_t;
! 1511:
! 1512: typedef struct {
! 1513: u_char size[4];
! 1514: u_char name[4];
! 1515: u_char version[1];
! 1516: u_char flags[3];
! 1517: u_char creation_time[8];
! 1518: u_char modification_time[8];
! 1519: u_char timescale[4];
! 1520: u_char duration[8];
! 1521: u_char language[2];
! 1522: u_char quality[2];
! 1523: } ngx_mp4_mdhd64_atom_t;
! 1524:
! 1525:
! 1526: static ngx_int_t
! 1527: ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1528: {
! 1529: u_char *atom_header;
! 1530: size_t atom_size;
! 1531: uint32_t timescale;
! 1532: uint64_t duration;
! 1533: ngx_buf_t *atom;
! 1534: ngx_http_mp4_trak_t *trak;
! 1535: ngx_mp4_mdhd_atom_t *mdhd_atom;
! 1536: ngx_mp4_mdhd64_atom_t *mdhd64_atom;
! 1537:
! 1538: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 mdhd atom");
! 1539:
! 1540: atom_header = ngx_mp4_atom_header(mp4);
! 1541: mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header;
! 1542: mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header;
! 1543: ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd');
! 1544:
! 1545: if (ngx_mp4_atom_data_size(ngx_mp4_mdhd_atom_t) > atom_data_size) {
! 1546: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1547: "\"%s\" mp4 mdhd atom too small", mp4->file.name.data);
! 1548: return NGX_ERROR;
! 1549: }
! 1550:
! 1551: if (mdhd_atom->version[0] == 0) {
! 1552: /* version 0: everything is 32-bit */
! 1553: timescale = ngx_mp4_get_32value(mdhd_atom->timescale);
! 1554: duration = ngx_mp4_get_32value(mdhd_atom->duration);
! 1555:
! 1556: } else {
! 1557: /* version 1: 64-bit duration and 32-bit timescale */
! 1558:
! 1559: if (ngx_mp4_atom_data_size(ngx_mp4_mdhd64_atom_t) > atom_data_size) {
! 1560: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1561: "\"%s\" mp4 mdhd atom too small",
! 1562: mp4->file.name.data);
! 1563: return NGX_ERROR;
! 1564: }
! 1565:
! 1566: timescale = ngx_mp4_get_32value(mdhd64_atom->timescale);
! 1567: duration = ngx_mp4_get_64value(mdhd64_atom->duration);
! 1568: }
! 1569:
! 1570: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1571: "mdhd timescale:%uD, duration:%uL, time:%.3fs",
! 1572: timescale, duration, (double) duration / timescale);
! 1573:
! 1574: duration -= (uint64_t) mp4->start * timescale / 1000;
! 1575:
! 1576: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1577: "mdhd new duration:%uL, time:%.3fs",
! 1578: duration, (double) duration / timescale);
! 1579:
! 1580: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1581:
! 1582: trak = ngx_mp4_last_trak(mp4);
! 1583: trak->mdhd_size = atom_size;
! 1584: trak->timescale = timescale;
! 1585:
! 1586: ngx_mp4_set_32value(mdhd_atom->size, atom_size);
! 1587:
! 1588: if (mdhd_atom->version[0] == 0) {
! 1589: ngx_mp4_set_32value(mdhd_atom->duration, duration);
! 1590:
! 1591: } else {
! 1592: ngx_mp4_set_64value(mdhd64_atom->duration, duration);
! 1593: }
! 1594:
! 1595: atom = &trak->mdhd_atom_buf;
! 1596: atom->temporary = 1;
! 1597: atom->pos = atom_header;
! 1598: atom->last = atom_header + atom_size;
! 1599:
! 1600: trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom;
! 1601:
! 1602: ngx_mp4_atom_next(mp4, atom_data_size);
! 1603:
! 1604: return NGX_OK;
! 1605: }
! 1606:
! 1607:
! 1608: static ngx_int_t
! 1609: ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1610: {
! 1611: u_char *atom_header;
! 1612: size_t atom_size;
! 1613: ngx_buf_t *atom;
! 1614: ngx_http_mp4_trak_t *trak;
! 1615:
! 1616: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 hdlr atom");
! 1617:
! 1618: atom_header = ngx_mp4_atom_header(mp4);
! 1619: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1620: ngx_mp4_set_32value(atom_header, atom_size);
! 1621: ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r');
! 1622:
! 1623: trak = ngx_mp4_last_trak(mp4);
! 1624:
! 1625: atom = &trak->hdlr_atom_buf;
! 1626: atom->temporary = 1;
! 1627: atom->pos = atom_header;
! 1628: atom->last = atom_header + atom_size;
! 1629:
! 1630: trak->hdlr_size = atom_size;
! 1631: trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom;
! 1632:
! 1633: ngx_mp4_atom_next(mp4, atom_data_size);
! 1634:
! 1635: return NGX_OK;
! 1636: }
! 1637:
! 1638:
! 1639: static ngx_int_t
! 1640: ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1641: {
! 1642: u_char *atom_header;
! 1643: ngx_buf_t *atom;
! 1644: ngx_http_mp4_trak_t *trak;
! 1645:
! 1646: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process minf atom");
! 1647:
! 1648: atom_header = ngx_mp4_atom_header(mp4);
! 1649: ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f');
! 1650:
! 1651: trak = ngx_mp4_last_trak(mp4);
! 1652:
! 1653: atom = &trak->minf_atom_buf;
! 1654: atom->temporary = 1;
! 1655: atom->pos = atom_header;
! 1656: atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
! 1657:
! 1658: trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom;
! 1659:
! 1660: return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size);
! 1661: }
! 1662:
! 1663:
! 1664: static void
! 1665: ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,
! 1666: ngx_http_mp4_trak_t *trak)
! 1667: {
! 1668: ngx_buf_t *atom;
! 1669:
! 1670: trak->size += sizeof(ngx_mp4_atom_header_t)
! 1671: + trak->vmhd_size
! 1672: + trak->smhd_size
! 1673: + trak->dinf_size;
! 1674: atom = &trak->minf_atom_buf;
! 1675: ngx_mp4_set_32value(atom->pos, trak->size);
! 1676: }
! 1677:
! 1678:
! 1679: static ngx_int_t
! 1680: ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1681: {
! 1682: u_char *atom_header;
! 1683: size_t atom_size;
! 1684: ngx_buf_t *atom;
! 1685: ngx_http_mp4_trak_t *trak;
! 1686:
! 1687: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 vmhd atom");
! 1688:
! 1689: atom_header = ngx_mp4_atom_header(mp4);
! 1690: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1691: ngx_mp4_set_32value(atom_header, atom_size);
! 1692: ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd');
! 1693:
! 1694: trak = ngx_mp4_last_trak(mp4);
! 1695:
! 1696: atom = &trak->vmhd_atom_buf;
! 1697: atom->temporary = 1;
! 1698: atom->pos = atom_header;
! 1699: atom->last = atom_header + atom_size;
! 1700:
! 1701: trak->vmhd_size += atom_size;
! 1702: trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom;
! 1703:
! 1704: ngx_mp4_atom_next(mp4, atom_data_size);
! 1705:
! 1706: return NGX_OK;
! 1707: }
! 1708:
! 1709:
! 1710: static ngx_int_t
! 1711: ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1712: {
! 1713: u_char *atom_header;
! 1714: size_t atom_size;
! 1715: ngx_buf_t *atom;
! 1716: ngx_http_mp4_trak_t *trak;
! 1717:
! 1718: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 smhd atom");
! 1719:
! 1720: atom_header = ngx_mp4_atom_header(mp4);
! 1721: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1722: ngx_mp4_set_32value(atom_header, atom_size);
! 1723: ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd');
! 1724:
! 1725: trak = ngx_mp4_last_trak(mp4);
! 1726:
! 1727: atom = &trak->smhd_atom_buf;
! 1728: atom->temporary = 1;
! 1729: atom->pos = atom_header;
! 1730: atom->last = atom_header + atom_size;
! 1731:
! 1732: trak->vmhd_size += atom_size;
! 1733: trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom;
! 1734:
! 1735: ngx_mp4_atom_next(mp4, atom_data_size);
! 1736:
! 1737: return NGX_OK;
! 1738: }
! 1739:
! 1740:
! 1741: static ngx_int_t
! 1742: ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1743: {
! 1744: u_char *atom_header;
! 1745: size_t atom_size;
! 1746: ngx_buf_t *atom;
! 1747: ngx_http_mp4_trak_t *trak;
! 1748:
! 1749: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 dinf atom");
! 1750:
! 1751: atom_header = ngx_mp4_atom_header(mp4);
! 1752: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1753: ngx_mp4_set_32value(atom_header, atom_size);
! 1754: ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f');
! 1755:
! 1756: trak = ngx_mp4_last_trak(mp4);
! 1757:
! 1758: atom = &trak->dinf_atom_buf;
! 1759: atom->temporary = 1;
! 1760: atom->pos = atom_header;
! 1761: atom->last = atom_header + atom_size;
! 1762:
! 1763: trak->dinf_size += atom_size;
! 1764: trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom;
! 1765:
! 1766: ngx_mp4_atom_next(mp4, atom_data_size);
! 1767:
! 1768: return NGX_OK;
! 1769: }
! 1770:
! 1771:
! 1772: static ngx_int_t
! 1773: ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1774: {
! 1775: u_char *atom_header;
! 1776: ngx_buf_t *atom;
! 1777: ngx_http_mp4_trak_t *trak;
! 1778:
! 1779: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "process stbl atom");
! 1780:
! 1781: atom_header = ngx_mp4_atom_header(mp4);
! 1782: ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l');
! 1783:
! 1784: trak = ngx_mp4_last_trak(mp4);
! 1785:
! 1786: atom = &trak->stbl_atom_buf;
! 1787: atom->temporary = 1;
! 1788: atom->pos = atom_header;
! 1789: atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);
! 1790:
! 1791: trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom;
! 1792:
! 1793: return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size);
! 1794: }
! 1795:
! 1796:
! 1797: static void
! 1798: ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,
! 1799: ngx_http_mp4_trak_t *trak)
! 1800: {
! 1801: ngx_buf_t *atom;
! 1802:
! 1803: trak->size += sizeof(ngx_mp4_atom_header_t);
! 1804: atom = &trak->stbl_atom_buf;
! 1805: ngx_mp4_set_32value(atom->pos, trak->size);
! 1806: }
! 1807:
! 1808:
! 1809: typedef struct {
! 1810: u_char size[4];
! 1811: u_char name[4];
! 1812: u_char version[1];
! 1813: u_char flags[3];
! 1814: u_char entries[4];
! 1815:
! 1816: u_char media_size[4];
! 1817: u_char media_name[4];
! 1818: } ngx_mp4_stsd_atom_t;
! 1819:
! 1820:
! 1821: static ngx_int_t
! 1822: ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1823: {
! 1824: u_char *atom_header, *atom_table;
! 1825: size_t atom_size;
! 1826: ngx_buf_t *atom;
! 1827: ngx_mp4_stsd_atom_t *stsd_atom;
! 1828: ngx_http_mp4_trak_t *trak;
! 1829:
! 1830: /* sample description atom */
! 1831:
! 1832: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsd atom");
! 1833:
! 1834: atom_header = ngx_mp4_atom_header(mp4);
! 1835: stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header;
! 1836: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 1837: atom_table = atom_header + atom_size;
! 1838: ngx_mp4_set_32value(stsd_atom->size, atom_size);
! 1839: ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');
! 1840:
! 1841: if (ngx_mp4_atom_data_size(ngx_mp4_stsd_atom_t) > atom_data_size) {
! 1842: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1843: "\"%s\" mp4 stsd atom too small", mp4->file.name.data);
! 1844: return NGX_ERROR;
! 1845: }
! 1846:
! 1847: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1848: "stsd entries:%uD, media:%*s",
! 1849: ngx_mp4_get_32value(stsd_atom->entries),
! 1850: 4, stsd_atom->media_name);
! 1851:
! 1852: trak = ngx_mp4_last_trak(mp4);
! 1853:
! 1854: atom = &trak->stsd_atom_buf;
! 1855: atom->temporary = 1;
! 1856: atom->pos = atom_header;
! 1857: atom->last = atom_table;
! 1858:
! 1859: trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom;
! 1860: trak->size += atom_size;
! 1861:
! 1862: ngx_mp4_atom_next(mp4, atom_data_size);
! 1863:
! 1864: return NGX_OK;
! 1865: }
! 1866:
! 1867:
! 1868: typedef struct {
! 1869: u_char size[4];
! 1870: u_char name[4];
! 1871: u_char version[1];
! 1872: u_char flags[3];
! 1873: u_char entries[4];
! 1874: } ngx_mp4_stts_atom_t;
! 1875:
! 1876: typedef struct {
! 1877: u_char count[4];
! 1878: u_char duration[4];
! 1879: } ngx_mp4_stts_entry_t;
! 1880:
! 1881:
! 1882: static ngx_int_t
! 1883: ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 1884: {
! 1885: u_char *atom_header, *atom_table, *atom_end;
! 1886: uint32_t entries;
! 1887: ngx_buf_t *atom, *data;
! 1888: ngx_mp4_stts_atom_t *stts_atom;
! 1889: ngx_http_mp4_trak_t *trak;
! 1890:
! 1891: /* time-to-sample atom */
! 1892:
! 1893: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stts atom");
! 1894:
! 1895: atom_header = ngx_mp4_atom_header(mp4);
! 1896: stts_atom = (ngx_mp4_stts_atom_t *) atom_header;
! 1897: ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's');
! 1898:
! 1899: if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t) > atom_data_size) {
! 1900: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1901: "\"%s\" mp4 stts atom too small", mp4->file.name.data);
! 1902: return NGX_ERROR;
! 1903: }
! 1904:
! 1905: entries = ngx_mp4_get_32value(stts_atom->entries);
! 1906:
! 1907: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1908: "mp4 time-to-sample entries:%uD", entries);
! 1909:
! 1910: if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t)
! 1911: + entries * sizeof(ngx_mp4_stts_entry_t) > atom_data_size)
! 1912: {
! 1913: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1914: "\"%s\" mp4 stts atom too small", mp4->file.name.data);
! 1915: return NGX_ERROR;
! 1916: }
! 1917:
! 1918: atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t);
! 1919: atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t);
! 1920:
! 1921: trak = ngx_mp4_last_trak(mp4);
! 1922: trak->time_to_sample_entries = entries;
! 1923:
! 1924: atom = &trak->stts_atom_buf;
! 1925: atom->temporary = 1;
! 1926: atom->pos = atom_header;
! 1927: atom->last = atom_table;
! 1928:
! 1929: data = &trak->stts_data_buf;
! 1930: data->temporary = 1;
! 1931: data->pos = atom_table;
! 1932: data->last = atom_end;
! 1933:
! 1934: trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom;
! 1935: trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data;
! 1936:
! 1937: ngx_mp4_atom_next(mp4, atom_data_size);
! 1938:
! 1939: return NGX_OK;
! 1940: }
! 1941:
! 1942:
! 1943: static ngx_int_t
! 1944: ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,
! 1945: ngx_http_mp4_trak_t *trak)
! 1946: {
! 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;
! 1954:
! 1955: /*
! 1956: * mdia.minf.stbl.stts updating requires trak->timescale
! 1957: * from mdia.mdhd atom which may reside after mdia.minf
! 1958: */
! 1959:
! 1960: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1961: "mp4 stts atom update");
! 1962:
! 1963: data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;
! 1964:
! 1965: if (data == NULL) {
! 1966: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 1967: "no mp4 stts atoms were found in \"%s\"",
! 1968: mp4->file.name.data);
! 1969: return NGX_ERROR;
! 1970: }
! 1971:
! 1972: entries = trak->time_to_sample_entries;
! 1973: start_time = (uint64_t) mp4->start * trak->timescale / 1000;
! 1974:
! 1975: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1976: "time-to-sample start_time:%uL", start_time);
! 1977:
! 1978: start_sample = 0;
! 1979: entry = (ngx_mp4_stts_entry_t *) data->pos;
! 1980: end = (ngx_mp4_stts_entry_t *) data->last;
! 1981:
! 1982: while (entry < end) {
! 1983: count = ngx_mp4_get_32value(entry->count);
! 1984: duration = ngx_mp4_get_32value(entry->duration);
! 1985:
! 1986: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 1987: "count:%uD, duration:%uD", count, duration);
! 1988:
! 1989: if (start_time < (uint64_t) count * duration) {
! 1990: start_sample += (ngx_uint_t) (start_time / duration);
! 1991: count -= (uint32_t) (start_time / duration);
! 1992: ngx_mp4_set_32value(entry->count, count);
! 1993: goto found;
! 1994: }
! 1995:
! 1996: start_sample += count;
! 1997: start_time -= count * duration;
! 1998: entries--;
! 1999: entry++;
! 2000: }
! 2001:
! 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);
! 2005:
! 2006: return NGX_ERROR;
! 2007:
! 2008: found:
! 2009:
! 2010: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2011: "start_sample:%ui, new count:%uD", start_sample, count);
! 2012:
! 2013: trak->start_sample = start_sample;
! 2014:
! 2015: data->pos = (u_char *) entry;
! 2016: atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);
! 2017: trak->size += atom_size;
! 2018:
! 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);
! 2023:
! 2024: return NGX_OK;
! 2025: }
! 2026:
! 2027:
! 2028: typedef struct {
! 2029: u_char size[4];
! 2030: u_char name[4];
! 2031: u_char version[1];
! 2032: u_char flags[3];
! 2033: u_char entries[4];
! 2034: } ngx_http_mp4_stss_atom_t;
! 2035:
! 2036:
! 2037: static ngx_int_t
! 2038: ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 2039: {
! 2040: u_char *atom_header, *atom_table, *atom_end;
! 2041: uint32_t entries;
! 2042: ngx_buf_t *atom, *data;
! 2043: ngx_http_mp4_trak_t *trak;
! 2044: ngx_http_mp4_stss_atom_t *stss_atom;
! 2045:
! 2046: /* sync samples atom */
! 2047:
! 2048: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stss atom");
! 2049:
! 2050: atom_header = ngx_mp4_atom_header(mp4);
! 2051: stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header;
! 2052: ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's');
! 2053:
! 2054: if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t) > atom_data_size) {
! 2055: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2056: "\"%s\" mp4 stss atom too small", mp4->file.name.data);
! 2057: return NGX_ERROR;
! 2058: }
! 2059:
! 2060: entries = ngx_mp4_get_32value(stss_atom->entries);
! 2061:
! 2062: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2063: "sync sample entries:%uD", entries);
! 2064:
! 2065: trak = ngx_mp4_last_trak(mp4);
! 2066: trak->sync_samples_entries = entries;
! 2067:
! 2068: atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t);
! 2069:
! 2070: atom = &trak->stss_atom_buf;
! 2071: atom->temporary = 1;
! 2072: atom->pos = atom_header;
! 2073: atom->last = atom_table;
! 2074:
! 2075: if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t)
! 2076: + entries * sizeof(uint32_t) > atom_data_size)
! 2077: {
! 2078: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2079: "\"%s\" mp4 stss atom too small", mp4->file.name.data);
! 2080: return NGX_ERROR;
! 2081: }
! 2082:
! 2083: atom_end = atom_table + entries * sizeof(uint32_t);
! 2084:
! 2085: data = &trak->stss_data_buf;
! 2086: data->temporary = 1;
! 2087: data->pos = atom_table;
! 2088: data->last = atom_end;
! 2089:
! 2090: trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom;
! 2091: trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data;
! 2092:
! 2093: ngx_mp4_atom_next(mp4, atom_data_size);
! 2094:
! 2095: return NGX_OK;
! 2096: }
! 2097:
! 2098:
! 2099: static ngx_int_t
! 2100: ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,
! 2101: ngx_http_mp4_trak_t *trak)
! 2102: {
! 2103: size_t atom_size;
! 2104: uint32_t entries, sample, start_sample, *entry, *end;
! 2105: ngx_buf_t *atom, *data;
! 2106: ngx_http_mp4_stss_atom_t *stss_atom;
! 2107:
! 2108: /*
! 2109: * mdia.minf.stbl.stss updating requires trak->start_sample
! 2110: * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
! 2111: * atom which may reside after mdia.minf
! 2112: */
! 2113:
! 2114: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2115: "mp4 stss atom update");
! 2116:
! 2117: data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;
! 2118:
! 2119: if (data == NULL) {
! 2120: return NGX_OK;
! 2121: }
! 2122:
! 2123: /* sync samples starts from 1 */
! 2124: start_sample = trak->start_sample + 1;
! 2125: entries = trak->sync_samples_entries;
! 2126:
! 2127: entry = (uint32_t *) data->pos;
! 2128: end = (uint32_t *) data->last;
! 2129:
! 2130: while (entry < end) {
! 2131: sample = ngx_mp4_get_32value(entry);
! 2132:
! 2133: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2134: "start:%uD, sync:%uD", start_sample, sample);
! 2135:
! 2136: if (sample >= start_sample) {
! 2137: goto found;
! 2138: }
! 2139:
! 2140: entries--;
! 2141: entry++;
! 2142: }
! 2143:
! 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;
! 2149:
! 2150: found:
! 2151:
! 2152: data->pos = (u_char *) entry;
! 2153:
! 2154: start_sample = trak->start_sample;
! 2155:
! 2156: while (entry < end) {
! 2157: sample = ngx_mp4_get_32value(entry);
! 2158: sample -= start_sample;
! 2159: ngx_mp4_set_32value(entry, sample);
! 2160: entry++;
! 2161: }
! 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: }
! 2174:
! 2175:
! 2176: typedef struct {
! 2177: u_char size[4];
! 2178: u_char name[4];
! 2179: u_char version[1];
! 2180: u_char flags[3];
! 2181: u_char entries[4];
! 2182: } ngx_mp4_ctts_atom_t;
! 2183:
! 2184: typedef struct {
! 2185: u_char count[4];
! 2186: u_char offset[4];
! 2187: } ngx_mp4_ctts_entry_t;
! 2188:
! 2189:
! 2190: static ngx_int_t
! 2191: ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 2192: {
! 2193: u_char *atom_header, *atom_table, *atom_end;
! 2194: uint32_t entries;
! 2195: ngx_buf_t *atom, *data;
! 2196: ngx_mp4_ctts_atom_t *ctts_atom;
! 2197: ngx_http_mp4_trak_t *trak;
! 2198:
! 2199: /* composition offsets atom */
! 2200:
! 2201: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 ctts atom");
! 2202:
! 2203: atom_header = ngx_mp4_atom_header(mp4);
! 2204: ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header;
! 2205: ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's');
! 2206:
! 2207: if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t) > atom_data_size) {
! 2208: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2209: "\"%s\" mp4 ctts atom too small", mp4->file.name.data);
! 2210: return NGX_ERROR;
! 2211: }
! 2212:
! 2213: entries = ngx_mp4_get_32value(ctts_atom->entries);
! 2214:
! 2215: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2216: "composition offset entries:%uD", entries);
! 2217:
! 2218: trak = ngx_mp4_last_trak(mp4);
! 2219: trak->composition_offset_entries = entries;
! 2220:
! 2221: atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t);
! 2222:
! 2223: atom = &trak->ctts_atom_buf;
! 2224: atom->temporary = 1;
! 2225: atom->pos = atom_header;
! 2226: atom->last = atom_table;
! 2227:
! 2228: if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t)
! 2229: + entries * sizeof(ngx_mp4_ctts_entry_t) > atom_data_size)
! 2230: {
! 2231: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2232: "\"%s\" mp4 ctts atom too small", mp4->file.name.data);
! 2233: return NGX_ERROR;
! 2234: }
! 2235:
! 2236: atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t);
! 2237:
! 2238: data = &trak->ctts_data_buf;
! 2239: data->temporary = 1;
! 2240: data->pos = atom_table;
! 2241: data->last = atom_end;
! 2242:
! 2243: trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom;
! 2244: trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data;
! 2245:
! 2246: ngx_mp4_atom_next(mp4, atom_data_size);
! 2247:
! 2248: return NGX_OK;
! 2249: }
! 2250:
! 2251:
! 2252: static void
! 2253: ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,
! 2254: ngx_http_mp4_trak_t *trak)
! 2255: {
! 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;
! 2261:
! 2262: /*
! 2263: * mdia.minf.stbl.ctts updating requires trak->start_sample
! 2264: * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
! 2265: * atom which may reside after mdia.minf
! 2266: */
! 2267:
! 2268: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2269: "mp4 ctts atom update");
! 2270:
! 2271: data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;
! 2272:
! 2273: if (data == NULL) {
! 2274: return;
! 2275: }
! 2276:
! 2277: /* sync samples starts from 1 */
! 2278: start_sample = trak->start_sample + 1;
! 2279: entries = trak->composition_offset_entries;
! 2280: entry = (ngx_mp4_ctts_entry_t *) data->pos;
! 2281: end = (ngx_mp4_ctts_entry_t *) data->last;
! 2282:
! 2283: while (entry < end) {
! 2284: count = ngx_mp4_get_32value(entry->count);
! 2285:
! 2286: ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2287: "start:%uD, count:%uD, offset:%uD",
! 2288: start_sample, count, ngx_mp4_get_32value(entry->offset));
! 2289:
! 2290: if (start_sample <= count) {
! 2291: count -= (start_sample - 1);
! 2292: ngx_mp4_set_32value(entry->count, count);
! 2293: goto found;
! 2294: }
! 2295:
! 2296: start_sample -= count;
! 2297: entries--;
! 2298: entry++;
! 2299: }
! 2300:
! 2301: trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;
! 2302: trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;
! 2303:
! 2304: return;
! 2305:
! 2306: found:
! 2307:
! 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;
! 2314:
! 2315: ngx_mp4_set_32value(ctts_atom->size, atom_size);
! 2316: ngx_mp4_set_32value(ctts_atom->entries, entries);
! 2317:
! 2318: return;
! 2319: }
! 2320:
! 2321:
! 2322: typedef struct {
! 2323: u_char size[4];
! 2324: u_char name[4];
! 2325: u_char version[1];
! 2326: u_char flags[3];
! 2327: u_char entries[4];
! 2328: } ngx_mp4_stsc_atom_t;
! 2329:
! 2330:
! 2331: static ngx_int_t
! 2332: ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 2333: {
! 2334: u_char *atom_header, *atom_table, *atom_end;
! 2335: uint32_t entries;
! 2336: ngx_buf_t *atom, *data;
! 2337: ngx_mp4_stsc_atom_t *stsc_atom;
! 2338: ngx_http_mp4_trak_t *trak;
! 2339:
! 2340: /* sample-to-chunk atom */
! 2341:
! 2342: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsc atom");
! 2343:
! 2344: atom_header = ngx_mp4_atom_header(mp4);
! 2345: stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header;
! 2346: ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c');
! 2347:
! 2348: if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t) > atom_data_size) {
! 2349: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2350: "\"%s\" mp4 stsc atom too small", mp4->file.name.data);
! 2351: return NGX_ERROR;
! 2352: }
! 2353:
! 2354: entries = ngx_mp4_get_32value(stsc_atom->entries);
! 2355:
! 2356: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2357: "sample-to-chunk entries:%uD", entries);
! 2358:
! 2359: if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t)
! 2360: + entries * sizeof(ngx_mp4_stsc_entry_t) > atom_data_size)
! 2361: {
! 2362: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2363: "\"%s\" mp4 stsc atom too small", mp4->file.name.data);
! 2364: return NGX_ERROR;
! 2365: }
! 2366:
! 2367: atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t);
! 2368: atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t);
! 2369:
! 2370: trak = ngx_mp4_last_trak(mp4);
! 2371: trak->sample_to_chunk_entries = entries;
! 2372:
! 2373: atom = &trak->stsc_atom_buf;
! 2374: atom->temporary = 1;
! 2375: atom->pos = atom_header;
! 2376: atom->last = atom_table;
! 2377:
! 2378: data = &trak->stsc_data_buf;
! 2379: data->temporary = 1;
! 2380: data->pos = atom_table;
! 2381: data->last = atom_end;
! 2382:
! 2383: trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom;
! 2384: trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data;
! 2385:
! 2386: ngx_mp4_atom_next(mp4, atom_data_size);
! 2387:
! 2388: return NGX_OK;
! 2389: }
! 2390:
! 2391:
! 2392: static ngx_int_t
! 2393: ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,
! 2394: ngx_http_mp4_trak_t *trak)
! 2395: {
! 2396: size_t atom_size;
! 2397: uint32_t start_sample, entries, chunk, samples, id,
! 2398: next_chunk, n;
! 2399: ngx_buf_t *atom, *data, *buf;
! 2400: ngx_mp4_stsc_atom_t *stsc_atom;
! 2401: ngx_mp4_stsc_entry_t *entry, *first, *end;
! 2402:
! 2403: /*
! 2404: * mdia.minf.stbl.stsc updating requires trak->start_sample
! 2405: * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
! 2406: * atom which may reside after mdia.minf
! 2407: */
! 2408:
! 2409: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2410: "mp4 stsc atom update");
! 2411:
! 2412: data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;
! 2413:
! 2414: if (data == NULL) {
! 2415: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2416: "no mp4 stsc atoms were found in \"%s\"",
! 2417: mp4->file.name.data);
! 2418: return NGX_ERROR;
! 2419: }
! 2420:
! 2421: if (trak->sample_to_chunk_entries == 0) {
! 2422: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2423: "zero number of entries in stsc atom in \"%s\"",
! 2424: mp4->file.name.data);
! 2425: return NGX_ERROR;
! 2426: }
! 2427:
! 2428: start_sample = (uint32_t) trak->start_sample;
! 2429: entries = trak->sample_to_chunk_entries - 1;
! 2430:
! 2431: entry = (ngx_mp4_stsc_entry_t *) data->pos;
! 2432: end = (ngx_mp4_stsc_entry_t *) data->last;
! 2433:
! 2434: chunk = ngx_mp4_get_32value(entry->chunk);
! 2435: samples = ngx_mp4_get_32value(entry->samples);
! 2436: id = ngx_mp4_get_32value(entry->id);
! 2437: entry++;
! 2438:
! 2439: while (entry < end) {
! 2440:
! 2441: next_chunk = ngx_mp4_get_32value(entry->chunk);
! 2442:
! 2443: ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2444: "start_sample:%uD, chunk:%uD, chunks:%uD, "
! 2445: "samples:%uD, id:%uD",
! 2446: start_sample, chunk, next_chunk - chunk, samples, id);
! 2447:
! 2448: n = (next_chunk - chunk) * samples;
! 2449:
! 2450: if (start_sample <= n) {
! 2451: goto found;
! 2452: }
! 2453:
! 2454: start_sample -= n;
! 2455:
! 2456: chunk = next_chunk;
! 2457: samples = ngx_mp4_get_32value(entry->samples);
! 2458: id = ngx_mp4_get_32value(entry->id);
! 2459: entries--;
! 2460: entry++;
! 2461: }
! 2462:
! 2463: next_chunk = trak->chunks;
! 2464:
! 2465: ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2466: "start_sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
! 2467: start_sample, chunk, next_chunk - chunk, samples);
! 2468:
! 2469: n = (next_chunk - chunk) * samples;
! 2470:
! 2471: if (start_sample > n) {
! 2472: 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);
! 2475: return NGX_ERROR;
! 2476: }
! 2477:
! 2478: found:
! 2479:
! 2480: entries++;
! 2481: entry--;
! 2482:
! 2483: if (samples == 0) {
! 2484: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2485: "zero number of samples in \"%s\"",
! 2486: mp4->file.name.data);
! 2487: return NGX_ERROR;
! 2488: }
! 2489:
! 2490: trak->start_chunk = chunk - 1;
! 2491:
! 2492: trak->start_chunk += start_sample / samples;
! 2493: trak->chunk_samples = start_sample % samples;
! 2494:
! 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);
! 2498:
! 2499: data->pos = (u_char *) entry;
! 2500: atom_size = sizeof(ngx_mp4_stsc_atom_t) + (data->last - data->pos);
! 2501:
! 2502: ngx_mp4_set_32value(entry->chunk, 1);
! 2503:
! 2504: if (trak->chunk_samples && next_chunk - trak->start_chunk == 2) {
! 2505:
! 2506: /* last chunk in the entry */
! 2507:
! 2508: ngx_mp4_set_32value(entry->samples, samples - trak->chunk_samples);
! 2509:
! 2510: } else if (trak->chunk_samples) {
! 2511:
! 2512: first = &trak->stsc_chunk_entry;
! 2513: ngx_mp4_set_32value(first->chunk, 1);
! 2514: ngx_mp4_set_32value(first->samples, samples - trak->chunk_samples);
! 2515: ngx_mp4_set_32value(first->id, id);
! 2516:
! 2517: buf = &trak->stsc_chunk_buf;
! 2518: buf->temporary = 1;
! 2519: buf->pos = (u_char *) first;
! 2520: buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);
! 2521:
! 2522: trak->out[NGX_HTTP_MP4_STSC_CHUNK].buf = buf;
! 2523:
! 2524: ngx_mp4_set_32value(entry->chunk, 2);
! 2525:
! 2526: entries++;
! 2527: atom_size += sizeof(ngx_mp4_stsc_entry_t);
! 2528: }
! 2529:
! 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: }
! 2535:
! 2536: trak->size += atom_size;
! 2537:
! 2538: atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;
! 2539: stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;
! 2540:
! 2541: ngx_mp4_set_32value(stsc_atom->size, atom_size);
! 2542: ngx_mp4_set_32value(stsc_atom->entries, entries);
! 2543:
! 2544: return NGX_OK;
! 2545: }
! 2546:
! 2547:
! 2548: typedef struct {
! 2549: u_char size[4];
! 2550: u_char name[4];
! 2551: u_char version[1];
! 2552: u_char flags[3];
! 2553: u_char uniform_size[4];
! 2554: u_char entries[4];
! 2555: } ngx_mp4_stsz_atom_t;
! 2556:
! 2557:
! 2558: static ngx_int_t
! 2559: ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 2560: {
! 2561: u_char *atom_header, *atom_table, *atom_end;
! 2562: size_t atom_size;
! 2563: uint32_t entries, size;
! 2564: ngx_buf_t *atom, *data;
! 2565: ngx_mp4_stsz_atom_t *stsz_atom;
! 2566: ngx_http_mp4_trak_t *trak;
! 2567:
! 2568: /* sample sizes atom */
! 2569:
! 2570: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stsz atom");
! 2571:
! 2572: atom_header = ngx_mp4_atom_header(mp4);
! 2573: stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header;
! 2574: ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z');
! 2575:
! 2576: if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t) > atom_data_size) {
! 2577: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2578: "\"%s\" mp4 stsz atom too small", mp4->file.name.data);
! 2579: return NGX_ERROR;
! 2580: }
! 2581:
! 2582: size = ngx_mp4_get_32value(stsz_atom->uniform_size);
! 2583: entries = ngx_mp4_get_32value(stsz_atom->entries);
! 2584:
! 2585: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2586: "sample uniform size:%uD, entries:%uD", size, entries);
! 2587:
! 2588: trak = ngx_mp4_last_trak(mp4);
! 2589: trak->sample_sizes_entries = entries;
! 2590:
! 2591: atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t);
! 2592:
! 2593: atom = &trak->stsz_atom_buf;
! 2594: atom->temporary = 1;
! 2595: atom->pos = atom_header;
! 2596: atom->last = atom_table;
! 2597:
! 2598: trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom;
! 2599:
! 2600: if (size == 0) {
! 2601: if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t)
! 2602: + entries * sizeof(uint32_t) > atom_data_size)
! 2603: {
! 2604: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2605: "\"%s\" mp4 stsz atom too small",
! 2606: mp4->file.name.data);
! 2607: return NGX_ERROR;
! 2608: }
! 2609:
! 2610: atom_end = atom_table + entries * sizeof(uint32_t);
! 2611:
! 2612: data = &trak->stsz_data_buf;
! 2613: data->temporary = 1;
! 2614: data->pos = atom_table;
! 2615: data->last = atom_end;
! 2616:
! 2617: trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;
! 2618:
! 2619: } else {
! 2620: /* if size != 0 then all samples are the same size */
! 2621: /* TODO : chunk samples */
! 2622: atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;
! 2623: ngx_mp4_set_32value(atom_header, atom_size);
! 2624: trak->size += atom_size;
! 2625: }
! 2626:
! 2627: ngx_mp4_atom_next(mp4, atom_data_size);
! 2628:
! 2629: return NGX_OK;
! 2630: }
! 2631:
! 2632:
! 2633: static ngx_int_t
! 2634: ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,
! 2635: ngx_http_mp4_trak_t *trak)
! 2636: {
! 2637: size_t atom_size;
! 2638: uint32_t *pos, *end;
! 2639: ngx_buf_t *atom, *data;
! 2640: ngx_mp4_stsz_atom_t *stsz_atom;
! 2641:
! 2642: /*
! 2643: * mdia.minf.stbl.stsz updating requires trak->start_sample
! 2644: * from mdia.minf.stbl.stts which depends on value from mdia.mdhd
! 2645: * atom which may reside after mdia.minf
! 2646: */
! 2647:
! 2648: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2649: "mp4 stsz atom update");
! 2650:
! 2651: data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;
! 2652:
! 2653: if (data) {
! 2654: if (trak->start_sample > trak->sample_sizes_entries) {
! 2655: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2656: "start time is out mp4 stsz samples in \"%s\"",
! 2657: mp4->file.name.data);
! 2658: return NGX_ERROR;
! 2659: }
! 2660:
! 2661: data->pos += trak->start_sample * sizeof(uint32_t);
! 2662: end = (uint32_t *) data->pos;
! 2663:
! 2664: for (pos = end - trak->chunk_samples; pos < end; pos++) {
! 2665: trak->chunk_samples_size += ngx_mp4_get_32value(pos);
! 2666: }
! 2667:
! 2668: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2669: "chunk samples sizes:%uL", trak->chunk_samples_size);
! 2670:
! 2671: atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);
! 2672: trak->size += atom_size;
! 2673:
! 2674: atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;
! 2675: stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;
! 2676:
! 2677: 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);
! 2680: }
! 2681:
! 2682: return NGX_OK;
! 2683: }
! 2684:
! 2685:
! 2686: typedef struct {
! 2687: u_char size[4];
! 2688: u_char name[4];
! 2689: u_char version[1];
! 2690: u_char flags[3];
! 2691: u_char entries[4];
! 2692: } ngx_mp4_stco_atom_t;
! 2693:
! 2694:
! 2695: static ngx_int_t
! 2696: ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 2697: {
! 2698: u_char *atom_header, *atom_table, *atom_end;
! 2699: uint32_t entries;
! 2700: ngx_buf_t *atom, *data;
! 2701: ngx_mp4_stco_atom_t *stco_atom;
! 2702: ngx_http_mp4_trak_t *trak;
! 2703:
! 2704: /* chunk offsets atom */
! 2705:
! 2706: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 stco atom");
! 2707:
! 2708: atom_header = ngx_mp4_atom_header(mp4);
! 2709: stco_atom = (ngx_mp4_stco_atom_t *) atom_header;
! 2710: ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o');
! 2711:
! 2712: if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t) > atom_data_size) {
! 2713: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2714: "\"%s\" mp4 stco atom too small", mp4->file.name.data);
! 2715: return NGX_ERROR;
! 2716: }
! 2717:
! 2718: entries = ngx_mp4_get_32value(stco_atom->entries);
! 2719:
! 2720: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
! 2721:
! 2722: if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t)
! 2723: + entries * sizeof(uint32_t) > atom_data_size)
! 2724: {
! 2725: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2726: "\"%s\" mp4 stco atom too small", mp4->file.name.data);
! 2727: return NGX_ERROR;
! 2728: }
! 2729:
! 2730: atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t);
! 2731: atom_end = atom_table + entries * sizeof(uint32_t);
! 2732:
! 2733: trak = ngx_mp4_last_trak(mp4);
! 2734: trak->chunks = entries;
! 2735:
! 2736: atom = &trak->stco_atom_buf;
! 2737: atom->temporary = 1;
! 2738: atom->pos = atom_header;
! 2739: atom->last = atom_table;
! 2740:
! 2741: data = &trak->stco_data_buf;
! 2742: data->temporary = 1;
! 2743: data->pos = atom_table;
! 2744: data->last = atom_end;
! 2745:
! 2746: trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;
! 2747: trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data;
! 2748:
! 2749: ngx_mp4_atom_next(mp4, atom_data_size);
! 2750:
! 2751: return NGX_OK;
! 2752: }
! 2753:
! 2754:
! 2755: static ngx_int_t
! 2756: ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,
! 2757: ngx_http_mp4_trak_t *trak)
! 2758: {
! 2759: size_t atom_size;
! 2760: ngx_buf_t *atom, *data;
! 2761: ngx_mp4_stco_atom_t *stco_atom;
! 2762:
! 2763: /*
! 2764: * mdia.minf.stbl.stco updating requires trak->start_chunk
! 2765: * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
! 2766: * atom which may reside after mdia.minf
! 2767: */
! 2768:
! 2769: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2770: "mp4 stco atom update");
! 2771:
! 2772: data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
! 2773:
! 2774: if (data == NULL) {
! 2775: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2776: "no mp4 stco atoms were found in \"%s\"",
! 2777: mp4->file.name.data);
! 2778: return NGX_ERROR;
! 2779: }
! 2780:
! 2781: if (trak->start_chunk > trak->chunks) {
! 2782: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2783: "start time is out mp4 stco chunks in \"%s\"",
! 2784: mp4->file.name.data);
! 2785: return NGX_ERROR;
! 2786: }
! 2787:
! 2788: 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:
! 2792: trak->start_offset = ngx_mp4_get_32value(data->pos);
! 2793: trak->start_offset += trak->chunk_samples_size;
! 2794: ngx_mp4_set_32value(data->pos, trak->start_offset);
! 2795:
! 2796: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2797: "start chunk offset:%uD", trak->start_offset);
! 2798:
! 2799: atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;
! 2800: stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;
! 2801:
! 2802: ngx_mp4_set_32value(stco_atom->size, atom_size);
! 2803: ngx_mp4_set_32value(stco_atom->entries, trak->chunks - trak->start_chunk);
! 2804:
! 2805: return NGX_OK;
! 2806: }
! 2807:
! 2808:
! 2809: static void
! 2810: ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,
! 2811: ngx_http_mp4_trak_t *trak, int32_t adjustment)
! 2812: {
! 2813: uint32_t offset, *entry, *end;
! 2814: ngx_buf_t *data;
! 2815:
! 2816: /*
! 2817: * moov.trak.mdia.minf.stbl.stco adjustment requires
! 2818: * minimal start offset of all traks and new moov atom size
! 2819: */
! 2820:
! 2821: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2822: "mp4 stco atom adjustment");
! 2823:
! 2824: data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;
! 2825: entry = (uint32_t *) data->pos;
! 2826: end = (uint32_t *) data->last;
! 2827:
! 2828: while (entry < end) {
! 2829: offset = ngx_mp4_get_32value(entry);
! 2830: offset += adjustment;
! 2831: ngx_mp4_set_32value(entry, offset);
! 2832: entry++;
! 2833: }
! 2834: }
! 2835:
! 2836:
! 2837: typedef struct {
! 2838: u_char size[4];
! 2839: u_char name[4];
! 2840: u_char version[1];
! 2841: u_char flags[3];
! 2842: u_char entries[4];
! 2843: } ngx_mp4_co64_atom_t;
! 2844:
! 2845:
! 2846: static ngx_int_t
! 2847: ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)
! 2848: {
! 2849: u_char *atom_header, *atom_table, *atom_end;
! 2850: uint32_t entries;
! 2851: ngx_buf_t *atom, *data;
! 2852: ngx_mp4_co64_atom_t *co64_atom;
! 2853: ngx_http_mp4_trak_t *trak;
! 2854:
! 2855: /* chunk offsets atom */
! 2856:
! 2857: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "mp4 co64 atom");
! 2858:
! 2859: atom_header = ngx_mp4_atom_header(mp4);
! 2860: co64_atom = (ngx_mp4_co64_atom_t *) atom_header;
! 2861: ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');
! 2862:
! 2863: if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t) > atom_data_size) {
! 2864: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2865: "\"%s\" mp4 co64 atom too small", mp4->file.name.data);
! 2866: return NGX_ERROR;
! 2867: }
! 2868:
! 2869: entries = ngx_mp4_get_32value(co64_atom->entries);
! 2870:
! 2871: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, "chunks:%uD", entries);
! 2872:
! 2873: if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t)
! 2874: + entries * sizeof(uint64_t) > atom_data_size)
! 2875: {
! 2876: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2877: "\"%s\" mp4 co64 atom too small", mp4->file.name.data);
! 2878: return NGX_ERROR;
! 2879: }
! 2880:
! 2881: atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);
! 2882: atom_end = atom_table + entries * sizeof(uint64_t);
! 2883:
! 2884: trak = ngx_mp4_last_trak(mp4);
! 2885: trak->chunks = entries;
! 2886:
! 2887: atom = &trak->co64_atom_buf;
! 2888: atom->temporary = 1;
! 2889: atom->pos = atom_header;
! 2890: atom->last = atom_table;
! 2891:
! 2892: data = &trak->co64_data_buf;
! 2893: data->temporary = 1;
! 2894: data->pos = atom_table;
! 2895: data->last = atom_end;
! 2896:
! 2897: trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;
! 2898: trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;
! 2899:
! 2900: ngx_mp4_atom_next(mp4, atom_data_size);
! 2901:
! 2902: return NGX_OK;
! 2903: }
! 2904:
! 2905:
! 2906: static ngx_int_t
! 2907: ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,
! 2908: ngx_http_mp4_trak_t *trak)
! 2909: {
! 2910: size_t atom_size;
! 2911: ngx_buf_t *atom, *data;
! 2912: ngx_mp4_co64_atom_t *co64_atom;
! 2913:
! 2914: /*
! 2915: * mdia.minf.stbl.co64 updating requires trak->start_chunk
! 2916: * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd
! 2917: * atom which may reside after mdia.minf
! 2918: */
! 2919:
! 2920: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2921: "mp4 co64 atom update");
! 2922:
! 2923: data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
! 2924:
! 2925: if (data == NULL) {
! 2926: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2927: "no mp4 co64 atoms were found in \"%s\"",
! 2928: mp4->file.name.data);
! 2929: return NGX_ERROR;
! 2930: }
! 2931:
! 2932: if (trak->start_chunk > trak->chunks) {
! 2933: ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
! 2934: "start time is out mp4 co64 chunks in \"%s\"",
! 2935: mp4->file.name.data);
! 2936: return NGX_ERROR;
! 2937: }
! 2938:
! 2939: 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:
! 2943: trak->start_offset = ngx_mp4_get_64value(data->pos);
! 2944: trak->start_offset += trak->chunk_samples_size;
! 2945: ngx_mp4_set_64value(data->pos, trak->start_offset);
! 2946:
! 2947: ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2948: "start chunk offset:%uL", trak->start_offset);
! 2949:
! 2950: atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;
! 2951: co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;
! 2952:
! 2953: ngx_mp4_set_32value(co64_atom->size, atom_size);
! 2954: ngx_mp4_set_32value(co64_atom->entries, trak->chunks - trak->start_chunk);
! 2955:
! 2956: return NGX_OK;
! 2957: }
! 2958:
! 2959:
! 2960: static void
! 2961: ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,
! 2962: ngx_http_mp4_trak_t *trak, off_t adjustment)
! 2963: {
! 2964: uint64_t offset, *entry, *end;
! 2965: ngx_buf_t *data;
! 2966:
! 2967: /*
! 2968: * moov.trak.mdia.minf.stbl.co64 adjustment requires
! 2969: * minimal start offset of all traks and new moov atom size
! 2970: */
! 2971:
! 2972: ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
! 2973: "mp4 co64 atom adjustment");
! 2974:
! 2975: data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;
! 2976: entry = (uint64_t *) data->pos;
! 2977: end = (uint64_t *) data->last;
! 2978:
! 2979: while (entry < end) {
! 2980: offset = ngx_mp4_get_64value(entry);
! 2981: offset += adjustment;
! 2982: ngx_mp4_set_64value(entry, offset);
! 2983: entry++;
! 2984: }
! 2985: }
! 2986:
! 2987:
! 2988: static char *
! 2989: ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
! 2990: {
! 2991: ngx_http_core_loc_conf_t *clcf;
! 2992:
! 2993: clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
! 2994: clcf->handler = ngx_http_mp4_handler;
! 2995:
! 2996: return NGX_CONF_OK;
! 2997: }
! 2998:
! 2999:
! 3000: static void *
! 3001: ngx_http_mp4_create_conf(ngx_conf_t *cf)
! 3002: {
! 3003: ngx_http_mp4_conf_t *conf;
! 3004:
! 3005: conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t));
! 3006: if (conf == NULL) {
! 3007: return NULL;
! 3008: }
! 3009:
! 3010: conf->buffer_size = NGX_CONF_UNSET_SIZE;
! 3011: conf->max_buffer_size = NGX_CONF_UNSET_SIZE;
! 3012:
! 3013: return conf;
! 3014: }
! 3015:
! 3016:
! 3017: static char *
! 3018: ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)
! 3019: {
! 3020: ngx_http_mp4_conf_t *prev = parent;
! 3021: ngx_http_mp4_conf_t *conf = child;
! 3022:
! 3023: ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024);
! 3024: ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size,
! 3025: 10 * 1024 * 1024);
! 3026:
! 3027: return NGX_CONF_OK;
! 3028: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>