├── .gitignore ├── README.md └── src ├── Makefile ├── mp4.cc ├── mp4_common.h ├── mp4_meta.cc └── mp4_meta.h /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | *.lo 4 | *.so 5 | *.swp 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Name 3 | ts-mp4 - MP4 streaming media, implement as plugin of Apache TrafficServer 4 | 5 | ## Status 6 | This module is under active development and is production ready. 7 | 8 | ## Description 9 | This module provides streaming media server support for MP4 files. User can send a HTTP request to the server with `start` argument which is measured in seconds, and the server will respond with the stream such that its start position corresponds to the requested time, for example: 10 | 11 | ```c 12 | http://v.foo.com/dota2.mp4?start=290.12 13 | ``` 14 | 15 | This allows performing a random seeking at any time. We can use flash player, vlc, mplayer or chrome to play the streaming media. 16 | 17 | We can write this in remap.config: 18 | 19 | ```c 20 | map http://v.foo.com/ http://i.foo.com/ @plugin=/xx/libtsmp4.so 21 | ``` 22 | 23 | ## System Requirements 24 | * linux/freebsd 64bits 25 | * trafficserver 26 | 27 | ## Build 28 | **step1**: get ts-mp4 29 | 30 | git clone https://github.com/portl4t/ts-mp4.git 31 | cd ts-mp4/src 32 | 33 | **step2**: build, requires linux/freebsd (64bits is recommended) 34 | 35 | make 36 | 37 | **step3**: modify remap.config and restart the trafficserver 38 | 39 | ## Note 40 | It is not a good idea to cache a large mp4 file, as we have to generate new meta data if `start` exists, and it will take a long time to accomplish. As far as I know, many video sites will cut a large video file into many small mp4 files, and each small mp4 file will be less than 80M(bytes), it will be a reasonable choice. 41 | 42 | ## History 43 | * 2015-01-08, prepare to add to master 44 | * 2015-01-01, output from the last key sample if start time can not be reached 45 | * 2014-12-30, change TSIOBufferReaderCopy to IOBufferReaderCopy, support freebsd 46 | * 2014-12-29, fix integer overflow, support more than one stts entries. 47 | * 2014-12-27, fix memory leak. 48 | * 2014-12-24, fix bugs that cache can be corrupted. 49 | * 2014-12-21, first commit. 50 | 51 | ## See Also 52 | * [ts-flv](https://github.com/portl4t/ts-flv) FLV streaming media, implement as plugin of Apache TrafficServer. 53 | 54 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: .cc .o .lo 2 | 3 | COMPILE = $(CXX) -Wall -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -g -O 4 | INC_PATH = 5 | LIB_PATH = -L/usr/local/lib 6 | 7 | SHARED_OBJS = mp4.lo mp4_meta.lo 8 | 9 | ALL_OBJS = $(SHARED_OBJS) 10 | 11 | ALL_PRGS = 12 | ALL_LIBS = libtsmp4.so 13 | 14 | all: $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) 15 | 16 | libtsmp4.so: 17 | $(COMPILE) -o $@ $< -shared $(SHARED_OBJS) $(LIB_PATH) 18 | .cc: 19 | $(COMPILE) -o $@ $< $(SHARED_OBJS) $(LIB_PATH) $(INC_PATH) 20 | .cc.o: 21 | $(COMPILE) -c -o $@ $< $(INC_PATH) 22 | .cc.lo: 23 | $(COMPILE) -c -fPIC -o $@ $< $(INC_PATH) 24 | install: 25 | /bin/cp -f $(ALL_LIBS) /usr/lib64/trafficserver/plugins/ 26 | clean: 27 | rm -rf $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS) build 28 | 29 | -------------------------------------------------------------------------------- /src/mp4.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | #include "mp4_common.h" 20 | 21 | 22 | char * ts_arg(const char *param, size_t param_len, const char *key, size_t key_len, size_t *val_len); 23 | static int mp4_handler(TSCont contp, TSEvent event, void *edata); 24 | static void mp4_cache_lookup_complete(Mp4Context *mc, TSHttpTxn txnp); 25 | static void mp4_read_response(Mp4Context *mc, TSHttpTxn txnp); 26 | static void mp4_add_transform(Mp4Context *mc, TSHttpTxn txnp); 27 | static int mp4_transform_entry(TSCont contp, TSEvent event, void *edata); 28 | static int mp4_transform_handler(TSCont contp, Mp4Context *mc); 29 | static int mp4_parse_meta(Mp4TransformContext *mtc, bool body_complete); 30 | 31 | 32 | TSReturnCode 33 | TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size) 34 | { 35 | if (!api_info) { 36 | snprintf(errbuf, errbuf_size, "[TSRemapInit] - Invalid TSRemapInterface argument"); 37 | return TS_ERROR; 38 | } 39 | 40 | if (api_info->size < sizeof(TSRemapInterface)) { 41 | snprintf(errbuf, errbuf_size, "[TSRemapInit] - Incorrect size of TSRemapInterface structure"); 42 | return TS_ERROR; 43 | } 44 | 45 | return TS_SUCCESS; 46 | } 47 | 48 | TSReturnCode 49 | TSRemapNewInstance(int argc, char** /* argv ATS_UNUSED */, void** ih, char* errbuf, int errbuf_size) 50 | { 51 | if (argc > 2) { 52 | snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Argument should be removed"); 53 | } 54 | 55 | *ih = NULL; 56 | return TS_SUCCESS; 57 | } 58 | 59 | void 60 | TSRemapDeleteInstance(void* /* ih ATS_UNUSED */) 61 | { 62 | return; 63 | } 64 | 65 | TSRemapStatus 66 | TSRemapDoRemap(void* /* ih ATS_UNUSED */, TSHttpTxn rh, TSRemapRequestInfo *rri) 67 | { 68 | const char *method, *query, *path; 69 | int method_len, query_len, path_len; 70 | size_t val_len; 71 | const char *val; 72 | int ret; 73 | float start; 74 | char buf[1024]; 75 | int buf_len; 76 | int left, right; 77 | TSMLoc ae_field, range_field; 78 | TSCont contp; 79 | Mp4Context *mc; 80 | 81 | method = TSHttpHdrMethodGet(rri->requestBufp, rri->requestHdrp, &method_len); 82 | if (method != TS_HTTP_METHOD_GET) { 83 | return TSREMAP_NO_REMAP; 84 | } 85 | 86 | // check suffix 87 | path = TSUrlPathGet(rri->requestBufp, rri->requestUrl, &path_len); 88 | 89 | if (path == NULL || path_len <= 4) { 90 | return TSREMAP_NO_REMAP; 91 | 92 | } else if (strncasecmp(path + path_len - 4, ".mp4", 4) != 0) { 93 | return TSREMAP_NO_REMAP; 94 | } 95 | 96 | start = 0; 97 | query = TSUrlHttpQueryGet(rri->requestBufp, rri->requestUrl, &query_len); 98 | 99 | val = ts_arg(query, query_len, "start", sizeof("start")-1, &val_len); 100 | if (val != NULL) { 101 | ret = sscanf(val, "%f", &start); 102 | if (ret != 1) 103 | start = 0; 104 | } 105 | 106 | if (start == 0) { 107 | return TSREMAP_NO_REMAP; 108 | 109 | } else if (start < 0) { 110 | TSHttpTxnSetHttpRetStatus(rh, TS_HTTP_STATUS_BAD_REQUEST); 111 | TSHttpTxnErrorBodySet(rh, TSstrdup("Invalid request."), sizeof("Invalid request.")-1, NULL); 112 | } 113 | 114 | // reset args 115 | left = val - sizeof("start") - query; 116 | right = query + query_len - val - val_len; 117 | 118 | if (left > 0) { 119 | left--; 120 | } 121 | 122 | if (left == 0 && right > 0) { 123 | right--; 124 | } 125 | 126 | buf_len = sprintf(buf, "%.*s%.*s", left, query, right, query+query_len-right); 127 | TSUrlHttpQuerySet(rri->requestBufp, rri->requestUrl, buf, buf_len); 128 | 129 | // remove Accept-Encoding 130 | ae_field = TSMimeHdrFieldFind(rri->requestBufp, rri->requestHdrp, 131 | TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING); 132 | if (ae_field) { 133 | TSMimeHdrFieldDestroy(rri->requestBufp, rri->requestHdrp, ae_field); 134 | TSHandleMLocRelease(rri->requestBufp, rri->requestHdrp, ae_field); 135 | } 136 | 137 | // remove Range 138 | range_field = TSMimeHdrFieldFind(rri->requestBufp, rri->requestHdrp, 139 | TS_MIME_FIELD_RANGE, TS_MIME_LEN_RANGE); 140 | if (range_field) { 141 | TSMimeHdrFieldDestroy(rri->requestBufp, rri->requestHdrp, range_field); 142 | TSHandleMLocRelease(rri->requestBufp, rri->requestHdrp, range_field); 143 | } 144 | 145 | mc = new Mp4Context(start); 146 | contp = TSContCreate(mp4_handler, NULL); 147 | TSContDataSet(contp, mc); 148 | 149 | TSHttpTxnHookAdd(rh, TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp); 150 | TSHttpTxnHookAdd(rh, TS_HTTP_READ_RESPONSE_HDR_HOOK, contp); 151 | TSHttpTxnHookAdd(rh, TS_HTTP_TXN_CLOSE_HOOK, contp); 152 | return TSREMAP_NO_REMAP; 153 | } 154 | 155 | static int 156 | mp4_handler(TSCont contp, TSEvent event, void *edata) 157 | { 158 | TSHttpTxn txnp; 159 | Mp4Context *mc; 160 | 161 | txnp = (TSHttpTxn)edata; 162 | mc = (Mp4Context*)TSContDataGet(contp); 163 | 164 | switch (event) { 165 | 166 | case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE: 167 | mp4_cache_lookup_complete(mc, txnp); 168 | break; 169 | 170 | case TS_EVENT_HTTP_READ_RESPONSE_HDR: 171 | mp4_read_response(mc, txnp); 172 | break; 173 | 174 | case TS_EVENT_HTTP_TXN_CLOSE: 175 | delete mc; 176 | TSContDestroy(contp); 177 | break; 178 | 179 | default: 180 | break; 181 | } 182 | 183 | TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE); 184 | return 0; 185 | } 186 | 187 | static void 188 | mp4_cache_lookup_complete(Mp4Context *mc, TSHttpTxn txnp) 189 | { 190 | TSMBuffer bufp; 191 | TSMLoc hdrp; 192 | TSMLoc cl_field; 193 | TSHttpStatus code; 194 | int obj_status; 195 | int64_t n; 196 | 197 | if (TSHttpTxnCacheLookupStatusGet(txnp, &obj_status) == TS_ERROR) { 198 | TSError("[%s] Couldn't get cache status of object", __FUNCTION__); 199 | return; 200 | } 201 | 202 | if (obj_status != TS_CACHE_LOOKUP_HIT_STALE && obj_status != TS_CACHE_LOOKUP_HIT_FRESH) 203 | return; 204 | 205 | if (TSHttpTxnCachedRespGet(txnp, &bufp, &hdrp) != TS_SUCCESS) { 206 | TSError("[%s] Couldn't get cache resp", __FUNCTION__); 207 | return; 208 | } 209 | 210 | code = TSHttpHdrStatusGet(bufp, hdrp); 211 | if (code != TS_HTTP_STATUS_OK) { 212 | goto release; 213 | } 214 | 215 | n = 0; 216 | 217 | cl_field = TSMimeHdrFieldFind(bufp, hdrp, TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH); 218 | if (cl_field) { 219 | n = TSMimeHdrFieldValueInt64Get(bufp, hdrp, cl_field, -1); 220 | TSHandleMLocRelease(bufp, hdrp, cl_field); 221 | } 222 | 223 | if (n <= 0) 224 | goto release; 225 | 226 | mc->cl = n; 227 | mp4_add_transform(mc, txnp); 228 | 229 | release: 230 | 231 | TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdrp); 232 | } 233 | 234 | static void 235 | mp4_read_response(Mp4Context *mc, TSHttpTxn txnp) 236 | { 237 | TSMBuffer bufp; 238 | TSMLoc hdrp; 239 | TSMLoc cl_field; 240 | TSHttpStatus status; 241 | int64_t n; 242 | 243 | if (TSHttpTxnServerRespGet(txnp, &bufp, &hdrp) != TS_SUCCESS) { 244 | TSError("[%s] could not get request os data", __FUNCTION__); 245 | return; 246 | } 247 | 248 | status = TSHttpHdrStatusGet(bufp, hdrp); 249 | if (status != TS_HTTP_STATUS_OK) 250 | goto release; 251 | 252 | n = 0; 253 | cl_field = TSMimeHdrFieldFind(bufp, hdrp, TS_MIME_FIELD_CONTENT_LENGTH, TS_MIME_LEN_CONTENT_LENGTH); 254 | if (cl_field) { 255 | n = TSMimeHdrFieldValueInt64Get(bufp, hdrp, cl_field, -1); 256 | TSHandleMLocRelease(bufp, hdrp, cl_field); 257 | } 258 | 259 | if (n <= 0) 260 | goto release; 261 | 262 | mc->cl = n; 263 | mp4_add_transform(mc, txnp); 264 | 265 | release: 266 | 267 | TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdrp); 268 | } 269 | 270 | static void 271 | mp4_add_transform(Mp4Context *mc, TSHttpTxn txnp) 272 | { 273 | TSVConn connp; 274 | 275 | if (mc->transform_added) 276 | return; 277 | 278 | mc->mtc = new Mp4TransformContext(mc->start, mc->cl); 279 | 280 | TSHttpTxnUntransformedRespCache(txnp, 1); 281 | TSHttpTxnTransformedRespCache(txnp, 0); 282 | 283 | connp = TSTransformCreate(mp4_transform_entry, txnp); 284 | TSContDataSet(connp, mc); 285 | TSHttpTxnHookAdd(txnp, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp); 286 | 287 | mc->transform_added = true; 288 | } 289 | 290 | static int 291 | mp4_transform_entry(TSCont contp, TSEvent event, void* /* edata ATS_UNUSED */) 292 | { 293 | TSVIO input_vio; 294 | Mp4Context *mc = (Mp4Context*)TSContDataGet(contp); 295 | 296 | if (TSVConnClosedGet(contp)) { 297 | TSContDestroy(contp); 298 | return 0; 299 | } 300 | 301 | switch (event) { 302 | 303 | case TS_EVENT_ERROR: 304 | input_vio = TSVConnWriteVIOGet(contp); 305 | TSContCall(TSVIOContGet(input_vio), TS_EVENT_ERROR, input_vio); 306 | break; 307 | 308 | case TS_EVENT_VCONN_WRITE_COMPLETE: 309 | TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1); 310 | break; 311 | 312 | case TS_EVENT_VCONN_WRITE_READY: 313 | default: 314 | mp4_transform_handler(contp, mc); 315 | break; 316 | } 317 | 318 | return 0; 319 | } 320 | 321 | static int 322 | mp4_transform_handler(TSCont contp, Mp4Context *mc) 323 | { 324 | TSVConn output_conn; 325 | TSVIO input_vio; 326 | TSIOBufferReader input_reader; 327 | int64_t avail, toread, need, upstream_done; 328 | int ret; 329 | bool write_down; 330 | Mp4TransformContext *mtc; 331 | 332 | mtc = mc->mtc; 333 | 334 | output_conn = TSTransformOutputVConnGet(contp); 335 | input_vio = TSVConnWriteVIOGet(contp); 336 | input_reader = TSVIOReaderGet(input_vio); 337 | 338 | if (!TSVIOBufferGet(input_vio)) { 339 | if (mtc->output.buffer) { 340 | TSVIONBytesSet(mtc->output.vio, mtc->total); 341 | TSVIOReenable(mtc->output.vio); 342 | } 343 | return 1; 344 | } 345 | 346 | avail = TSIOBufferReaderAvail(input_reader); 347 | upstream_done = TSVIONDoneGet(input_vio); 348 | 349 | TSIOBufferCopy(mtc->res_buffer, input_reader, avail, 0); 350 | TSIOBufferReaderConsume(input_reader, avail); 351 | TSVIONDoneSet(input_vio, upstream_done + avail); 352 | 353 | toread = TSVIONTodoGet(input_vio); 354 | write_down = false; 355 | 356 | if (!mtc->parse_over) { 357 | 358 | ret = mp4_parse_meta(mtc, toread <= 0); 359 | if (ret == 0) 360 | goto trans; 361 | 362 | mtc->parse_over = true; 363 | mtc->output.buffer = TSIOBufferCreate(); 364 | mtc->output.reader = TSIOBufferReaderAlloc(mtc->output.buffer); 365 | 366 | if (ret < 0) { 367 | mtc->output.vio = TSVConnWrite(output_conn, contp, mtc->output.reader, mc->cl); 368 | mtc->raw_transform = true; 369 | 370 | } else { 371 | mtc->output.vio = TSVConnWrite(output_conn, contp, mtc->output.reader, mtc->content_length); 372 | } 373 | } 374 | 375 | avail = TSIOBufferReaderAvail(mtc->res_reader); 376 | 377 | if (mtc->raw_transform) { 378 | if (avail > 0) { 379 | TSIOBufferCopy(mtc->output.buffer, mtc->res_reader, avail, 0); 380 | TSIOBufferReaderConsume(mtc->res_reader, avail); 381 | mtc->total += avail; 382 | write_down = true; 383 | } 384 | 385 | } else { 386 | 387 | // copy the new meta data 388 | if (mtc->total < mtc->meta_length) { 389 | TSIOBufferCopy(mtc->output.buffer, mtc->mm.out_handle.reader, mtc->meta_length, 0); 390 | mtc->total += mtc->meta_length; 391 | write_down = true; 392 | } 393 | 394 | // ignore useless part 395 | if (mtc->pos < mtc->tail) { 396 | avail = TSIOBufferReaderAvail(mtc->res_reader); 397 | need = mtc->tail - mtc->pos; 398 | if (need > avail) { 399 | need = avail; 400 | } 401 | 402 | if (need > 0) { 403 | TSIOBufferReaderConsume(mtc->res_reader, need); 404 | mtc->pos += need; 405 | } 406 | } 407 | 408 | // copy the video & audio data 409 | if (mtc->pos >= mtc->tail) { 410 | avail = TSIOBufferReaderAvail(mtc->res_reader); 411 | 412 | if (avail > 0) { 413 | TSIOBufferCopy(mtc->output.buffer, mtc->res_reader, avail, 0); 414 | TSIOBufferReaderConsume(mtc->res_reader, avail); 415 | 416 | mtc->pos += avail; 417 | mtc->total += avail; 418 | write_down = true; 419 | } 420 | } 421 | } 422 | 423 | trans: 424 | 425 | if (write_down) 426 | TSVIOReenable(mtc->output.vio); 427 | 428 | if (toread > 0) { 429 | TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_READY, input_vio); 430 | 431 | } else { 432 | TSVIONBytesSet(mtc->output.vio, mtc->total); 433 | TSContCall(TSVIOContGet(input_vio), TS_EVENT_VCONN_WRITE_COMPLETE, input_vio); 434 | } 435 | 436 | return 1; 437 | } 438 | 439 | static int 440 | mp4_parse_meta(Mp4TransformContext *mtc, bool body_complete) 441 | { 442 | int ret; 443 | int64_t avail, bytes; 444 | TSIOBufferBlock blk; 445 | const char *data; 446 | Mp4Meta *mm; 447 | 448 | mm = &mtc->mm; 449 | 450 | avail = TSIOBufferReaderAvail(mtc->dup_reader); 451 | blk = TSIOBufferReaderStart(mtc->dup_reader); 452 | 453 | while (blk != NULL) { 454 | data = TSIOBufferBlockReadStart(blk, mtc->dup_reader, &bytes); 455 | if (bytes > 0) { 456 | TSIOBufferWrite(mm->meta_buffer, data, bytes); 457 | } 458 | 459 | blk = TSIOBufferBlockNext(blk); 460 | } 461 | 462 | TSIOBufferReaderConsume(mtc->dup_reader, avail); 463 | 464 | ret = mm->parse_meta(body_complete); 465 | 466 | if (ret > 0) { // meta success 467 | mtc->tail = mm->start_pos; 468 | mtc->content_length = mm->content_length; 469 | mtc->meta_length = TSIOBufferReaderAvail(mm->out_handle.reader); 470 | } 471 | 472 | if (ret != 0 && mtc->dup_reader) { 473 | TSIOBufferReaderFree(mtc->dup_reader); 474 | mtc->dup_reader = NULL; 475 | } 476 | 477 | return ret; 478 | } 479 | 480 | char * 481 | ts_arg(const char *param, size_t param_len, const char *key, size_t key_len, size_t *val_len) 482 | { 483 | const char *p, *last; 484 | const char *val; 485 | 486 | *val_len = 0; 487 | 488 | if (!param || !param_len) 489 | return NULL; 490 | 491 | p = param; 492 | last = p + param_len; 493 | 494 | for ( ; p < last; p++) { 495 | 496 | p = (char*)memmem(p, last-p, key, key_len); 497 | 498 | if (p == NULL) 499 | return NULL; 500 | 501 | if ((p == param || *(p - 1) == '&') && *(p + key_len) == '=') { 502 | 503 | val = p + key_len + 1; 504 | 505 | p = (char*)memchr(p, '&', last-p); 506 | 507 | if (p == NULL) 508 | p = param + param_len; 509 | 510 | *val_len = p - val; 511 | 512 | return (char*)val; 513 | } 514 | } 515 | 516 | return NULL; 517 | } 518 | -------------------------------------------------------------------------------- /src/mp4_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | #ifndef _MP4_COMMON_H 20 | #define _MP4_COMMON_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include "mp4_meta.h" 32 | 33 | 34 | class IOHandle 35 | { 36 | public: 37 | IOHandle(): vio(NULL), buffer(NULL), reader(NULL) 38 | { 39 | } 40 | 41 | ~IOHandle() 42 | { 43 | if (reader) { 44 | TSIOBufferReaderFree(reader); 45 | reader = NULL; 46 | } 47 | 48 | if (buffer) { 49 | TSIOBufferDestroy(buffer); 50 | buffer = NULL; 51 | } 52 | } 53 | 54 | public: 55 | TSVIO vio; 56 | TSIOBuffer buffer; 57 | TSIOBufferReader reader; 58 | }; 59 | 60 | class Mp4TransformContext 61 | { 62 | public: 63 | Mp4TransformContext(float offset, int64_t cl): 64 | total(0), tail(0), pos(0), content_length(0), 65 | parse_over(false), raw_transform(false) 66 | { 67 | res_buffer = TSIOBufferCreate(); 68 | res_reader = TSIOBufferReaderAlloc(res_buffer); 69 | dup_reader = TSIOBufferReaderAlloc(res_buffer); 70 | 71 | mm.start = offset * 1000; 72 | mm.cl = cl; 73 | } 74 | 75 | ~Mp4TransformContext() 76 | { 77 | if (res_reader) { 78 | TSIOBufferReaderFree(res_reader); 79 | } 80 | 81 | if (dup_reader) { 82 | TSIOBufferReaderFree(dup_reader); 83 | } 84 | 85 | if (res_buffer) { 86 | TSIOBufferDestroy(res_buffer); 87 | } 88 | } 89 | 90 | public: 91 | IOHandle output; 92 | Mp4Meta mm; 93 | int64_t total; 94 | int64_t tail; 95 | int64_t pos; 96 | int64_t content_length; 97 | int64_t meta_length; 98 | 99 | TSIOBuffer res_buffer; 100 | TSIOBufferReader res_reader; 101 | TSIOBufferReader dup_reader; 102 | 103 | bool parse_over; 104 | bool raw_transform; 105 | }; 106 | 107 | class Mp4Context 108 | { 109 | public: 110 | Mp4Context(float s): start(s), cl(0), mtc(NULL), transform_added(false) 111 | { 112 | } 113 | 114 | ~Mp4Context() 115 | { 116 | if (mtc) { 117 | delete mtc; 118 | mtc = NULL; 119 | } 120 | } 121 | 122 | public: 123 | float start; 124 | int64_t cl; 125 | 126 | Mp4TransformContext *mtc; 127 | 128 | bool transform_added; 129 | }; 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /src/mp4_meta.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | #include "mp4_meta.h" 20 | 21 | 22 | static mp4_atom_handler mp4_atoms[] = { 23 | { "ftyp", &Mp4Meta::mp4_read_ftyp_atom }, 24 | { "moov", &Mp4Meta::mp4_read_moov_atom }, 25 | { "mdat", &Mp4Meta::mp4_read_mdat_atom }, 26 | { NULL, NULL } 27 | }; 28 | 29 | static mp4_atom_handler mp4_moov_atoms[] = { 30 | { "mvhd", &Mp4Meta::mp4_read_mvhd_atom }, 31 | { "trak", &Mp4Meta::mp4_read_trak_atom }, 32 | { "cmov", &Mp4Meta::mp4_read_cmov_atom }, 33 | { NULL, NULL } 34 | }; 35 | 36 | static mp4_atom_handler mp4_trak_atoms[] = { 37 | { "tkhd", &Mp4Meta::mp4_read_tkhd_atom }, 38 | { "mdia", &Mp4Meta::mp4_read_mdia_atom }, 39 | { NULL, NULL } 40 | }; 41 | 42 | static mp4_atom_handler mp4_mdia_atoms[] = { 43 | { "mdhd", &Mp4Meta::mp4_read_mdhd_atom }, 44 | { "hdlr", &Mp4Meta::mp4_read_hdlr_atom }, 45 | { "minf", &Mp4Meta::mp4_read_minf_atom }, 46 | { NULL, NULL } 47 | }; 48 | 49 | static mp4_atom_handler mp4_minf_atoms[] = { 50 | { "vmhd", &Mp4Meta::mp4_read_vmhd_atom }, 51 | { "smhd", &Mp4Meta::mp4_read_smhd_atom }, 52 | { "dinf", &Mp4Meta::mp4_read_dinf_atom }, 53 | { "stbl", &Mp4Meta::mp4_read_stbl_atom }, 54 | { NULL, NULL } 55 | }; 56 | 57 | static mp4_atom_handler mp4_stbl_atoms[] = { 58 | { "stsd", &Mp4Meta::mp4_read_stsd_atom }, 59 | { "stts", &Mp4Meta::mp4_read_stts_atom }, 60 | { "stss", &Mp4Meta::mp4_read_stss_atom }, 61 | { "ctts", &Mp4Meta::mp4_read_ctts_atom }, 62 | { "stsc", &Mp4Meta::mp4_read_stsc_atom }, 63 | { "stsz", &Mp4Meta::mp4_read_stsz_atom }, 64 | { "stco", &Mp4Meta::mp4_read_stco_atom }, 65 | { "co64", &Mp4Meta::mp4_read_co64_atom }, 66 | { NULL, NULL } 67 | }; 68 | 69 | 70 | static void mp4_reader_set_32value(TSIOBufferReader readerp, int64_t offset, uint32_t n); 71 | static void mp4_reader_set_64value(TSIOBufferReader readerp, int64_t offset, uint64_t n); 72 | static uint32_t mp4_reader_get_32value(TSIOBufferReader readerp, int64_t offset); 73 | static uint64_t mp4_reader_get_64value(TSIOBufferReader readerp, int64_t offset); 74 | static int64_t IOBufferReaderCopy(TSIOBufferReader readerp, void *buf, int64_t length); 75 | 76 | 77 | int 78 | Mp4Meta::parse_meta(bool body_complete) 79 | { 80 | int ret, rc; 81 | 82 | meta_avail = TSIOBufferReaderAvail(meta_reader); 83 | 84 | if (wait_next && wait_next <= meta_avail) { 85 | mp4_meta_consume(wait_next); 86 | wait_next = 0; 87 | } 88 | 89 | if (meta_avail < MP4_MIN_BUFFER_SIZE && !body_complete) 90 | return 0; 91 | 92 | ret = this->parse_root_atoms(); 93 | 94 | if (ret < 0) { 95 | return -1; 96 | 97 | } else if (ret == 0) { 98 | 99 | if (body_complete) { 100 | return -1; 101 | 102 | } else { 103 | return 0; 104 | } 105 | } 106 | 107 | // generate new meta data 108 | rc = this->post_process_meta(); 109 | if (rc != 0) { 110 | return -1; 111 | } 112 | 113 | return 1; 114 | } 115 | 116 | void 117 | Mp4Meta::mp4_meta_consume(int64_t size) 118 | { 119 | TSIOBufferReaderConsume(meta_reader, size); 120 | meta_avail -= size; 121 | passed += size; 122 | } 123 | 124 | 125 | int 126 | Mp4Meta::post_process_meta() 127 | { 128 | off_t start_offset, adjustment; 129 | uint32_t i, j; 130 | int64_t avail; 131 | BufferHandle *bh; 132 | Mp4Trak *trak; 133 | 134 | if (this->trak_num == 0) { 135 | return -1; 136 | } 137 | 138 | if (mdat_atom.buffer == NULL) { 139 | return -1; 140 | } 141 | 142 | out_handle.buffer = TSIOBufferCreate(); 143 | out_handle.reader = TSIOBufferReaderAlloc(out_handle.buffer); 144 | 145 | if (ftyp_atom.buffer) { 146 | TSIOBufferCopy(out_handle.buffer, ftyp_atom.reader, 147 | TSIOBufferReaderAvail(ftyp_atom.reader), 0); 148 | } 149 | 150 | if (moov_atom.buffer) { 151 | TSIOBufferCopy(out_handle.buffer, moov_atom.reader, 152 | TSIOBufferReaderAvail(moov_atom.reader), 0); 153 | } 154 | 155 | if (mvhd_atom.buffer) { 156 | avail = TSIOBufferReaderAvail(mvhd_atom.reader); 157 | TSIOBufferCopy(out_handle.buffer, mvhd_atom.reader, avail, 0); 158 | this->moov_size += avail; 159 | } 160 | 161 | start_offset = cl; 162 | 163 | for (i = 0; i < trak_num; i++) { 164 | 165 | trak = trak_vec[i]; 166 | 167 | if (mp4_update_stts_atom(trak) != 0) { 168 | return -1; 169 | } 170 | 171 | if (mp4_update_stss_atom(trak) != 0) { 172 | return -1; 173 | } 174 | 175 | mp4_update_ctts_atom(trak); 176 | 177 | if (mp4_update_stsc_atom(trak) != 0) { 178 | return -1; 179 | } 180 | 181 | if (mp4_update_stsz_atom(trak) != 0) { 182 | return -1; 183 | } 184 | 185 | if (trak->co64_data.buffer) { 186 | 187 | if (mp4_update_co64_atom(trak) != 0) 188 | return -1; 189 | 190 | } else if (mp4_update_stco_atom(trak) != 0) { 191 | return -1; 192 | } 193 | 194 | mp4_update_stbl_atom(trak); 195 | mp4_update_minf_atom(trak); 196 | trak->size += trak->mdhd_size; 197 | trak->size += trak->hdlr_size; 198 | mp4_update_mdia_atom(trak); 199 | trak->size += trak->tkhd_size; 200 | mp4_update_trak_atom(trak); 201 | 202 | this->moov_size += trak->size; 203 | 204 | if (start_offset > trak->start_offset) 205 | start_offset = trak->start_offset; 206 | 207 | bh = &(trak->trak_atom); 208 | for (j = 0; j <= MP4_LAST_ATOM; j++) { 209 | if (bh[j].buffer) { 210 | TSIOBufferCopy(out_handle.buffer, bh[j].reader, 211 | TSIOBufferReaderAvail(bh[j].reader), 0); 212 | } 213 | } 214 | 215 | mp4_update_tkhd_duration(trak); 216 | mp4_update_mdhd_duration(trak); 217 | } 218 | 219 | this->moov_size += 8; 220 | 221 | mp4_reader_set_32value(moov_atom.reader, 0, this->moov_size); 222 | this->content_length += this->moov_size; 223 | 224 | adjustment = this->ftyp_size + this->moov_size + 225 | mp4_update_mdat_atom(start_offset) - start_offset; 226 | 227 | 228 | TSIOBufferCopy(out_handle.buffer, mdat_atom.reader, 229 | TSIOBufferReaderAvail(mdat_atom.reader), 0); 230 | 231 | for (i = 0; i < trak_num; i++) { 232 | trak = trak_vec[i]; 233 | 234 | if (trak->co64_data.buffer) { 235 | mp4_adjust_co64_atom(trak, adjustment); 236 | 237 | } else { 238 | mp4_adjust_stco_atom(trak, adjustment); 239 | } 240 | } 241 | 242 | mp4_update_mvhd_duration(); 243 | 244 | return 0; 245 | } 246 | 247 | /* 248 | * -1: error 249 | * 0: unfinished 250 | * 1: success. 251 | */ 252 | int 253 | Mp4Meta::parse_root_atoms() 254 | { 255 | int i, ret, rc; 256 | int64_t atom_size, atom_header_size; 257 | char buf[64]; 258 | char *atom_header, *atom_name; 259 | 260 | memset(buf, 0, sizeof(buf)); 261 | 262 | for (;;) { 263 | 264 | if (meta_avail < (int64_t)sizeof(uint32_t)) 265 | return 0; 266 | 267 | IOBufferReaderCopy(meta_reader, buf, sizeof(mp4_atom_header64)); 268 | atom_size = mp4_get_32value(buf); 269 | 270 | if (atom_size == 0) { 271 | return 1; 272 | } 273 | 274 | atom_header = buf; 275 | 276 | if (atom_size < (int64_t)sizeof(mp4_atom_header)) { 277 | 278 | if (atom_size == 1) { 279 | if (meta_avail < (int64_t)sizeof(mp4_atom_header64)) { 280 | return 0; 281 | } 282 | 283 | } else { 284 | return -1; 285 | } 286 | 287 | atom_size = mp4_get_64value(atom_header + 8); 288 | atom_header_size = sizeof(mp4_atom_header64); 289 | 290 | } else { // regular atom 291 | 292 | if (meta_avail < (int64_t)sizeof(mp4_atom_header)) // not enough for atom header 293 | return 0; 294 | 295 | atom_header_size = sizeof(mp4_atom_header); 296 | } 297 | 298 | atom_name = atom_header + 4; 299 | 300 | if (atom_size + this->passed > this->cl) { 301 | return -1; 302 | } 303 | 304 | for (i = 0; mp4_atoms[i].name; i++) { 305 | if (memcmp(atom_name, mp4_atoms[i].name, 4) == 0) { 306 | 307 | ret = (this->*mp4_atoms[i].handler)(atom_header_size, atom_size - atom_header_size); // -1: error, 0: unfinished, 1: success 308 | 309 | if (ret <= 0) { 310 | return ret; 311 | 312 | } else if (meta_complete) { // success 313 | return 1; 314 | } 315 | 316 | goto next; 317 | } 318 | } 319 | 320 | // nonsignificant atom box 321 | rc = mp4_atom_next(atom_size, true); // 0: unfinished, 1: success 322 | if (rc == 0) { 323 | return rc; 324 | } 325 | 326 | next: 327 | continue; 328 | } 329 | 330 | return 1; 331 | } 332 | 333 | int 334 | Mp4Meta::mp4_atom_next(int64_t atom_size, bool wait) 335 | { 336 | if (meta_avail >= atom_size) { 337 | mp4_meta_consume(atom_size); 338 | return 1; 339 | } 340 | 341 | if (wait) { 342 | wait_next = atom_size; 343 | return 0; 344 | } 345 | 346 | return -1; 347 | } 348 | 349 | 350 | /* 351 | * -1: error 352 | * 1: success 353 | */ 354 | int 355 | Mp4Meta::mp4_read_atom(mp4_atom_handler *atom, int64_t size) 356 | { 357 | int i, ret, rc; 358 | int64_t atom_size, atom_header_size; 359 | char buf[32]; 360 | char *atom_header, *atom_name; 361 | 362 | if (meta_avail < size) // data insufficient, not reasonable for internal atom box. 363 | return -1; 364 | 365 | while (size > 0) { 366 | 367 | if (meta_avail < (int64_t)sizeof(uint32_t)) // data insufficient, not reasonable for internal atom box. 368 | return -1; 369 | 370 | IOBufferReaderCopy(meta_reader, buf, sizeof(mp4_atom_header64)); 371 | atom_size = mp4_get_32value(buf); 372 | 373 | if (atom_size == 0) { 374 | return 1; 375 | } 376 | 377 | atom_header = buf; 378 | 379 | if (atom_size < (int64_t)sizeof(mp4_atom_header)) { 380 | 381 | if (atom_size == 1) { 382 | if (meta_avail < (int64_t)sizeof(mp4_atom_header64)) { 383 | return -1; 384 | } 385 | 386 | } else { 387 | return -1; 388 | } 389 | 390 | atom_size = mp4_get_64value(atom_header + 8); 391 | atom_header_size = sizeof(mp4_atom_header64); 392 | 393 | } else { // regular atom 394 | 395 | if (meta_avail < (int64_t)sizeof(mp4_atom_header)) 396 | return -1; 397 | 398 | atom_header_size = sizeof(mp4_atom_header); 399 | } 400 | 401 | atom_name = atom_header + 4; 402 | 403 | if (atom_size + this->passed > this->cl) { 404 | return -1; 405 | } 406 | 407 | for (i = 0; atom[i].name; i++) { 408 | if (memcmp(atom_name, atom[i].name, 4) == 0) { 409 | 410 | if (meta_avail < atom_size) 411 | return -1; 412 | 413 | ret = (this->*atom[i].handler)(atom_header_size, atom_size - atom_header_size); // -1: error, 0: success. 414 | 415 | if (ret < 0) { 416 | return ret; 417 | } 418 | 419 | goto next; 420 | } 421 | } 422 | 423 | // insignificant atom box 424 | rc = mp4_atom_next(atom_size, false); 425 | if (rc < 0) { 426 | return rc; 427 | } 428 | 429 | next: 430 | size -= atom_size; 431 | continue; 432 | } 433 | 434 | return 1; 435 | } 436 | 437 | int 438 | Mp4Meta::mp4_read_ftyp_atom(int64_t atom_header_size, int64_t atom_data_size) 439 | { 440 | int64_t atom_size; 441 | 442 | if (atom_data_size > MP4_MIN_BUFFER_SIZE) 443 | return -1; 444 | 445 | atom_size = atom_header_size + atom_data_size; 446 | 447 | if (meta_avail < atom_size) { // data unsufficient, reasonable from the first level 448 | return 0; 449 | } 450 | 451 | ftyp_atom.buffer = TSIOBufferCreate(); 452 | ftyp_atom.reader = TSIOBufferReaderAlloc(ftyp_atom.buffer); 453 | 454 | TSIOBufferCopy(ftyp_atom.buffer, meta_reader, atom_size, 0); 455 | mp4_meta_consume(atom_size); 456 | 457 | content_length = atom_size; 458 | ftyp_size = atom_size; 459 | 460 | return 1; 461 | } 462 | 463 | int 464 | Mp4Meta::mp4_read_moov_atom(int64_t atom_header_size, int64_t atom_data_size) 465 | { 466 | int64_t atom_size; 467 | int ret; 468 | 469 | if (mdat_atom.buffer != NULL) // not reasonable for streaming media 470 | return -1; 471 | 472 | atom_size = atom_header_size + atom_data_size; 473 | 474 | if (atom_data_size >= MP4_MAX_BUFFER_SIZE) 475 | return -1; 476 | 477 | if (meta_avail < atom_size) { // data unsufficient, wait 478 | return 0; 479 | } 480 | 481 | moov_atom.buffer = TSIOBufferCreate(); 482 | moov_atom.reader = TSIOBufferReaderAlloc(moov_atom.buffer); 483 | 484 | TSIOBufferCopy(moov_atom.buffer, meta_reader, atom_header_size, 0); 485 | mp4_meta_consume(atom_header_size); 486 | 487 | ret = mp4_read_atom(mp4_moov_atoms, atom_data_size); 488 | 489 | return ret; 490 | } 491 | 492 | int 493 | Mp4Meta::mp4_read_mvhd_atom(int64_t atom_header_size, int64_t atom_data_size) 494 | { 495 | int64_t atom_size; 496 | uint32_t timescale; 497 | mp4_mvhd_atom *mvhd; 498 | mp4_mvhd64_atom mvhd64; 499 | 500 | if (sizeof(mp4_mvhd_atom) - 8 > (size_t)atom_data_size) 501 | return -1; 502 | 503 | IOBufferReaderCopy(meta_reader, &mvhd64, sizeof(mp4_mvhd64_atom)); 504 | mvhd = (mp4_mvhd_atom*)&mvhd64; 505 | 506 | if (mvhd->version[0] == 0) { 507 | timescale = mp4_get_32value(mvhd->timescale); 508 | 509 | } else { // 64-bit duration 510 | timescale = mp4_get_32value(mvhd64.timescale); 511 | } 512 | 513 | this->timescale = timescale; 514 | 515 | atom_size = atom_header_size + atom_data_size; 516 | 517 | mvhd_atom.buffer = TSIOBufferCreate(); 518 | mvhd_atom.reader = TSIOBufferReaderAlloc(mvhd_atom.buffer); 519 | 520 | TSIOBufferCopy(mvhd_atom.buffer, meta_reader, atom_size, 0); 521 | mp4_meta_consume(atom_size); 522 | 523 | return 1; 524 | } 525 | 526 | int 527 | Mp4Meta::mp4_read_trak_atom(int64_t atom_header_size, int64_t atom_data_size) 528 | { 529 | int rc; 530 | Mp4Trak *trak; 531 | 532 | if (trak_num >= MP4_MAX_TRAK_NUM - 1) 533 | return -1; 534 | 535 | trak = new Mp4Trak(); 536 | trak_vec[trak_num++] = trak; 537 | 538 | trak->trak_atom.buffer = TSIOBufferCreate(); 539 | trak->trak_atom.reader = TSIOBufferReaderAlloc(trak->trak_atom.buffer); 540 | 541 | TSIOBufferCopy(trak->trak_atom.buffer, meta_reader, atom_header_size, 0); 542 | mp4_meta_consume(atom_header_size); 543 | 544 | rc = mp4_read_atom(mp4_trak_atoms, atom_data_size); 545 | 546 | return rc; 547 | } 548 | 549 | int 550 | Mp4Meta::mp4_read_cmov_atom(int64_t /*atom_header_size ATS_UNUSED */, int64_t /* atom_data_size ATS_UNUSED */) 551 | { 552 | return -1; 553 | } 554 | 555 | int 556 | Mp4Meta::mp4_read_tkhd_atom(int64_t atom_header_size, int64_t atom_data_size) 557 | { 558 | int64_t atom_size; 559 | Mp4Trak *trak; 560 | 561 | atom_size = atom_header_size + atom_data_size; 562 | 563 | trak = trak_vec[trak_num-1]; 564 | trak->tkhd_size = atom_size; 565 | 566 | trak->tkhd_atom.buffer = TSIOBufferCreate(); 567 | trak->tkhd_atom.reader = TSIOBufferReaderAlloc(trak->tkhd_atom.buffer); 568 | 569 | TSIOBufferCopy(trak->tkhd_atom.buffer, meta_reader, atom_size, 0); 570 | mp4_meta_consume(atom_size); 571 | 572 | mp4_reader_set_32value(trak->tkhd_atom.reader, 573 | offsetof(mp4_tkhd_atom, size), atom_size); 574 | 575 | return 1; 576 | } 577 | 578 | int 579 | Mp4Meta::mp4_read_mdia_atom(int64_t atom_header_size, int64_t atom_data_size) 580 | { 581 | Mp4Trak *trak; 582 | 583 | trak = trak_vec[trak_num-1]; 584 | 585 | trak->mdia_atom.buffer = TSIOBufferCreate(); 586 | trak->mdia_atom.reader = TSIOBufferReaderAlloc(trak->mdia_atom.buffer); 587 | 588 | TSIOBufferCopy(trak->mdia_atom.buffer, meta_reader, atom_header_size, 0); 589 | mp4_meta_consume(atom_header_size); 590 | 591 | return mp4_read_atom(mp4_mdia_atoms, atom_data_size); 592 | } 593 | 594 | int 595 | Mp4Meta::mp4_read_mdhd_atom(int64_t atom_header_size, int64_t atom_data_size) 596 | { 597 | int64_t atom_size, duration; 598 | uint32_t ts; 599 | Mp4Trak *trak; 600 | mp4_mdhd_atom *mdhd; 601 | mp4_mdhd64_atom mdhd64; 602 | 603 | IOBufferReaderCopy(meta_reader, &mdhd64, sizeof(mp4_mdhd64_atom)); 604 | mdhd = (mp4_mdhd_atom*)&mdhd64; 605 | 606 | if (mdhd->version[0] == 0) { 607 | ts = mp4_get_32value(mdhd->timescale); 608 | duration = mp4_get_32value(mdhd->duration); 609 | 610 | } else { 611 | ts = mp4_get_32value(mdhd64.timescale); 612 | duration = mp4_get_64value(mdhd64.duration); 613 | } 614 | 615 | atom_size = atom_header_size + atom_data_size; 616 | 617 | trak = trak_vec[trak_num-1]; 618 | trak->mdhd_size = atom_size; 619 | trak->timescale = ts; 620 | trak->duration = duration; 621 | 622 | trak->mdhd_atom.buffer = TSIOBufferCreate(); 623 | trak->mdhd_atom.reader = TSIOBufferReaderAlloc(trak->mdhd_atom.buffer); 624 | 625 | TSIOBufferCopy(trak->mdhd_atom.buffer, meta_reader, atom_size, 0); 626 | mp4_meta_consume(atom_size); 627 | 628 | mp4_reader_set_32value(trak->mdhd_atom.reader, 629 | offsetof(mp4_mdhd_atom, size), atom_size); 630 | 631 | return 1; 632 | } 633 | 634 | int 635 | Mp4Meta::mp4_read_hdlr_atom(int64_t atom_header_size, int64_t atom_data_size) 636 | { 637 | int64_t atom_size; 638 | Mp4Trak *trak; 639 | 640 | atom_size = atom_header_size + atom_data_size; 641 | 642 | trak = trak_vec[trak_num-1]; 643 | trak->hdlr_size = atom_size; 644 | 645 | trak->hdlr_atom.buffer = TSIOBufferCreate(); 646 | trak->hdlr_atom.reader = TSIOBufferReaderAlloc(trak->hdlr_atom.buffer); 647 | 648 | TSIOBufferCopy(trak->hdlr_atom.buffer, meta_reader, atom_size, 0); 649 | mp4_meta_consume(atom_size); 650 | 651 | return 1; 652 | } 653 | 654 | int 655 | Mp4Meta::mp4_read_minf_atom(int64_t atom_header_size, int64_t atom_data_size) 656 | { 657 | Mp4Trak *trak; 658 | 659 | trak = trak_vec[trak_num-1]; 660 | 661 | trak->minf_atom.buffer = TSIOBufferCreate(); 662 | trak->minf_atom.reader = TSIOBufferReaderAlloc(trak->minf_atom.buffer); 663 | 664 | TSIOBufferCopy(trak->minf_atom.buffer, meta_reader, atom_header_size, 0); 665 | mp4_meta_consume(atom_header_size); 666 | 667 | return mp4_read_atom(mp4_minf_atoms, atom_data_size); 668 | } 669 | 670 | int 671 | Mp4Meta::mp4_read_vmhd_atom(int64_t atom_header_size, int64_t atom_data_size) 672 | { 673 | int64_t atom_size; 674 | Mp4Trak *trak; 675 | 676 | atom_size = atom_data_size + atom_header_size; 677 | 678 | trak = trak_vec[trak_num-1]; 679 | trak->vmhd_size += atom_size; 680 | 681 | trak->vmhd_atom.buffer = TSIOBufferCreate(); 682 | trak->vmhd_atom.reader = TSIOBufferReaderAlloc(trak->vmhd_atom.buffer); 683 | 684 | TSIOBufferCopy(trak->vmhd_atom.buffer, meta_reader, atom_size, 0); 685 | mp4_meta_consume(atom_size); 686 | 687 | return 1; 688 | } 689 | 690 | int 691 | Mp4Meta::mp4_read_smhd_atom(int64_t atom_header_size, int64_t atom_data_size) 692 | { 693 | int64_t atom_size; 694 | Mp4Trak *trak; 695 | 696 | atom_size = atom_data_size + atom_header_size; 697 | 698 | trak = trak_vec[trak_num-1]; 699 | trak->smhd_size += atom_size; 700 | 701 | trak->smhd_atom.buffer = TSIOBufferCreate(); 702 | trak->smhd_atom.reader = TSIOBufferReaderAlloc(trak->smhd_atom.buffer); 703 | 704 | TSIOBufferCopy(trak->smhd_atom.buffer, meta_reader, atom_size, 0); 705 | mp4_meta_consume(atom_size); 706 | 707 | return 1; 708 | } 709 | 710 | int 711 | Mp4Meta::mp4_read_dinf_atom(int64_t atom_header_size, int64_t atom_data_size) 712 | { 713 | int64_t atom_size; 714 | Mp4Trak *trak; 715 | 716 | atom_size = atom_data_size + atom_header_size; 717 | 718 | trak = trak_vec[trak_num-1]; 719 | trak->dinf_size += atom_size; 720 | 721 | trak->dinf_atom.buffer = TSIOBufferCreate(); 722 | trak->dinf_atom.reader = TSIOBufferReaderAlloc(trak->dinf_atom.buffer); 723 | 724 | TSIOBufferCopy(trak->dinf_atom.buffer, meta_reader, atom_size, 0); 725 | mp4_meta_consume(atom_size); 726 | 727 | return 1; 728 | } 729 | 730 | int 731 | Mp4Meta::mp4_read_stbl_atom(int64_t atom_header_size, int64_t atom_data_size) 732 | { 733 | Mp4Trak *trak; 734 | 735 | trak = trak_vec[trak_num-1]; 736 | 737 | trak->stbl_atom.buffer = TSIOBufferCreate(); 738 | trak->stbl_atom.reader = TSIOBufferReaderAlloc(trak->stbl_atom.buffer); 739 | 740 | TSIOBufferCopy(trak->stbl_atom.buffer, meta_reader, atom_header_size, 0); 741 | mp4_meta_consume(atom_header_size); 742 | 743 | return mp4_read_atom(mp4_stbl_atoms, atom_data_size); 744 | } 745 | 746 | int 747 | Mp4Meta::mp4_read_stsd_atom(int64_t atom_header_size, int64_t atom_data_size) 748 | { 749 | int64_t atom_size; 750 | Mp4Trak *trak; 751 | 752 | atom_size = atom_data_size + atom_header_size; 753 | 754 | trak = trak_vec[trak_num-1]; 755 | trak->size += atom_size; 756 | 757 | trak->stsd_atom.buffer = TSIOBufferCreate(); 758 | trak->stsd_atom.reader = TSIOBufferReaderAlloc(trak->stsd_atom.buffer); 759 | 760 | TSIOBufferCopy(trak->stsd_atom.buffer, meta_reader, atom_size, 0); 761 | 762 | mp4_meta_consume(atom_size); 763 | 764 | return 1; 765 | } 766 | 767 | int 768 | Mp4Meta::mp4_read_stts_atom(int64_t atom_header_size, int64_t atom_data_size) 769 | { 770 | int32_t entries; 771 | int64_t esize; 772 | mp4_stts_atom stts; 773 | Mp4Trak *trak; 774 | 775 | if (sizeof(mp4_stts_atom) - 8 > (size_t)atom_data_size) 776 | return -1; 777 | 778 | IOBufferReaderCopy(meta_reader, &stts, sizeof(mp4_stts_atom)); 779 | 780 | entries = mp4_get_32value(stts.entries); 781 | esize = entries * sizeof(mp4_stts_entry); 782 | 783 | if (sizeof(mp4_stts_atom) - 8 + esize > (size_t)atom_data_size) 784 | return -1; 785 | 786 | trak = trak_vec[trak_num - 1]; 787 | trak->time_to_sample_entries = entries; 788 | 789 | trak->stts_atom.buffer = TSIOBufferCreate(); 790 | trak->stts_atom.reader = TSIOBufferReaderAlloc(trak->stts_atom.buffer); 791 | TSIOBufferCopy(trak->stts_atom.buffer, meta_reader, sizeof(mp4_stts_atom), 0); 792 | 793 | trak->stts_data.buffer = TSIOBufferCreate(); 794 | trak->stts_data.reader = TSIOBufferReaderAlloc(trak->stts_data.buffer); 795 | TSIOBufferCopy(trak->stts_data.buffer, meta_reader, esize, sizeof(mp4_stts_atom)); 796 | 797 | mp4_meta_consume(atom_data_size + atom_header_size); 798 | 799 | return 1; 800 | } 801 | 802 | int 803 | Mp4Meta::mp4_read_stss_atom(int64_t atom_header_size, int64_t atom_data_size) 804 | { 805 | int32_t entries; 806 | int64_t esize; 807 | mp4_stss_atom stss; 808 | Mp4Trak *trak; 809 | 810 | if (sizeof(mp4_stss_atom) - 8 > (size_t)atom_data_size) 811 | return -1; 812 | 813 | IOBufferReaderCopy(meta_reader, &stss, sizeof(mp4_stss_atom)); 814 | entries = mp4_get_32value(stss.entries); 815 | esize = entries * sizeof(int32_t); 816 | 817 | if (sizeof(mp4_stss_atom) - 8 + esize > (size_t)atom_data_size) 818 | return -1; 819 | 820 | trak = trak_vec[trak_num-1]; 821 | trak->sync_samples_entries = entries; 822 | 823 | trak->stss_atom.buffer = TSIOBufferCreate(); 824 | trak->stss_atom.reader = TSIOBufferReaderAlloc(trak->stss_atom.buffer); 825 | TSIOBufferCopy(trak->stss_atom.buffer, meta_reader, sizeof(mp4_stss_atom), 0); 826 | 827 | trak->stss_data.buffer = TSIOBufferCreate(); 828 | trak->stss_data.reader = TSIOBufferReaderAlloc(trak->stss_data.buffer); 829 | TSIOBufferCopy(trak->stss_data.buffer, meta_reader, esize, sizeof(mp4_stss_atom)); 830 | 831 | mp4_meta_consume(atom_data_size + atom_header_size); 832 | 833 | return 1; 834 | } 835 | 836 | int 837 | Mp4Meta::mp4_read_ctts_atom(int64_t atom_header_size, int64_t atom_data_size) 838 | { 839 | int32_t entries; 840 | int64_t esize; 841 | mp4_ctts_atom ctts; 842 | Mp4Trak *trak; 843 | 844 | if (sizeof(mp4_ctts_atom) - 8 > (size_t)atom_data_size) 845 | return -1; 846 | 847 | IOBufferReaderCopy(meta_reader, &ctts, sizeof(mp4_ctts_atom)); 848 | entries = mp4_get_32value(ctts.entries); 849 | esize = entries * sizeof(mp4_ctts_entry); 850 | 851 | if (sizeof(mp4_ctts_atom) - 8 + esize > (size_t)atom_data_size) 852 | return -1; 853 | 854 | trak = trak_vec[trak_num - 1]; 855 | trak->composition_offset_entries = entries; 856 | 857 | trak->ctts_atom.buffer = TSIOBufferCreate(); 858 | trak->ctts_atom.reader = TSIOBufferReaderAlloc(trak->ctts_atom.buffer); 859 | TSIOBufferCopy(trak->ctts_atom.buffer, meta_reader, sizeof(mp4_ctts_atom), 0); 860 | 861 | trak->ctts_data.buffer = TSIOBufferCreate(); 862 | trak->ctts_data.reader = TSIOBufferReaderAlloc(trak->ctts_data.buffer); 863 | TSIOBufferCopy(trak->ctts_data.buffer, meta_reader, esize, sizeof(mp4_ctts_atom)); 864 | 865 | mp4_meta_consume(atom_data_size + atom_header_size); 866 | 867 | return 1; 868 | } 869 | 870 | int 871 | Mp4Meta::mp4_read_stsc_atom(int64_t atom_header_size, int64_t atom_data_size) 872 | { 873 | int32_t entries; 874 | int64_t esize; 875 | mp4_stsc_atom stsc; 876 | Mp4Trak *trak; 877 | 878 | if (sizeof(mp4_stsc_atom) - 8 > (size_t)atom_data_size) 879 | return -1; 880 | 881 | IOBufferReaderCopy(meta_reader, &stsc, sizeof(mp4_stsc_atom)); 882 | entries = mp4_get_32value(stsc.entries); 883 | esize = entries * sizeof(mp4_stsc_entry); 884 | 885 | if (sizeof(mp4_stsc_atom) - 8 + esize > (size_t)atom_data_size) 886 | return -1; 887 | 888 | trak = trak_vec[trak_num - 1]; 889 | trak->sample_to_chunk_entries = entries; 890 | 891 | trak->stsc_atom.buffer = TSIOBufferCreate(); 892 | trak->stsc_atom.reader = TSIOBufferReaderAlloc(trak->stsc_atom.buffer); 893 | TSIOBufferCopy(trak->stsc_atom.buffer, meta_reader, sizeof(mp4_stsc_atom), 0); 894 | 895 | trak->stsc_data.buffer = TSIOBufferCreate(); 896 | trak->stsc_data.reader = TSIOBufferReaderAlloc(trak->stsc_data.buffer); 897 | TSIOBufferCopy(trak->stsc_data.buffer, meta_reader, esize, sizeof(mp4_stsc_atom)); 898 | 899 | mp4_meta_consume(atom_data_size + atom_header_size); 900 | 901 | return 1; 902 | } 903 | 904 | int 905 | Mp4Meta::mp4_read_stsz_atom(int64_t atom_header_size, int64_t atom_data_size) 906 | { 907 | int32_t entries, size; 908 | int64_t esize, atom_size; 909 | mp4_stsz_atom stsz; 910 | Mp4Trak *trak; 911 | 912 | if (sizeof(mp4_stsz_atom) - 8 > (size_t)atom_data_size) 913 | return -1; 914 | 915 | IOBufferReaderCopy(meta_reader, &stsz, sizeof(mp4_stsz_atom)); 916 | entries = mp4_get_32value(stsz.entries); 917 | esize = entries * sizeof(int32_t); 918 | 919 | trak = trak_vec[trak_num - 1]; 920 | size = mp4_get_32value(stsz.uniform_size); 921 | 922 | trak->sample_sizes_entries = entries; 923 | 924 | trak->stsz_atom.buffer = TSIOBufferCreate(); 925 | trak->stsz_atom.reader = TSIOBufferReaderAlloc(trak->stsz_atom.buffer); 926 | TSIOBufferCopy(trak->stsz_atom.buffer, meta_reader, sizeof(mp4_stsz_atom), 0); 927 | 928 | if (size == 0) { 929 | 930 | if (sizeof(mp4_stsz_atom) - 8 + esize > (size_t)atom_data_size) 931 | return -1; 932 | 933 | trak->stsz_data.buffer = TSIOBufferCreate(); 934 | trak->stsz_data.reader = TSIOBufferReaderAlloc(trak->stsz_data.buffer); 935 | TSIOBufferCopy(trak->stsz_data.buffer, meta_reader, esize, sizeof(mp4_stsz_atom)); 936 | 937 | } else { 938 | atom_size = atom_header_size + atom_data_size; 939 | trak->size += atom_size; 940 | mp4_reader_set_32value(trak->stsz_atom.reader, 0, atom_size); 941 | } 942 | 943 | mp4_meta_consume(atom_data_size + atom_header_size); 944 | 945 | return 1; 946 | } 947 | 948 | int 949 | Mp4Meta::mp4_read_stco_atom(int64_t atom_header_size, int64_t atom_data_size) 950 | { 951 | int32_t entries; 952 | int64_t esize; 953 | mp4_stco_atom stco; 954 | Mp4Trak *trak; 955 | 956 | if (sizeof(mp4_stco_atom) - 8 > (size_t)atom_data_size) 957 | return -1; 958 | 959 | IOBufferReaderCopy(meta_reader, &stco, sizeof(mp4_stco_atom)); 960 | entries = mp4_get_32value(stco.entries); 961 | esize = entries * sizeof(int32_t); 962 | 963 | if (sizeof(mp4_stco_atom) - 8 + esize > (size_t)atom_data_size) 964 | return -1; 965 | 966 | trak = trak_vec[trak_num - 1]; 967 | trak->chunks = entries; 968 | 969 | trak->stco_atom.buffer = TSIOBufferCreate(); 970 | trak->stco_atom.reader = TSIOBufferReaderAlloc(trak->stco_atom.buffer); 971 | TSIOBufferCopy(trak->stco_atom.buffer, meta_reader, sizeof(mp4_stco_atom), 0); 972 | 973 | trak->stco_data.buffer = TSIOBufferCreate(); 974 | trak->stco_data.reader = TSIOBufferReaderAlloc(trak->stco_data.buffer); 975 | TSIOBufferCopy(trak->stco_data.buffer, meta_reader, esize, sizeof(mp4_stco_atom)); 976 | 977 | mp4_meta_consume(atom_data_size + atom_header_size); 978 | 979 | return 1; 980 | } 981 | 982 | int 983 | Mp4Meta::mp4_read_co64_atom(int64_t atom_header_size, int64_t atom_data_size) 984 | { 985 | int32_t entries; 986 | int64_t esize; 987 | mp4_co64_atom co64; 988 | Mp4Trak *trak; 989 | 990 | if (sizeof(mp4_co64_atom) - 8 > (size_t)atom_data_size) 991 | return -1; 992 | 993 | IOBufferReaderCopy(meta_reader, &co64, sizeof(mp4_co64_atom)); 994 | entries = mp4_get_32value(co64.entries); 995 | esize = entries * sizeof(int64_t); 996 | 997 | if (sizeof(mp4_co64_atom) - 8 + esize > (size_t)atom_data_size) 998 | return -1; 999 | 1000 | trak = trak_vec[trak_num - 1]; 1001 | trak->chunks = entries; 1002 | 1003 | trak->co64_atom.buffer = TSIOBufferCreate(); 1004 | trak->co64_atom.reader = TSIOBufferReaderAlloc(trak->co64_atom.buffer); 1005 | TSIOBufferCopy(trak->co64_atom.buffer, meta_reader, sizeof(mp4_co64_atom), 0); 1006 | 1007 | trak->co64_data.buffer = TSIOBufferCreate(); 1008 | trak->co64_data.reader = TSIOBufferReaderAlloc(trak->co64_data.buffer); 1009 | TSIOBufferCopy(trak->co64_data.buffer, meta_reader, esize, sizeof(mp4_co64_atom)); 1010 | 1011 | mp4_meta_consume(atom_data_size + atom_header_size); 1012 | 1013 | return 1; 1014 | } 1015 | 1016 | int 1017 | Mp4Meta::mp4_read_mdat_atom(int64_t /* atom_header_size ATS_UNUSED */, int64_t /* atom_data_size ATS_UNUSED */) 1018 | { 1019 | mdat_atom.buffer = TSIOBufferCreate(); 1020 | mdat_atom.reader = TSIOBufferReaderAlloc(mdat_atom.buffer); 1021 | 1022 | meta_complete = true; 1023 | return 1; 1024 | } 1025 | 1026 | int 1027 | Mp4Meta::mp4_update_stts_atom(Mp4Trak *trak) 1028 | { 1029 | uint32_t i, entries, count, duration, pass; 1030 | uint32_t start_sample, left, start_count; 1031 | uint32_t key_sample, old_sample; 1032 | uint64_t start_time, sum; 1033 | int64_t atom_size; 1034 | TSIOBufferReader readerp; 1035 | 1036 | if (trak->stts_data.buffer == NULL) 1037 | return -1; 1038 | 1039 | sum = start_count = 0; 1040 | 1041 | entries = trak->time_to_sample_entries; 1042 | start_time = this->start * trak->timescale / 1000; 1043 | if (this->rs > 0) { 1044 | start_time = (uint64_t)(this->rs * trak->timescale / 1000); 1045 | } 1046 | 1047 | start_sample = 0; 1048 | readerp = TSIOBufferReaderClone(trak->stts_data.reader); 1049 | 1050 | for (i = 0; i < entries; i++) { 1051 | duration = (uint32_t)mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, duration)); 1052 | count = (uint32_t)mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, count)); 1053 | 1054 | if (start_time < (uint64_t)count * duration) { 1055 | pass = (uint32_t)(start_time/duration); 1056 | start_sample += pass; 1057 | count -= pass; 1058 | 1059 | goto found; 1060 | } 1061 | 1062 | start_sample += count; 1063 | start_time -= (uint64_t)count * duration; 1064 | TSIOBufferReaderConsume(readerp, sizeof(mp4_stts_entry)); 1065 | } 1066 | 1067 | found: 1068 | 1069 | TSIOBufferReaderFree(readerp); 1070 | 1071 | old_sample = start_sample; 1072 | key_sample = this->mp4_find_key_sample(start_sample, trak); // find the last key frame before start_sample 1073 | 1074 | if (old_sample != key_sample) { 1075 | start_sample = key_sample - 1; 1076 | } 1077 | 1078 | readerp = TSIOBufferReaderClone(trak->stts_data.reader); 1079 | 1080 | trak->start_sample = start_sample; 1081 | 1082 | for (i = 0; i < entries; i++) { 1083 | duration = (uint32_t)mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, duration)); 1084 | count = (uint32_t)mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, count)); 1085 | 1086 | if (start_sample < count) { 1087 | count -= start_sample; 1088 | mp4_reader_set_32value(readerp, offsetof(mp4_stts_entry, count), count); 1089 | 1090 | sum += (uint64_t)start_sample * duration; 1091 | break; 1092 | } 1093 | 1094 | start_sample -= count; 1095 | sum += (uint64_t)count * duration; 1096 | 1097 | TSIOBufferReaderConsume(readerp, sizeof(mp4_stts_entry)); 1098 | } 1099 | 1100 | if (this->rs == 0) { 1101 | this->rs = ((double)sum/trak->duration) * ((double)trak->duration/trak->timescale) * 1000; 1102 | } 1103 | 1104 | left = entries - i; 1105 | 1106 | atom_size = sizeof(mp4_stts_atom) + left * sizeof(mp4_stts_entry); 1107 | trak->size += atom_size; 1108 | 1109 | mp4_reader_set_32value(trak->stts_atom.reader, offsetof(mp4_stts_atom, size), atom_size); 1110 | mp4_reader_set_32value(trak->stts_atom.reader, offsetof(mp4_stts_atom, entries), left); 1111 | 1112 | TSIOBufferReaderConsume(trak->stts_data.reader, i * sizeof(mp4_stts_entry)); 1113 | TSIOBufferReaderFree(readerp); 1114 | 1115 | return 0; 1116 | } 1117 | 1118 | int 1119 | Mp4Meta::mp4_update_stss_atom(Mp4Trak *trak) 1120 | { 1121 | int64_t atom_size; 1122 | uint32_t i, j, entries, sample, start_sample, left; 1123 | TSIOBufferReader readerp; 1124 | 1125 | if (trak->stss_data.buffer == NULL) 1126 | return 0; 1127 | 1128 | readerp = TSIOBufferReaderClone(trak->stss_data.reader); 1129 | 1130 | start_sample = trak->start_sample + 1; 1131 | entries = trak->sync_samples_entries; 1132 | 1133 | for (i = 0; i < entries ; i++) { 1134 | sample = (uint32_t)mp4_reader_get_32value(readerp, 0); 1135 | 1136 | if (sample >= start_sample) { 1137 | goto found; 1138 | } 1139 | 1140 | TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); 1141 | } 1142 | 1143 | TSIOBufferReaderFree(readerp); 1144 | return -1; 1145 | 1146 | found: 1147 | 1148 | left = entries - i; 1149 | 1150 | start_sample = trak->start_sample; 1151 | for (j = 0; j < left; j++) { 1152 | sample = (uint32_t)mp4_reader_get_32value(readerp, 0); 1153 | sample -= start_sample; 1154 | mp4_reader_set_32value(readerp, 0, sample); 1155 | TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); 1156 | } 1157 | 1158 | atom_size = sizeof(mp4_stss_atom) + left * sizeof(uint32_t); 1159 | trak->size += atom_size; 1160 | 1161 | mp4_reader_set_32value(trak->stss_atom.reader, offsetof(mp4_stss_atom, size), 1162 | atom_size); 1163 | 1164 | mp4_reader_set_32value(trak->stss_atom.reader, offsetof(mp4_stss_atom, entries), 1165 | left); 1166 | 1167 | TSIOBufferReaderConsume(trak->stss_data.reader, i * sizeof(uint32_t)); 1168 | TSIOBufferReaderFree(readerp); 1169 | 1170 | return 0; 1171 | } 1172 | 1173 | int 1174 | Mp4Meta::mp4_update_ctts_atom(Mp4Trak *trak) 1175 | { 1176 | int64_t atom_size; 1177 | uint32_t i, entries, start_sample, left; 1178 | uint32_t count; 1179 | TSIOBufferReader readerp; 1180 | 1181 | if (trak->ctts_data.buffer == NULL) 1182 | return 0; 1183 | 1184 | readerp = TSIOBufferReaderClone(trak->ctts_data.reader); 1185 | 1186 | start_sample = trak->start_sample + 1; 1187 | entries = trak->composition_offset_entries; 1188 | 1189 | for (i = 0; i < entries; i++) { 1190 | count = (uint32_t)mp4_reader_get_32value(readerp, offsetof(mp4_ctts_entry, count)); 1191 | 1192 | if (start_sample <= count) { 1193 | count -= (start_sample - 1); 1194 | mp4_reader_set_32value(readerp, offsetof(mp4_ctts_entry, count), count); 1195 | goto found; 1196 | } 1197 | 1198 | start_sample -= count; 1199 | TSIOBufferReaderConsume(readerp, sizeof(mp4_ctts_entry)); 1200 | } 1201 | 1202 | if (trak->ctts_atom.reader) { 1203 | TSIOBufferReaderFree(trak->ctts_atom.reader); 1204 | TSIOBufferDestroy(trak->ctts_atom.buffer); 1205 | 1206 | trak->ctts_atom.buffer = NULL; 1207 | trak->ctts_atom.reader = NULL; 1208 | } 1209 | 1210 | if (trak->ctts_data.reader) { 1211 | TSIOBufferReaderFree(trak->ctts_data.reader); 1212 | TSIOBufferDestroy(trak->ctts_data.buffer); 1213 | 1214 | trak->ctts_data.reader = NULL; 1215 | trak->ctts_data.buffer = NULL; 1216 | } 1217 | 1218 | TSIOBufferReaderFree(readerp); 1219 | return 0; 1220 | 1221 | found: 1222 | 1223 | left = entries - i; 1224 | atom_size = sizeof(mp4_ctts_atom) + left * sizeof(mp4_ctts_entry); 1225 | trak->size += atom_size; 1226 | 1227 | mp4_reader_set_32value(trak->ctts_atom.reader, offsetof(mp4_ctts_atom, size), atom_size); 1228 | mp4_reader_set_32value(trak->ctts_atom.reader, offsetof(mp4_ctts_atom, entries), left); 1229 | 1230 | TSIOBufferReaderConsume(trak->ctts_data.reader, i * sizeof(mp4_ctts_entry)); 1231 | TSIOBufferReaderFree(readerp); 1232 | 1233 | return 0; 1234 | } 1235 | 1236 | int 1237 | Mp4Meta::mp4_update_stsc_atom(Mp4Trak *trak) 1238 | { 1239 | int64_t atom_size; 1240 | uint32_t i, entries, samples, start_sample; 1241 | uint32_t chunk, next_chunk, n, id, j; 1242 | mp4_stsc_entry *first; 1243 | TSIOBufferReader readerp; 1244 | 1245 | if (trak->stsc_data.buffer == NULL) 1246 | return -1; 1247 | 1248 | if (trak->sample_to_chunk_entries == 0) 1249 | return -1; 1250 | 1251 | start_sample = (uint32_t) trak->start_sample; 1252 | entries = trak->sample_to_chunk_entries - 1; 1253 | 1254 | readerp = TSIOBufferReaderClone(trak->stsc_data.reader); 1255 | 1256 | chunk = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, chunk)); 1257 | samples = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, samples)); 1258 | id = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, id)); 1259 | 1260 | TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); 1261 | 1262 | for (i = 1; i < trak->sample_to_chunk_entries; i++) { 1263 | next_chunk = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, chunk)); 1264 | 1265 | n = (next_chunk - chunk) * samples; 1266 | 1267 | if (start_sample <= n) { 1268 | goto found; 1269 | } 1270 | 1271 | start_sample -= n; 1272 | 1273 | chunk = next_chunk; 1274 | samples = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, samples)); 1275 | id = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, id)); 1276 | 1277 | TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); 1278 | } 1279 | 1280 | next_chunk = trak->chunks; 1281 | 1282 | n = (next_chunk - chunk) * samples; 1283 | if (start_sample > n) { 1284 | TSIOBufferReaderFree(readerp); 1285 | return -1; 1286 | } 1287 | 1288 | found: 1289 | 1290 | TSIOBufferReaderFree(readerp); 1291 | 1292 | entries = trak->sample_to_chunk_entries - i + 1; 1293 | if (samples == 0) 1294 | return -1; 1295 | 1296 | readerp = TSIOBufferReaderClone(trak->stsc_data.reader); 1297 | TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry) * (i-1)); 1298 | 1299 | trak->start_chunk = chunk - 1; 1300 | trak->start_chunk += start_sample / samples; 1301 | trak->chunk_samples = start_sample % samples; 1302 | 1303 | atom_size = sizeof(mp4_stsc_atom) + entries * sizeof(mp4_stsc_entry); 1304 | 1305 | mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, chunk), 1); 1306 | 1307 | if (trak->chunk_samples && next_chunk - trak->start_chunk == 2) { 1308 | mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, samples), 1309 | samples - trak->chunk_samples); 1310 | 1311 | } else if (trak->chunk_samples) { 1312 | first = &trak->stsc_chunk_entry; 1313 | mp4_set_32value(first->chunk, 1); 1314 | mp4_set_32value(first->samples, samples - trak->chunk_samples); 1315 | mp4_set_32value(first->id, id); 1316 | 1317 | trak->stsc_chunk.buffer = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_128); 1318 | trak->stsc_chunk.reader = TSIOBufferReaderAlloc(trak->stsc_chunk.buffer); 1319 | TSIOBufferWrite(trak->stsc_chunk.buffer, first, sizeof(mp4_stsc_entry)); 1320 | 1321 | mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, chunk), 2); 1322 | 1323 | entries++; 1324 | atom_size += sizeof(mp4_stsc_entry); 1325 | } 1326 | 1327 | TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); 1328 | 1329 | for (j = i; j < trak->sample_to_chunk_entries; j++) { 1330 | chunk = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, chunk)); 1331 | chunk -= trak->start_chunk; 1332 | mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, chunk), chunk); 1333 | TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); 1334 | } 1335 | 1336 | trak->size += atom_size; 1337 | 1338 | mp4_reader_set_32value(trak->stsc_atom.reader, offsetof(mp4_stsc_atom, size), atom_size); 1339 | mp4_reader_set_32value(trak->stsc_atom.reader, offsetof(mp4_stsc_atom, entries), entries); 1340 | 1341 | TSIOBufferReaderConsume(trak->stsc_data.reader, (i-1) * sizeof(mp4_stsc_entry)); 1342 | TSIOBufferReaderFree(readerp); 1343 | 1344 | return 0; 1345 | } 1346 | 1347 | int 1348 | Mp4Meta::mp4_update_stsz_atom(Mp4Trak *trak) 1349 | { 1350 | uint32_t i; 1351 | int64_t atom_size, avail; 1352 | uint32_t pass; 1353 | TSIOBufferReader readerp; 1354 | 1355 | if (trak->stsz_data.buffer == NULL) 1356 | return 0; 1357 | 1358 | if (trak->start_sample > trak->sample_sizes_entries) 1359 | return -1; 1360 | 1361 | readerp = TSIOBufferReaderClone(trak->stsz_data.reader); 1362 | avail = TSIOBufferReaderAvail(readerp); 1363 | 1364 | pass = trak->start_sample * sizeof(uint32_t); 1365 | 1366 | TSIOBufferReaderConsume(readerp, pass - sizeof(uint32_t)*(trak->chunk_samples)); 1367 | 1368 | for (i = 0; i < trak->chunk_samples; i++) { 1369 | trak->chunk_samples_size += mp4_reader_get_32value(readerp, 0); 1370 | TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); 1371 | } 1372 | 1373 | atom_size = sizeof(mp4_stsz_atom) + avail - pass; 1374 | trak->size += atom_size; 1375 | 1376 | mp4_reader_set_32value(trak->stsz_atom.reader, offsetof(mp4_stsz_atom, size), 1377 | atom_size); 1378 | mp4_reader_set_32value(trak->stsz_atom.reader, offsetof(mp4_stsz_atom, entries), 1379 | trak->sample_sizes_entries - trak->start_sample); 1380 | 1381 | TSIOBufferReaderConsume(trak->stsz_data.reader, pass); 1382 | TSIOBufferReaderFree(readerp); 1383 | 1384 | return 0; 1385 | } 1386 | 1387 | int 1388 | Mp4Meta::mp4_update_co64_atom(Mp4Trak *trak) 1389 | { 1390 | int64_t atom_size, avail, pass; 1391 | TSIOBufferReader readerp; 1392 | 1393 | if (trak->co64_data.buffer == NULL) 1394 | return -1; 1395 | 1396 | if (trak->start_chunk > trak->chunks) 1397 | return -1; 1398 | 1399 | readerp = trak->co64_data.reader; 1400 | avail = TSIOBufferReaderAvail(readerp); 1401 | 1402 | pass = trak->start_chunk * sizeof(uint64_t); 1403 | atom_size = sizeof(mp4_co64_atom) + avail - pass; 1404 | trak->size += atom_size; 1405 | 1406 | TSIOBufferReaderConsume(readerp, pass); 1407 | trak->start_offset = mp4_reader_get_64value(readerp, 0); 1408 | trak->start_offset += trak->chunk_samples_size; 1409 | mp4_reader_set_64value(readerp, 0, trak->start_offset); 1410 | 1411 | mp4_reader_set_32value(trak->co64_atom.reader, offsetof(mp4_co64_atom, size), 1412 | atom_size); 1413 | mp4_reader_set_32value(trak->co64_atom.reader, offsetof(mp4_co64_atom, entries), 1414 | trak->chunks - trak->start_chunk); 1415 | 1416 | return 0; 1417 | } 1418 | 1419 | int 1420 | Mp4Meta::mp4_update_stco_atom(Mp4Trak *trak) 1421 | { 1422 | int64_t atom_size, avail; 1423 | uint32_t pass; 1424 | TSIOBufferReader readerp; 1425 | 1426 | if (trak->stco_data.buffer == NULL) 1427 | return -1; 1428 | 1429 | if (trak->start_chunk > trak->chunks) 1430 | return -1; 1431 | 1432 | readerp = trak->stco_data.reader; 1433 | avail = TSIOBufferReaderAvail(readerp); 1434 | 1435 | pass = trak->start_chunk * sizeof(uint32_t); 1436 | atom_size = sizeof(mp4_stco_atom) + avail - pass; 1437 | trak->size += atom_size; 1438 | 1439 | TSIOBufferReaderConsume(readerp, pass); 1440 | 1441 | trak->start_offset = mp4_reader_get_32value(readerp, 0); 1442 | trak->start_offset += trak->chunk_samples_size; 1443 | mp4_reader_set_32value(readerp, 0, trak->start_offset); 1444 | 1445 | mp4_reader_set_32value(trak->stco_atom.reader, offsetof(mp4_stco_atom, size), 1446 | atom_size); 1447 | mp4_reader_set_32value(trak->stco_atom.reader, offsetof(mp4_stco_atom, entries), 1448 | trak->chunks - trak->start_chunk); 1449 | 1450 | return 0; 1451 | } 1452 | 1453 | int 1454 | Mp4Meta::mp4_update_stbl_atom(Mp4Trak *trak) 1455 | { 1456 | trak->size += sizeof(mp4_atom_header); 1457 | mp4_reader_set_32value(trak->stbl_atom.reader, 0, trak->size); 1458 | 1459 | return 0; 1460 | } 1461 | 1462 | int 1463 | Mp4Meta::mp4_update_minf_atom(Mp4Trak *trak) 1464 | { 1465 | trak->size += sizeof(mp4_atom_header) + 1466 | trak->vmhd_size + 1467 | trak->smhd_size + 1468 | trak->dinf_size; 1469 | 1470 | mp4_reader_set_32value(trak->minf_atom.reader, 0, trak->size); 1471 | 1472 | return 0; 1473 | } 1474 | 1475 | int 1476 | Mp4Meta::mp4_update_mdia_atom(Mp4Trak *trak) 1477 | { 1478 | trak->size += sizeof(mp4_atom_header); 1479 | mp4_reader_set_32value(trak->mdia_atom.reader, 0, trak->size); 1480 | 1481 | return 0; 1482 | } 1483 | 1484 | int 1485 | Mp4Meta::mp4_update_trak_atom(Mp4Trak *trak) 1486 | { 1487 | trak->size += sizeof(mp4_atom_header); 1488 | mp4_reader_set_32value(trak->trak_atom.reader, 0, trak->size); 1489 | 1490 | return 0; 1491 | } 1492 | 1493 | int 1494 | Mp4Meta::mp4_adjust_co64_atom(Mp4Trak *trak, off_t adjustment) 1495 | { 1496 | int64_t pos, avail, offset; 1497 | TSIOBufferReader readerp; 1498 | 1499 | readerp = TSIOBufferReaderClone(trak->co64_data.reader); 1500 | avail = TSIOBufferReaderAvail(readerp); 1501 | 1502 | for (pos = 0; pos < avail; pos += sizeof(uint64_t)) { 1503 | offset = mp4_reader_get_64value(readerp, 0); 1504 | offset += adjustment; 1505 | mp4_reader_set_64value(readerp, 0, offset); 1506 | TSIOBufferReaderConsume(readerp, sizeof(uint64_t)); 1507 | } 1508 | 1509 | TSIOBufferReaderFree(readerp); 1510 | 1511 | return 0; 1512 | } 1513 | 1514 | int 1515 | Mp4Meta::mp4_adjust_stco_atom(Mp4Trak *trak, int32_t adjustment) 1516 | { 1517 | int64_t pos, avail, offset; 1518 | TSIOBufferReader readerp; 1519 | 1520 | readerp = TSIOBufferReaderClone(trak->stco_data.reader); 1521 | avail = TSIOBufferReaderAvail(readerp); 1522 | 1523 | for (pos = 0; pos < avail; pos += sizeof(uint32_t)) { 1524 | offset = mp4_reader_get_32value(readerp, 0); 1525 | offset += adjustment; 1526 | mp4_reader_set_32value(readerp, 0, offset); 1527 | TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); 1528 | } 1529 | 1530 | TSIOBufferReaderFree(readerp); 1531 | 1532 | return 0; 1533 | } 1534 | 1535 | int64_t 1536 | Mp4Meta::mp4_update_mdat_atom(int64_t start_offset) 1537 | { 1538 | int64_t atom_data_size; 1539 | int64_t atom_size; 1540 | int64_t atom_header_size; 1541 | u_char *atom_header; 1542 | 1543 | atom_data_size = this->cl - start_offset; 1544 | this->start_pos = start_offset; 1545 | 1546 | atom_header = mdat_atom_header; 1547 | 1548 | if (atom_data_size > 0xffffffff) { 1549 | atom_size = 1; 1550 | atom_header_size = sizeof(mp4_atom_header64); 1551 | mp4_set_64value(atom_header + sizeof(mp4_atom_header), 1552 | sizeof(mp4_atom_header64) + atom_data_size); 1553 | 1554 | } else { 1555 | atom_size = sizeof(mp4_atom_header) + atom_data_size; 1556 | atom_header_size = sizeof(mp4_atom_header); 1557 | } 1558 | 1559 | this->content_length += atom_header_size + atom_data_size; 1560 | 1561 | mp4_set_32value(atom_header, atom_size); 1562 | mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't'); 1563 | 1564 | mdat_atom.buffer = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_128); 1565 | mdat_atom.reader = TSIOBufferReaderAlloc(mdat_atom.buffer); 1566 | 1567 | TSIOBufferWrite(mdat_atom.buffer, atom_header, atom_header_size); 1568 | 1569 | return atom_header_size; 1570 | } 1571 | 1572 | 1573 | uint32_t 1574 | Mp4Meta::mp4_find_key_sample(uint32_t start_sample, Mp4Trak *trak) 1575 | { 1576 | uint32_t i; 1577 | uint32_t sample, prev_sample, entries; 1578 | TSIOBufferReader readerp; 1579 | 1580 | if (trak->stss_data.buffer == NULL) 1581 | return start_sample; 1582 | 1583 | prev_sample = 1; 1584 | entries = trak->sync_samples_entries; 1585 | 1586 | readerp = TSIOBufferReaderClone(trak->stss_data.reader); 1587 | 1588 | for (i = 0; i < entries; i++) { 1589 | sample = (uint32_t)mp4_reader_get_32value(readerp, 0); 1590 | 1591 | if (sample > start_sample) { 1592 | goto found; 1593 | } 1594 | 1595 | prev_sample = sample; 1596 | TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); 1597 | } 1598 | 1599 | found: 1600 | 1601 | TSIOBufferReaderFree(readerp); 1602 | return prev_sample; 1603 | } 1604 | 1605 | void 1606 | Mp4Meta::mp4_update_mvhd_duration() 1607 | { 1608 | int64_t need; 1609 | uint64_t duration, cut; 1610 | mp4_mvhd_atom *mvhd; 1611 | mp4_mvhd64_atom mvhd64; 1612 | 1613 | need = TSIOBufferReaderAvail(mvhd_atom.reader); 1614 | 1615 | if (need > (int64_t)sizeof(mp4_mvhd64_atom)) 1616 | need = sizeof(mp4_mvhd64_atom); 1617 | 1618 | IOBufferReaderCopy(mvhd_atom.reader, &mvhd64, need); 1619 | mvhd = (mp4_mvhd_atom*)&mvhd64; 1620 | 1621 | if (this->rs > 0) { 1622 | cut = (uint64_t)(this->rs * this->timescale / 1000); 1623 | 1624 | } else { 1625 | cut = this->start * this->timescale / 1000; 1626 | } 1627 | 1628 | if (mvhd->version[0] == 0) { 1629 | duration = mp4_get_32value(mvhd->duration); 1630 | duration -= cut; 1631 | mp4_reader_set_32value(mvhd_atom.reader, offsetof(mp4_mvhd_atom, duration), duration); 1632 | 1633 | } else { // 64-bit duration 1634 | duration = mp4_get_64value(mvhd64.duration); 1635 | duration -= cut; 1636 | mp4_reader_set_64value(mvhd_atom.reader, offsetof(mp4_mvhd64_atom, duration), duration); 1637 | } 1638 | } 1639 | 1640 | void 1641 | Mp4Meta::mp4_update_tkhd_duration(Mp4Trak *trak) 1642 | { 1643 | int64_t need, cut; 1644 | mp4_tkhd_atom *tkhd_atom; 1645 | mp4_tkhd64_atom tkhd64_atom; 1646 | int64_t duration; 1647 | 1648 | need = TSIOBufferReaderAvail(trak->tkhd_atom.reader); 1649 | 1650 | if (need > (int64_t)sizeof(mp4_tkhd64_atom)) 1651 | need = sizeof(mp4_tkhd64_atom); 1652 | 1653 | IOBufferReaderCopy(trak->tkhd_atom.reader, &tkhd64_atom, need); 1654 | tkhd_atom = (mp4_tkhd_atom*)&tkhd64_atom; 1655 | 1656 | if (this->rs > 0) { 1657 | cut = (uint64_t)(this->rs * this->timescale / 1000); 1658 | 1659 | } else { 1660 | cut = this->start * this->timescale / 1000; 1661 | } 1662 | 1663 | if (tkhd_atom->version[0] == 0) { 1664 | duration = mp4_get_32value(tkhd_atom->duration); 1665 | duration -= cut; 1666 | mp4_reader_set_32value(trak->tkhd_atom.reader, offsetof(mp4_tkhd_atom, duration), duration); 1667 | 1668 | } else { 1669 | duration = mp4_get_64value(tkhd64_atom.duration); 1670 | duration -= cut; 1671 | mp4_reader_set_64value(trak->tkhd_atom.reader, offsetof(mp4_tkhd64_atom, duration), duration); 1672 | } 1673 | } 1674 | 1675 | void 1676 | Mp4Meta::mp4_update_mdhd_duration(Mp4Trak *trak) 1677 | { 1678 | int64_t duration, need, cut; 1679 | mp4_mdhd_atom *mdhd; 1680 | mp4_mdhd64_atom mdhd64; 1681 | 1682 | need = TSIOBufferReaderAvail(trak->mdhd_atom.reader); 1683 | if (need > (int64_t)sizeof(mp4_mdhd64_atom)) 1684 | need = sizeof(mp4_mdhd64_atom); 1685 | 1686 | IOBufferReaderCopy(trak->mdhd_atom.reader, &mdhd64, need); 1687 | mdhd = (mp4_mdhd_atom*)&mdhd64; 1688 | 1689 | if (this->rs > 0) { 1690 | cut = (uint64_t)(this->rs * trak->timescale / 1000); 1691 | 1692 | } else { 1693 | cut = this->start * trak->timescale / 1000; 1694 | } 1695 | 1696 | if (mdhd->version[0] == 0) { 1697 | duration = mp4_get_32value(mdhd->duration); 1698 | duration -= cut; 1699 | mp4_reader_set_32value(trak->mdhd_atom.reader, offsetof(mp4_mdhd_atom, duration), duration); 1700 | 1701 | } else { 1702 | duration = mp4_get_64value(mdhd64.duration); 1703 | duration -= cut; 1704 | mp4_reader_set_64value(trak->mdhd_atom.reader, offsetof(mp4_mdhd64_atom, duration), duration); 1705 | } 1706 | } 1707 | 1708 | 1709 | static void 1710 | mp4_reader_set_32value(TSIOBufferReader readerp, int64_t offset, uint32_t n) 1711 | { 1712 | int pos; 1713 | int64_t avail, left; 1714 | TSIOBufferBlock blk; 1715 | const char *start; 1716 | u_char *ptr; 1717 | 1718 | pos = 0; 1719 | blk = TSIOBufferReaderStart(readerp); 1720 | 1721 | while (blk) { 1722 | 1723 | start = TSIOBufferBlockReadStart(blk, readerp, &avail); 1724 | 1725 | if (avail <= offset) { 1726 | offset -= avail; 1727 | 1728 | } else { 1729 | 1730 | left = avail - offset; 1731 | ptr = (u_char*)(const_cast (start) + offset); 1732 | 1733 | while (pos < 4 && left > 0) { 1734 | *ptr++ = (u_char) ((n) >> ((3 - pos) * 8)); 1735 | pos++; 1736 | left--; 1737 | } 1738 | 1739 | if (pos >= 4) 1740 | return; 1741 | 1742 | offset = 0; 1743 | } 1744 | 1745 | blk = TSIOBufferBlockNext(blk); 1746 | } 1747 | } 1748 | 1749 | static void 1750 | mp4_reader_set_64value(TSIOBufferReader readerp, int64_t offset, uint64_t n) 1751 | { 1752 | int pos; 1753 | int64_t avail, left; 1754 | TSIOBufferBlock blk; 1755 | const char *start; 1756 | u_char *ptr; 1757 | 1758 | pos = 0; 1759 | blk = TSIOBufferReaderStart(readerp); 1760 | 1761 | while (blk) { 1762 | 1763 | start = TSIOBufferBlockReadStart(blk, readerp, &avail); 1764 | 1765 | if (avail <= offset) { 1766 | offset -= avail; 1767 | 1768 | } else { 1769 | 1770 | left = avail - offset; 1771 | ptr = (u_char*)(const_cast (start) + offset); 1772 | 1773 | while (pos < 8 && left > 0) { 1774 | *ptr++ = (u_char) ((n) >> ((7 - pos) * 8)); 1775 | pos++; 1776 | left--; 1777 | } 1778 | 1779 | if (pos >= 4) 1780 | return; 1781 | 1782 | offset = 0; 1783 | } 1784 | 1785 | blk = TSIOBufferBlockNext(blk); 1786 | } 1787 | } 1788 | 1789 | static uint32_t 1790 | mp4_reader_get_32value(TSIOBufferReader readerp, int64_t offset) 1791 | { 1792 | int pos; 1793 | int64_t avail, left; 1794 | TSIOBufferBlock blk; 1795 | const char *start; 1796 | const u_char *ptr; 1797 | u_char res[4]; 1798 | 1799 | pos = 0; 1800 | blk = TSIOBufferReaderStart(readerp); 1801 | 1802 | while (blk) { 1803 | 1804 | start = TSIOBufferBlockReadStart(blk, readerp, &avail); 1805 | 1806 | if (avail <= offset) { 1807 | offset -= avail; 1808 | 1809 | } else { 1810 | 1811 | left = avail - offset; 1812 | ptr = (u_char*)(start + offset); 1813 | 1814 | while (pos < 4 && left > 0) { 1815 | res[3-pos] = *ptr++; 1816 | pos++; 1817 | left--; 1818 | } 1819 | 1820 | if (pos >= 4) { 1821 | return *(uint32_t*)res; 1822 | } 1823 | 1824 | offset = 0; 1825 | } 1826 | 1827 | blk = TSIOBufferBlockNext(blk); 1828 | } 1829 | 1830 | return -1; 1831 | } 1832 | 1833 | static uint64_t 1834 | mp4_reader_get_64value(TSIOBufferReader readerp, int64_t offset) 1835 | { 1836 | int pos; 1837 | int64_t avail, left; 1838 | TSIOBufferBlock blk; 1839 | const char *start; 1840 | u_char *ptr; 1841 | u_char res[8]; 1842 | 1843 | pos = 0; 1844 | blk = TSIOBufferReaderStart(readerp); 1845 | 1846 | while (blk) { 1847 | 1848 | start = TSIOBufferBlockReadStart(blk, readerp, &avail); 1849 | 1850 | if (avail <= offset) { 1851 | offset -= avail; 1852 | 1853 | } else { 1854 | 1855 | left = avail - offset; 1856 | ptr = (u_char*)(start + offset); 1857 | 1858 | while (pos < 8 && left > 0) { 1859 | res[7-pos] = *ptr++; 1860 | pos++; 1861 | left--; 1862 | } 1863 | 1864 | if (pos >= 8) { 1865 | return *(uint64_t*)res; 1866 | } 1867 | 1868 | offset = 0; 1869 | } 1870 | 1871 | blk = TSIOBufferBlockNext(blk); 1872 | } 1873 | 1874 | return -1; 1875 | } 1876 | 1877 | static int64_t 1878 | IOBufferReaderCopy(TSIOBufferReader readerp, void *buf, int64_t length) 1879 | { 1880 | int64_t avail, need, n; 1881 | const char *start; 1882 | TSIOBufferBlock blk; 1883 | 1884 | n = 0; 1885 | blk = TSIOBufferReaderStart(readerp); 1886 | 1887 | while (blk) { 1888 | start = TSIOBufferBlockReadStart(blk, readerp, &avail); 1889 | need = length < avail ? length : avail; 1890 | 1891 | if (need > 0) { 1892 | memcpy((char*)buf + n, start, need); 1893 | length -= need; 1894 | n += need; 1895 | } 1896 | 1897 | if (length == 0) 1898 | break; 1899 | 1900 | blk = TSIOBufferBlockNext(blk); 1901 | } 1902 | 1903 | return n; 1904 | } 1905 | -------------------------------------------------------------------------------- /src/mp4_meta.h: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | #ifndef _MP4_META_H 20 | #define _MP4_META_H 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #define MP4_MAX_TRAK_NUM 6 33 | #define MP4_MAX_BUFFER_SIZE (10 * 1024 * 1024) 34 | #define MP4_MIN_BUFFER_SIZE 1024 35 | 36 | #define DEBUG_TAG "ts_mp4" 37 | 38 | #define mp4_set_atom_name(p, n1, n2, n3, n4) \ 39 | ((u_char *) (p))[4] = n1; \ 40 | ((u_char *) (p))[5] = n2; \ 41 | ((u_char *) (p))[6] = n3; \ 42 | ((u_char *) (p))[7] = n4 43 | 44 | #define mp4_get_32value(p) \ 45 | ( ((uint32_t) ((u_char *) (p))[0] << 24) \ 46 | + ( ((u_char *) (p))[1] << 16) \ 47 | + ( ((u_char *) (p))[2] << 8) \ 48 | + ( ((u_char *) (p))[3]) ) 49 | 50 | #define mp4_set_32value(p, n) \ 51 | ((u_char *) (p))[0] = (u_char) ((n) >> 24); \ 52 | ((u_char *) (p))[1] = (u_char) ((n) >> 16); \ 53 | ((u_char *) (p))[2] = (u_char) ((n) >> 8); \ 54 | ((u_char *) (p))[3] = (u_char) (n) 55 | 56 | #define mp4_get_64value(p) \ 57 | ( ((uint64_t) ((u_char *) (p))[0] << 56) \ 58 | + ((uint64_t) ((u_char *) (p))[1] << 48) \ 59 | + ((uint64_t) ((u_char *) (p))[2] << 40) \ 60 | + ((uint64_t) ((u_char *) (p))[3] << 32) \ 61 | + ((uint64_t) ((u_char *) (p))[4] << 24) \ 62 | + ( ((u_char *) (p))[5] << 16) \ 63 | + ( ((u_char *) (p))[6] << 8) \ 64 | + ( ((u_char *) (p))[7]) ) 65 | 66 | #define mp4_set_64value(p, n) \ 67 | ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56); \ 68 | ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48); \ 69 | ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40); \ 70 | ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32); \ 71 | ((u_char *) (p))[4] = (u_char) ( (n) >> 24); \ 72 | ((u_char *) (p))[5] = (u_char) ( (n) >> 16); \ 73 | ((u_char *) (p))[6] = (u_char) ( (n) >> 8); \ 74 | ((u_char *) (p))[7] = (u_char) (n) 75 | 76 | 77 | typedef enum { 78 | MP4_TRAK_ATOM = 0, 79 | MP4_TKHD_ATOM, 80 | MP4_MDIA_ATOM, 81 | MP4_MDHD_ATOM, 82 | MP4_HDLR_ATOM, 83 | MP4_MINF_ATOM, 84 | MP4_VMHD_ATOM, 85 | MP4_SMHD_ATOM, 86 | MP4_DINF_ATOM, 87 | MP4_STBL_ATOM, 88 | MP4_STSD_ATOM, 89 | MP4_STTS_ATOM, 90 | MP4_STTS_DATA, 91 | MP4_STSS_ATOM, 92 | MP4_STSS_DATA, 93 | MP4_CTTS_ATOM, 94 | MP4_CTTS_DATA, 95 | MP4_STSC_ATOM, 96 | MP4_STSC_CHUNK, 97 | MP4_STSC_DATA, 98 | MP4_STSZ_ATOM, 99 | MP4_STSZ_DATA, 100 | MP4_STCO_ATOM, 101 | MP4_STCO_DATA, 102 | MP4_CO64_ATOM, 103 | MP4_CO64_DATA, 104 | MP4_LAST_ATOM = MP4_CO64_DATA 105 | } TSMp4AtomID; 106 | 107 | 108 | typedef struct { 109 | u_char size[4]; 110 | u_char name[4]; 111 | } mp4_atom_header; 112 | 113 | typedef struct { 114 | u_char size[4]; 115 | u_char name[4]; 116 | u_char size64[8]; 117 | } mp4_atom_header64; 118 | 119 | typedef struct { 120 | u_char size[4]; 121 | u_char name[4]; 122 | u_char version[1]; 123 | u_char flags[3]; 124 | u_char creation_time[4]; 125 | u_char modification_time[4]; 126 | u_char timescale[4]; 127 | u_char duration[4]; 128 | u_char rate[4]; 129 | u_char volume[2]; 130 | u_char reserved[10]; 131 | u_char matrix[36]; 132 | u_char preview_time[4]; 133 | u_char preview_duration[4]; 134 | u_char poster_time[4]; 135 | u_char selection_time[4]; 136 | u_char selection_duration[4]; 137 | u_char current_time[4]; 138 | u_char next_track_id[4]; 139 | } mp4_mvhd_atom; 140 | 141 | typedef struct { 142 | u_char size[4]; 143 | u_char name[4]; 144 | u_char version[1]; 145 | u_char flags[3]; 146 | u_char creation_time[8]; 147 | u_char modification_time[8]; 148 | u_char timescale[4]; 149 | u_char duration[8]; 150 | u_char rate[4]; 151 | u_char volume[2]; 152 | u_char reserved[10]; 153 | u_char matrix[36]; 154 | u_char preview_time[4]; 155 | u_char preview_duration[4]; 156 | u_char poster_time[4]; 157 | u_char selection_time[4]; 158 | u_char selection_duration[4]; 159 | u_char current_time[4]; 160 | u_char next_track_id[4]; 161 | } mp4_mvhd64_atom; 162 | 163 | typedef struct { 164 | u_char size[4]; 165 | u_char name[4]; 166 | u_char version[1]; 167 | u_char flags[3]; 168 | u_char creation_time[4]; 169 | u_char modification_time[4]; 170 | u_char track_id[4]; 171 | u_char reserved1[4]; 172 | u_char duration[4]; 173 | u_char reserved2[8]; 174 | u_char layer[2]; 175 | u_char group[2]; 176 | u_char volume[2]; 177 | u_char reverved3[2]; 178 | u_char matrix[36]; 179 | u_char width[4]; 180 | u_char heigth[4]; 181 | } mp4_tkhd_atom; 182 | 183 | typedef struct { 184 | u_char size[4]; 185 | u_char name[4]; 186 | u_char version[1]; 187 | u_char flags[3]; 188 | u_char creation_time[8]; 189 | u_char modification_time[8]; 190 | u_char track_id[4]; 191 | u_char reserved1[4]; 192 | u_char duration[8]; 193 | u_char reserved2[8]; 194 | u_char layer[2]; 195 | u_char group[2]; 196 | u_char volume[2]; 197 | u_char reverved3[2]; 198 | u_char matrix[36]; 199 | u_char width[4]; 200 | u_char heigth[4]; 201 | } mp4_tkhd64_atom; 202 | 203 | typedef struct { 204 | u_char size[4]; 205 | u_char name[4]; 206 | u_char version[1]; 207 | u_char flags[3]; 208 | u_char creation_time[4]; 209 | u_char modification_time[4]; 210 | u_char timescale[4]; 211 | u_char duration[4]; 212 | u_char language[2]; 213 | u_char quality[2]; 214 | } mp4_mdhd_atom; 215 | 216 | typedef struct { 217 | u_char size[4]; 218 | u_char name[4]; 219 | u_char version[1]; 220 | u_char flags[3]; 221 | u_char creation_time[8]; 222 | u_char modification_time[8]; 223 | u_char timescale[4]; 224 | u_char duration[8]; 225 | u_char language[2]; 226 | u_char quality[2]; 227 | } mp4_mdhd64_atom; 228 | 229 | typedef struct { 230 | u_char size[4]; 231 | u_char name[4]; 232 | u_char version[1]; 233 | u_char flags[3]; 234 | u_char entries[4]; 235 | 236 | u_char media_size[4]; 237 | u_char media_name[4]; 238 | } mp4_stsd_atom; 239 | 240 | typedef struct { 241 | u_char size[4]; 242 | u_char name[4]; 243 | u_char version[1]; 244 | u_char flags[3]; 245 | u_char entries[4]; 246 | } mp4_stts_atom; 247 | 248 | typedef struct { 249 | u_char count[4]; 250 | u_char duration[4]; 251 | } mp4_stts_entry; 252 | 253 | typedef struct { 254 | u_char size[4]; 255 | u_char name[4]; 256 | u_char version[1]; 257 | u_char flags[3]; 258 | u_char entries[4]; 259 | } mp4_stss_atom; 260 | 261 | typedef struct { 262 | u_char size[4]; 263 | u_char name[4]; 264 | u_char version[1]; 265 | u_char flags[3]; 266 | u_char entries[4]; 267 | } mp4_ctts_atom; 268 | 269 | typedef struct { 270 | u_char count[4]; 271 | u_char offset[4]; 272 | } mp4_ctts_entry; 273 | 274 | typedef struct { 275 | u_char size[4]; 276 | u_char name[4]; 277 | u_char version[1]; 278 | u_char flags[3]; 279 | u_char entries[4]; 280 | } mp4_stsc_atom; 281 | 282 | typedef struct { 283 | u_char chunk[4]; 284 | u_char samples[4]; 285 | u_char id[4]; 286 | } mp4_stsc_entry; 287 | 288 | typedef struct { 289 | u_char size[4]; 290 | u_char name[4]; 291 | u_char version[1]; 292 | u_char flags[3]; 293 | u_char uniform_size[4]; 294 | u_char entries[4]; 295 | } mp4_stsz_atom; 296 | 297 | typedef struct { 298 | u_char size[4]; 299 | u_char name[4]; 300 | u_char version[1]; 301 | u_char flags[3]; 302 | u_char entries[4]; 303 | } mp4_stco_atom; 304 | 305 | typedef struct { 306 | u_char size[4]; 307 | u_char name[4]; 308 | u_char version[1]; 309 | u_char flags[3]; 310 | u_char entries[4]; 311 | } mp4_co64_atom; 312 | 313 | class Mp4Meta; 314 | typedef int (Mp4Meta::*Mp4AtomHandler) (int64_t atom_header_size, int64_t atom_data_size); 315 | 316 | typedef struct { 317 | const char *name; 318 | Mp4AtomHandler handler; 319 | } mp4_atom_handler; 320 | 321 | 322 | class BufferHandle 323 | { 324 | public: 325 | BufferHandle(): buffer(NULL), reader(NULL) 326 | { 327 | } 328 | 329 | ~BufferHandle() 330 | { 331 | if (reader) { 332 | TSIOBufferReaderFree(reader); 333 | reader = NULL; 334 | } 335 | 336 | if (buffer) { 337 | TSIOBufferDestroy(buffer); 338 | buffer = NULL; 339 | } 340 | } 341 | 342 | public: 343 | TSIOBuffer buffer; 344 | TSIOBufferReader reader; 345 | }; 346 | 347 | class Mp4Trak 348 | { 349 | public: 350 | Mp4Trak(): timescale(0), duration(0), time_to_sample_entries(0), sample_to_chunk_entries(0), 351 | sync_samples_entries(0), composition_offset_entries(0), sample_sizes_entries(0), 352 | chunks(0), start_sample(0), start_chunk(0), chunk_samples(0), 353 | chunk_samples_size(0), start_offset(0), tkhd_size(0), mdhd_size(0), hdlr_size(0), 354 | vmhd_size(0), smhd_size(0), dinf_size(0), size(0) 355 | { 356 | memset(&stsc_chunk_entry, 0, sizeof(mp4_stsc_entry)); 357 | } 358 | 359 | ~Mp4Trak() 360 | { 361 | } 362 | 363 | public: 364 | uint32_t timescale; 365 | int64_t duration; 366 | 367 | uint32_t time_to_sample_entries; // stsc 368 | uint32_t sample_to_chunk_entries; // stsc 369 | uint32_t sync_samples_entries; // stss 370 | uint32_t composition_offset_entries; // ctts 371 | uint32_t sample_sizes_entries; // stsz 372 | uint32_t chunks; // stco, co64 373 | 374 | uint32_t start_sample; 375 | uint32_t start_chunk; 376 | uint32_t chunk_samples; 377 | uint64_t chunk_samples_size; 378 | off_t start_offset; 379 | 380 | size_t tkhd_size; 381 | size_t mdhd_size; 382 | size_t hdlr_size; 383 | size_t vmhd_size; 384 | size_t smhd_size; 385 | size_t dinf_size; 386 | size_t size; 387 | 388 | BufferHandle trak_atom; 389 | BufferHandle tkhd_atom; 390 | BufferHandle mdia_atom; 391 | BufferHandle mdhd_atom; 392 | BufferHandle hdlr_atom; 393 | BufferHandle minf_atom; 394 | BufferHandle vmhd_atom; 395 | BufferHandle smhd_atom; 396 | BufferHandle dinf_atom; 397 | BufferHandle stbl_atom; 398 | BufferHandle stsd_atom; 399 | BufferHandle stts_atom; 400 | BufferHandle stts_data; 401 | BufferHandle stss_atom; 402 | BufferHandle stss_data; 403 | BufferHandle ctts_atom; 404 | BufferHandle ctts_data; 405 | BufferHandle stsc_atom; 406 | BufferHandle stsc_chunk; 407 | BufferHandle stsc_data; 408 | BufferHandle stsz_atom; 409 | BufferHandle stsz_data; 410 | BufferHandle stco_atom; 411 | BufferHandle stco_data; 412 | BufferHandle co64_atom; 413 | BufferHandle co64_data; 414 | 415 | mp4_stsc_entry stsc_chunk_entry; 416 | }; 417 | 418 | class Mp4Meta 419 | { 420 | public: 421 | Mp4Meta(): start(0), cl(0), content_length(0), meta_atom_size(0), 422 | meta_avail(0), wait_next(0), need_size(0), rs(0), rate(0), 423 | ftyp_size(0), moov_size(0), start_pos(0), timescale(0), trak_num(0), 424 | passed(0), meta_complete(false) 425 | { 426 | meta_buffer = TSIOBufferCreate(); 427 | meta_reader = TSIOBufferReaderAlloc(meta_buffer); 428 | } 429 | 430 | ~Mp4Meta() 431 | { 432 | uint32_t i; 433 | 434 | for (i = 0; i < trak_num; i++) 435 | delete trak_vec[i]; 436 | 437 | if (meta_reader) { 438 | TSIOBufferReaderFree(meta_reader); 439 | meta_reader = NULL; 440 | } 441 | 442 | if (meta_buffer) { 443 | TSIOBufferDestroy(meta_buffer); 444 | meta_buffer = NULL; 445 | } 446 | } 447 | 448 | int parse_meta(bool body_complete); 449 | 450 | int post_process_meta(); 451 | void mp4_meta_consume(int64_t size); 452 | int mp4_atom_next(int64_t atom_size, bool wait=false); 453 | 454 | int mp4_read_atom(mp4_atom_handler *atom, int64_t size); 455 | int parse_root_atoms(); 456 | 457 | int mp4_read_ftyp_atom(int64_t header_size, int64_t data_size); 458 | int mp4_read_moov_atom(int64_t header_size, int64_t data_size); 459 | int mp4_read_mdat_atom(int64_t header_size, int64_t data_size); 460 | 461 | int mp4_read_mvhd_atom(int64_t header_size, int64_t data_size); 462 | int mp4_read_trak_atom(int64_t header_size, int64_t data_size); 463 | int mp4_read_cmov_atom(int64_t header_size, int64_t data_size); 464 | 465 | int mp4_read_tkhd_atom(int64_t header_size, int64_t data_size); 466 | int mp4_read_mdia_atom(int64_t header_size, int64_t data_size); 467 | 468 | int mp4_read_mdhd_atom(int64_t header_size, int64_t data_size); 469 | int mp4_read_hdlr_atom(int64_t header_size, int64_t data_size); 470 | int mp4_read_minf_atom(int64_t header_size, int64_t data_size); 471 | 472 | int mp4_read_vmhd_atom(int64_t header_size, int64_t data_size); 473 | int mp4_read_smhd_atom(int64_t header_size, int64_t data_size); 474 | int mp4_read_dinf_atom(int64_t header_size, int64_t data_size); 475 | int mp4_read_stbl_atom(int64_t header_size, int64_t data_size); 476 | 477 | int mp4_read_stsd_atom(int64_t header_size, int64_t data_size); 478 | int mp4_read_stts_atom(int64_t header_size, int64_t data_size); 479 | int mp4_read_stss_atom(int64_t header_size, int64_t data_size); 480 | int mp4_read_ctts_atom(int64_t header_size, int64_t data_size); 481 | int mp4_read_stsc_atom(int64_t header_size, int64_t data_size); 482 | int mp4_read_stsz_atom(int64_t header_size, int64_t data_size); 483 | int mp4_read_stco_atom(int64_t header_size, int64_t data_size); 484 | int mp4_read_co64_atom(int64_t header_size, int64_t data_size); 485 | 486 | int mp4_update_stts_atom(Mp4Trak *trak); 487 | int mp4_update_stss_atom(Mp4Trak *trak); 488 | int mp4_update_ctts_atom(Mp4Trak *trak); 489 | int mp4_update_stsc_atom(Mp4Trak *trak); 490 | int mp4_update_stsz_atom(Mp4Trak *trak); 491 | int mp4_update_co64_atom(Mp4Trak *trak); 492 | int mp4_update_stco_atom(Mp4Trak *trak); 493 | int mp4_update_stbl_atom(Mp4Trak *trak); 494 | int mp4_update_minf_atom(Mp4Trak *trak); 495 | int mp4_update_mdia_atom(Mp4Trak *trak); 496 | int mp4_update_trak_atom(Mp4Trak *trak); 497 | 498 | int64_t mp4_update_mdat_atom(int64_t start_offset); 499 | int mp4_adjust_co64_atom(Mp4Trak *trak, off_t adjustment); 500 | int mp4_adjust_stco_atom(Mp4Trak *trak, int32_t adjustment); 501 | 502 | uint32_t mp4_find_key_sample(uint32_t start_sample, Mp4Trak *trak); 503 | void mp4_update_mvhd_duration(); 504 | void mp4_update_tkhd_duration(Mp4Trak *trak); 505 | void mp4_update_mdhd_duration(Mp4Trak *trak); 506 | 507 | public: 508 | 509 | int64_t start; // requested start time, measured in milliseconds. 510 | int64_t cl; // the total size of the mp4 file 511 | int64_t content_length; // the size of the new mp4 file 512 | int64_t meta_atom_size; 513 | 514 | TSIOBuffer meta_buffer; // meta data to be parsed 515 | TSIOBufferReader meta_reader; 516 | 517 | int64_t meta_avail; 518 | int64_t wait_next; 519 | int64_t need_size; 520 | 521 | BufferHandle meta_atom; 522 | BufferHandle ftyp_atom; 523 | BufferHandle moov_atom; 524 | BufferHandle mvhd_atom; 525 | BufferHandle mdat_atom; 526 | BufferHandle mdat_data; 527 | BufferHandle out_handle; 528 | 529 | Mp4Trak *trak_vec[MP4_MAX_TRAK_NUM]; 530 | 531 | double rs; 532 | double rate; 533 | 534 | int64_t ftyp_size; 535 | int64_t moov_size; 536 | int64_t start_pos; // start position of the new mp4 file 537 | uint32_t timescale; 538 | 539 | uint32_t trak_num; 540 | 541 | int64_t passed; 542 | 543 | u_char mdat_atom_header[16]; 544 | 545 | bool meta_complete; 546 | }; 547 | 548 | #endif 549 | --------------------------------------------------------------------------------