| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "mp4_meta.h" |
| |
| static mp4_atom_handler mp4_atoms[] = { |
| {"ftyp", &Mp4Meta::mp4_read_ftyp_atom}, |
| {"moov", &Mp4Meta::mp4_read_moov_atom}, |
| {"mdat", &Mp4Meta::mp4_read_mdat_atom}, |
| {nullptr, nullptr } |
| }; |
| |
| static mp4_atom_handler mp4_moov_atoms[] = { |
| {"mvhd", &Mp4Meta::mp4_read_mvhd_atom}, |
| {"trak", &Mp4Meta::mp4_read_trak_atom}, |
| {"cmov", &Mp4Meta::mp4_read_cmov_atom}, |
| {nullptr, nullptr } |
| }; |
| |
| static mp4_atom_handler mp4_trak_atoms[] = { |
| {"tkhd", &Mp4Meta::mp4_read_tkhd_atom}, |
| {"mdia", &Mp4Meta::mp4_read_mdia_atom}, |
| {nullptr, nullptr } |
| }; |
| |
| static mp4_atom_handler mp4_mdia_atoms[] = { |
| {"mdhd", &Mp4Meta::mp4_read_mdhd_atom}, |
| {"hdlr", &Mp4Meta::mp4_read_hdlr_atom}, |
| {"minf", &Mp4Meta::mp4_read_minf_atom}, |
| {nullptr, nullptr } |
| }; |
| |
| static mp4_atom_handler mp4_minf_atoms[] = { |
| {"vmhd", &Mp4Meta::mp4_read_vmhd_atom}, |
| {"smhd", &Mp4Meta::mp4_read_smhd_atom}, |
| {"dinf", &Mp4Meta::mp4_read_dinf_atom}, |
| {"stbl", &Mp4Meta::mp4_read_stbl_atom}, |
| {nullptr, nullptr } |
| }; |
| |
| static mp4_atom_handler mp4_stbl_atoms[] = { |
| {"stsd", &Mp4Meta::mp4_read_stsd_atom}, |
| {"stts", &Mp4Meta::mp4_read_stts_atom}, |
| {"stss", &Mp4Meta::mp4_read_stss_atom}, |
| {"ctts", &Mp4Meta::mp4_read_ctts_atom}, |
| {"stsc", &Mp4Meta::mp4_read_stsc_atom}, |
| {"stsz", &Mp4Meta::mp4_read_stsz_atom}, |
| {"stco", &Mp4Meta::mp4_read_stco_atom}, |
| {"co64", &Mp4Meta::mp4_read_co64_atom}, |
| {nullptr, nullptr } |
| }; |
| |
| static void mp4_reader_set_32value(TSIOBufferReader readerp, int64_t offset, uint32_t n); |
| static void mp4_reader_set_64value(TSIOBufferReader readerp, int64_t offset, uint64_t n); |
| static uint32_t mp4_reader_get_32value(TSIOBufferReader readerp, int64_t offset); |
| static uint64_t mp4_reader_get_64value(TSIOBufferReader readerp, int64_t offset); |
| static int64_t IOBufferReaderCopy(TSIOBufferReader readerp, void *buf, int64_t length); |
| |
| int |
| Mp4Meta::parse_meta(bool body_complete) |
| { |
| int ret, rc; |
| |
| meta_avail = TSIOBufferReaderAvail(meta_reader); |
| |
| if (wait_next && wait_next <= meta_avail) { |
| mp4_meta_consume(wait_next); |
| wait_next = 0; |
| } |
| |
| if (meta_avail < MP4_MIN_BUFFER_SIZE && !body_complete) { |
| return 0; |
| } |
| |
| ret = this->parse_root_atoms(); |
| |
| if (ret < 0) { |
| return -1; |
| |
| } else if (ret == 0) { |
| if (body_complete) { |
| return -1; |
| |
| } else { |
| return 0; |
| } |
| } |
| |
| // generate new meta data |
| rc = this->post_process_meta(); |
| if (rc != 0) { |
| return -1; |
| } |
| |
| return 1; |
| } |
| |
| void |
| Mp4Meta::mp4_meta_consume(int64_t size) |
| { |
| TSIOBufferReaderConsume(meta_reader, size); |
| meta_avail -= size; |
| passed += size; |
| } |
| |
| int |
| Mp4Meta::post_process_meta() |
| { |
| off_t start_offset, adjustment; |
| uint32_t i, j; |
| int64_t avail; |
| Mp4Trak *trak; |
| |
| if (this->trak_num == 0) { |
| return -1; |
| } |
| |
| if (mdat_atom.buffer == nullptr) { |
| return -1; |
| } |
| |
| out_handle.buffer = TSIOBufferCreate(); |
| out_handle.reader = TSIOBufferReaderAlloc(out_handle.buffer); |
| |
| if (ftyp_atom.buffer) { |
| TSIOBufferCopy(out_handle.buffer, ftyp_atom.reader, TSIOBufferReaderAvail(ftyp_atom.reader), 0); |
| } |
| |
| if (moov_atom.buffer) { |
| TSIOBufferCopy(out_handle.buffer, moov_atom.reader, TSIOBufferReaderAvail(moov_atom.reader), 0); |
| } |
| |
| if (mvhd_atom.buffer) { |
| avail = TSIOBufferReaderAvail(mvhd_atom.reader); |
| TSIOBufferCopy(out_handle.buffer, mvhd_atom.reader, avail, 0); |
| this->moov_size += avail; |
| } |
| |
| start_offset = cl; |
| |
| for (i = 0; i < trak_num; i++) { |
| trak = trak_vec[i].get(); |
| |
| if (mp4_update_stts_atom(trak) != 0) { |
| return -1; |
| } |
| |
| if (mp4_update_stss_atom(trak) != 0) { |
| return -1; |
| } |
| |
| mp4_update_ctts_atom(trak); |
| |
| if (mp4_update_stsc_atom(trak) != 0) { |
| return -1; |
| } |
| |
| if (mp4_update_stsz_atom(trak) != 0) { |
| return -1; |
| } |
| |
| if (trak->atoms[MP4_CO64_DATA].buffer) { |
| if (mp4_update_co64_atom(trak) != 0) { |
| return -1; |
| } |
| |
| } else if (mp4_update_stco_atom(trak) != 0) { |
| return -1; |
| } |
| |
| mp4_update_stbl_atom(trak); |
| mp4_update_minf_atom(trak); |
| trak->size += trak->mdhd_size; |
| trak->size += trak->hdlr_size; |
| mp4_update_mdia_atom(trak); |
| trak->size += trak->tkhd_size; |
| mp4_update_trak_atom(trak); |
| |
| this->moov_size += trak->size; |
| |
| if (start_offset > trak->start_offset) { |
| start_offset = trak->start_offset; |
| } |
| |
| for (j = 0; j <= MP4_LAST_ATOM; j++) { |
| if (trak->atoms[j].buffer) { |
| TSIOBufferCopy(out_handle.buffer, trak->atoms[j].reader, TSIOBufferReaderAvail(trak->atoms[j].reader), 0); |
| } |
| } |
| |
| mp4_update_tkhd_duration(trak); |
| mp4_update_mdhd_duration(trak); |
| } |
| |
| this->moov_size += 8; |
| |
| mp4_reader_set_32value(moov_atom.reader, 0, this->moov_size); |
| this->content_length += this->moov_size; |
| |
| adjustment = this->ftyp_size + this->moov_size + mp4_update_mdat_atom(start_offset) - start_offset; |
| |
| TSIOBufferCopy(out_handle.buffer, mdat_atom.reader, TSIOBufferReaderAvail(mdat_atom.reader), 0); |
| |
| for (i = 0; i < trak_num; i++) { |
| trak = trak_vec[i].get(); |
| |
| if (trak->atoms[MP4_CO64_DATA].buffer) { |
| mp4_adjust_co64_atom(trak, adjustment); |
| |
| } else { |
| mp4_adjust_stco_atom(trak, adjustment); |
| } |
| } |
| |
| mp4_update_mvhd_duration(); |
| |
| return 0; |
| } |
| |
| /* |
| * -1: error |
| * 0: unfinished |
| * 1: success. |
| */ |
| int |
| Mp4Meta::parse_root_atoms() |
| { |
| int i, ret, rc; |
| int64_t atom_size, atom_header_size, copied_size; |
| char buf[64]; |
| char *atom_header, *atom_name; |
| |
| memset(buf, 0, sizeof(buf)); |
| |
| for (;;) { |
| if (meta_avail < static_cast<int64_t>(sizeof(uint32_t))) { |
| return 0; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, buf, sizeof(mp4_atom_header64)); |
| atom_size = copied_size > 0 ? mp4_get_32value(buf) : 0; |
| |
| if (atom_size == 0) { |
| return 1; |
| } |
| |
| atom_header = buf; |
| |
| if (atom_size < static_cast<int64_t>(sizeof(mp4_atom_header))) { |
| if (atom_size == 1) { |
| if (meta_avail < static_cast<int64_t>(sizeof(mp4_atom_header64))) { |
| return 0; |
| } |
| |
| } else { |
| return -1; |
| } |
| |
| atom_size = mp4_get_64value(atom_header + 8); |
| atom_header_size = sizeof(mp4_atom_header64); |
| |
| } else { // regular atom |
| |
| if (meta_avail < static_cast<int64_t>(sizeof(mp4_atom_header))) { // not enough for atom header |
| return 0; |
| } |
| |
| atom_header_size = sizeof(mp4_atom_header); |
| } |
| |
| atom_name = atom_header + 4; |
| |
| if (atom_size + this->passed > this->cl) { |
| return -1; |
| } |
| |
| for (i = 0; mp4_atoms[i].name; i++) { |
| if (memcmp(atom_name, mp4_atoms[i].name, 4) == 0) { |
| ret = (this->*mp4_atoms[i].handler)(atom_header_size, atom_size - atom_header_size); // -1: error, 0: unfinished, 1: success |
| |
| if (ret <= 0) { |
| return ret; |
| |
| } else if (meta_complete) { // success |
| return 1; |
| } |
| |
| goto next; |
| } |
| } |
| |
| // nonsignificant atom box |
| rc = mp4_atom_next(atom_size, true); // 0: unfinished, 1: success |
| if (rc == 0) { |
| return rc; |
| } |
| |
| next: |
| continue; |
| } |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_atom_next(int64_t atom_size, bool wait) |
| { |
| if (meta_avail >= atom_size) { |
| mp4_meta_consume(atom_size); |
| return 1; |
| } |
| |
| if (wait) { |
| wait_next = atom_size; |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| /* |
| * -1: error |
| * 1: success |
| */ |
| int |
| Mp4Meta::mp4_read_atom(mp4_atom_handler *atom, int64_t size) |
| { |
| int i, ret, rc; |
| int64_t atom_size, atom_header_size, copied_size; |
| char buf[32]; |
| char *atom_header, *atom_name; |
| |
| if (meta_avail < size) { // data insufficient, not reasonable for internal atom box. |
| return -1; |
| } |
| |
| while (size > 0) { |
| if (meta_avail < static_cast<int64_t>(sizeof(uint32_t))) { // data insufficient, not reasonable for internal atom box. |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, buf, sizeof(mp4_atom_header64)); |
| atom_size = copied_size > 0 ? mp4_get_32value(buf) : 0; |
| |
| if (atom_size == 0) { |
| return 1; |
| } |
| |
| atom_header = buf; |
| |
| if (atom_size < static_cast<int64_t>(sizeof(mp4_atom_header))) { |
| if (atom_size == 1) { |
| if (meta_avail < static_cast<int64_t>(sizeof(mp4_atom_header64))) { |
| return -1; |
| } |
| |
| } else { |
| return -1; |
| } |
| |
| atom_size = mp4_get_64value(atom_header + 8); |
| atom_header_size = sizeof(mp4_atom_header64); |
| |
| } else { // regular atom |
| |
| if (meta_avail < static_cast<int64_t>(sizeof(mp4_atom_header))) { |
| return -1; |
| } |
| |
| atom_header_size = sizeof(mp4_atom_header); |
| } |
| |
| atom_name = atom_header + 4; |
| |
| if (atom_size + this->passed > this->cl) { |
| return -1; |
| } |
| |
| for (i = 0; atom[i].name; i++) { |
| if (memcmp(atom_name, atom[i].name, 4) == 0) { |
| if (meta_avail < atom_size) { |
| return -1; |
| } |
| |
| ret = (this->*atom[i].handler)(atom_header_size, atom_size - atom_header_size); // -1: error, 0: success. |
| |
| if (ret < 0) { |
| return ret; |
| } |
| |
| goto next; |
| } |
| } |
| |
| // insignificant atom box |
| rc = mp4_atom_next(atom_size, false); |
| if (rc < 0) { |
| return rc; |
| } |
| |
| next: |
| size -= atom_size; |
| continue; |
| } |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_ftyp_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| |
| if (atom_data_size > MP4_MIN_BUFFER_SIZE) { |
| return -1; |
| } |
| |
| atom_size = atom_header_size + atom_data_size; |
| |
| if (meta_avail < atom_size) { // data insufficient, reasonable from the first level |
| return 0; |
| } |
| |
| ftyp_atom.buffer = TSIOBufferCreate(); |
| ftyp_atom.reader = TSIOBufferReaderAlloc(ftyp_atom.buffer); |
| |
| TSIOBufferCopy(ftyp_atom.buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| content_length = atom_size; |
| ftyp_size = atom_size; |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_moov_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| int ret; |
| |
| if (mdat_atom.buffer != nullptr) { // not reasonable for streaming media |
| return -1; |
| } |
| |
| atom_size = atom_header_size + atom_data_size; |
| |
| if (atom_data_size >= MP4_MAX_BUFFER_SIZE) { |
| return -1; |
| } |
| |
| if (meta_avail < atom_size) { // data insufficient, wait |
| return 0; |
| } |
| |
| moov_atom.buffer = TSIOBufferCreate(); |
| moov_atom.reader = TSIOBufferReaderAlloc(moov_atom.buffer); |
| |
| TSIOBufferCopy(moov_atom.buffer, meta_reader, atom_header_size, 0); |
| mp4_meta_consume(atom_header_size); |
| |
| ret = mp4_read_atom(mp4_moov_atoms, atom_data_size); |
| |
| return ret; |
| } |
| |
| int |
| Mp4Meta::mp4_read_mvhd_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| uint32_t timescale; |
| mp4_mvhd_atom *mvhd; |
| mp4_mvhd64_atom mvhd64; |
| |
| if (sizeof(mp4_mvhd_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| memset(&mvhd64, 0, sizeof(mvhd64)); |
| IOBufferReaderCopy(meta_reader, &mvhd64, sizeof(mp4_mvhd64_atom)); |
| mvhd = reinterpret_cast<mp4_mvhd_atom *>(&mvhd64); |
| |
| if (mvhd->version[0] == 0) { |
| timescale = mp4_get_32value(mvhd->timescale); |
| |
| } else { // 64-bit duration |
| timescale = mp4_get_32value(mvhd64.timescale); |
| } |
| |
| this->timescale = timescale; |
| |
| atom_size = atom_header_size + atom_data_size; |
| |
| mvhd_atom.buffer = TSIOBufferCreate(); |
| mvhd_atom.reader = TSIOBufferReaderAlloc(mvhd_atom.buffer); |
| |
| TSIOBufferCopy(mvhd_atom.buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_trak_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int rc; |
| Mp4Trak *trak; |
| |
| if (trak_num >= MP4_MAX_TRAK_NUM - 1) { |
| return -1; |
| } |
| |
| trak_vec[trak_num] = std::make_unique<Mp4Trak>(); |
| trak = trak_vec[trak_num++].get(); |
| |
| trak->atoms[MP4_TRAK_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_TRAK_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_TRAK_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_TRAK_ATOM].buffer, meta_reader, atom_header_size, 0); |
| mp4_meta_consume(atom_header_size); |
| |
| rc = mp4_read_atom(mp4_trak_atoms, atom_data_size); |
| |
| return rc; |
| } |
| |
| int |
| Mp4Meta::mp4_read_cmov_atom(int64_t /*atom_header_size ATS_UNUSED */, int64_t /* atom_data_size ATS_UNUSED */) |
| { |
| return -1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_tkhd_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| Mp4Trak *trak; |
| |
| atom_size = atom_header_size + atom_data_size; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->tkhd_size = atom_size; |
| |
| trak->atoms[MP4_TKHD_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_TKHD_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_TKHD_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_TKHD_ATOM].buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| mp4_reader_set_32value(trak->atoms[MP4_TKHD_ATOM].reader, offsetof(mp4_tkhd_atom, size), atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_mdia_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| Mp4Trak *trak; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| |
| trak->atoms[MP4_MDIA_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_MDIA_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_MDIA_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_MDIA_ATOM].buffer, meta_reader, atom_header_size, 0); |
| mp4_meta_consume(atom_header_size); |
| |
| return mp4_read_atom(mp4_mdia_atoms, atom_data_size); |
| } |
| |
| int |
| Mp4Meta::mp4_read_mdhd_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size, duration; |
| uint32_t ts; |
| Mp4Trak *trak; |
| mp4_mdhd_atom *mdhd; |
| mp4_mdhd64_atom mdhd64; |
| |
| memset(&mdhd64, 0, sizeof(mdhd64)); |
| IOBufferReaderCopy(meta_reader, &mdhd64, sizeof(mp4_mdhd64_atom)); |
| mdhd = reinterpret_cast<mp4_mdhd_atom *>(&mdhd64); |
| |
| if (mdhd->version[0] == 0) { |
| ts = mp4_get_32value(mdhd->timescale); |
| duration = mp4_get_32value(mdhd->duration); |
| |
| } else { |
| ts = mp4_get_32value(mdhd64.timescale); |
| duration = mp4_get_64value(mdhd64.duration); |
| } |
| |
| atom_size = atom_header_size + atom_data_size; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->mdhd_size = atom_size; |
| trak->timescale = ts; |
| trak->duration = duration; |
| |
| trak->atoms[MP4_MDHD_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_MDHD_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_MDHD_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_MDHD_ATOM].buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| mp4_reader_set_32value(trak->atoms[MP4_MDHD_ATOM].reader, offsetof(mp4_mdhd_atom, size), atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_hdlr_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| Mp4Trak *trak; |
| |
| atom_size = atom_header_size + atom_data_size; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->hdlr_size = atom_size; |
| |
| trak->atoms[MP4_HDLR_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_HDLR_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_HDLR_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_HDLR_ATOM].buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_minf_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| Mp4Trak *trak; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| |
| trak->atoms[MP4_MINF_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_MINF_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_MINF_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_MINF_ATOM].buffer, meta_reader, atom_header_size, 0); |
| mp4_meta_consume(atom_header_size); |
| |
| return mp4_read_atom(mp4_minf_atoms, atom_data_size); |
| } |
| |
| int |
| Mp4Meta::mp4_read_vmhd_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| Mp4Trak *trak; |
| |
| atom_size = atom_data_size + atom_header_size; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->vmhd_size += atom_size; |
| |
| trak->atoms[MP4_VMHD_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_VMHD_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_VMHD_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_VMHD_ATOM].buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_smhd_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| Mp4Trak *trak; |
| |
| atom_size = atom_data_size + atom_header_size; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->smhd_size += atom_size; |
| |
| trak->atoms[MP4_SMHD_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_SMHD_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_SMHD_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_SMHD_ATOM].buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_dinf_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| Mp4Trak *trak; |
| |
| atom_size = atom_data_size + atom_header_size; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->dinf_size += atom_size; |
| |
| trak->atoms[MP4_DINF_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_DINF_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_DINF_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_DINF_ATOM].buffer, meta_reader, atom_size, 0); |
| mp4_meta_consume(atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_stbl_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| Mp4Trak *trak; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| |
| trak->atoms[MP4_STBL_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STBL_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STBL_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_STBL_ATOM].buffer, meta_reader, atom_header_size, 0); |
| mp4_meta_consume(atom_header_size); |
| |
| return mp4_read_atom(mp4_stbl_atoms, atom_data_size); |
| } |
| |
| int |
| Mp4Meta::mp4_read_stsd_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int64_t atom_size; |
| Mp4Trak *trak; |
| |
| atom_size = atom_data_size + atom_header_size; |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->size += atom_size; |
| |
| trak->atoms[MP4_STSD_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STSD_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSD_ATOM].buffer); |
| |
| TSIOBufferCopy(trak->atoms[MP4_STSD_ATOM].buffer, meta_reader, atom_size, 0); |
| |
| mp4_meta_consume(atom_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_stts_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int32_t entries; |
| int64_t esize, copied_size; |
| mp4_stts_atom stts; |
| Mp4Trak *trak; |
| |
| if (sizeof(mp4_stts_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, &stts, sizeof(mp4_stts_atom)); |
| entries = copied_size > 0 ? mp4_get_32value(stts.entries) : 0; |
| esize = entries * sizeof(mp4_stts_entry); |
| |
| if (sizeof(mp4_stts_atom) - 8 + esize > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->time_to_sample_entries = entries; |
| |
| trak->atoms[MP4_STTS_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STTS_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STTS_ATOM].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STTS_ATOM].buffer, meta_reader, sizeof(mp4_stts_atom), 0); |
| |
| trak->atoms[MP4_STTS_DATA].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STTS_DATA].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STTS_DATA].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STTS_DATA].buffer, meta_reader, esize, sizeof(mp4_stts_atom)); |
| |
| mp4_meta_consume(atom_data_size + atom_header_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_stss_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int32_t entries; |
| int64_t esize, copied_size; |
| mp4_stss_atom stss; |
| Mp4Trak *trak; |
| |
| if (sizeof(mp4_stss_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, &stss, sizeof(mp4_stss_atom)); |
| entries = copied_size > 0 ? mp4_get_32value(stss.entries) : 0; |
| esize = entries * sizeof(int32_t); |
| |
| if (sizeof(mp4_stss_atom) - 8 + esize > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->sync_samples_entries = entries; |
| |
| trak->atoms[MP4_STSS_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STSS_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSS_ATOM].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STSS_ATOM].buffer, meta_reader, sizeof(mp4_stss_atom), 0); |
| |
| trak->atoms[MP4_STSS_DATA].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STSS_DATA].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSS_DATA].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STSS_DATA].buffer, meta_reader, esize, sizeof(mp4_stss_atom)); |
| |
| mp4_meta_consume(atom_data_size + atom_header_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_ctts_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int32_t entries; |
| int64_t esize, copied_size; |
| mp4_ctts_atom ctts; |
| Mp4Trak *trak; |
| |
| if (sizeof(mp4_ctts_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, &ctts, sizeof(mp4_ctts_atom)); |
| entries = copied_size > 0 ? mp4_get_32value(ctts.entries) : 0; |
| esize = entries * sizeof(mp4_ctts_entry); |
| |
| if (sizeof(mp4_ctts_atom) - 8 + esize > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->composition_offset_entries = entries; |
| |
| trak->atoms[MP4_CTTS_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_CTTS_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_CTTS_ATOM].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_CTTS_ATOM].buffer, meta_reader, sizeof(mp4_ctts_atom), 0); |
| |
| trak->atoms[MP4_CTTS_DATA].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_CTTS_DATA].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_CTTS_DATA].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_CTTS_DATA].buffer, meta_reader, esize, sizeof(mp4_ctts_atom)); |
| |
| mp4_meta_consume(atom_data_size + atom_header_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_stsc_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int32_t entries; |
| int64_t esize, copied_size; |
| mp4_stsc_atom stsc; |
| Mp4Trak *trak; |
| |
| if (sizeof(mp4_stsc_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, &stsc, sizeof(mp4_stsc_atom)); |
| entries = copied_size > 0 ? mp4_get_32value(stsc.entries) : 0; |
| esize = entries * sizeof(mp4_stsc_entry); |
| |
| if (sizeof(mp4_stsc_atom) - 8 + esize > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->sample_to_chunk_entries = entries; |
| |
| trak->atoms[MP4_STSC_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STSC_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSC_ATOM].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STSC_ATOM].buffer, meta_reader, sizeof(mp4_stsc_atom), 0); |
| |
| trak->atoms[MP4_STSC_DATA].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STSC_DATA].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSC_DATA].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STSC_DATA].buffer, meta_reader, esize, sizeof(mp4_stsc_atom)); |
| |
| mp4_meta_consume(atom_data_size + atom_header_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_stsz_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int32_t entries, size; |
| int64_t esize, atom_size, copied_size; |
| mp4_stsz_atom stsz; |
| Mp4Trak *trak; |
| |
| if (sizeof(mp4_stsz_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, &stsz, sizeof(mp4_stsz_atom)); |
| entries = copied_size > 0 ? mp4_get_32value(stsz.entries) : 0; |
| esize = entries * sizeof(int32_t); |
| |
| trak = trak_vec[trak_num - 1].get(); |
| size = copied_size > 0 ? mp4_get_32value(stsz.uniform_size) : 0; |
| |
| trak->sample_sizes_entries = entries; |
| |
| trak->atoms[MP4_STSZ_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STSZ_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSZ_ATOM].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STSZ_ATOM].buffer, meta_reader, sizeof(mp4_stsz_atom), 0); |
| |
| if (size == 0) { |
| if (sizeof(mp4_stsz_atom) - 8 + esize > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| trak->atoms[MP4_STSZ_DATA].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STSZ_DATA].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSZ_DATA].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STSZ_DATA].buffer, meta_reader, esize, sizeof(mp4_stsz_atom)); |
| |
| } else { |
| atom_size = atom_header_size + atom_data_size; |
| trak->size += atom_size; |
| mp4_reader_set_32value(trak->atoms[MP4_STSZ_ATOM].reader, 0, atom_size); |
| } |
| |
| mp4_meta_consume(atom_data_size + atom_header_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_stco_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int32_t entries; |
| int64_t esize, copied_size; |
| mp4_stco_atom stco; |
| Mp4Trak *trak; |
| |
| if (sizeof(mp4_stco_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, &stco, sizeof(mp4_stco_atom)); |
| entries = copied_size > 0 ? mp4_get_32value(stco.entries) : 0; |
| esize = entries * sizeof(int32_t); |
| |
| if (sizeof(mp4_stco_atom) - 8 + esize > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->chunks = entries; |
| |
| trak->atoms[MP4_STCO_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STCO_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STCO_ATOM].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STCO_ATOM].buffer, meta_reader, sizeof(mp4_stco_atom), 0); |
| |
| trak->atoms[MP4_STCO_DATA].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_STCO_DATA].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STCO_DATA].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_STCO_DATA].buffer, meta_reader, esize, sizeof(mp4_stco_atom)); |
| |
| mp4_meta_consume(atom_data_size + atom_header_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_co64_atom(int64_t atom_header_size, int64_t atom_data_size) |
| { |
| int32_t entries; |
| int64_t esize, copied_size; |
| mp4_co64_atom co64; |
| Mp4Trak *trak; |
| |
| if (sizeof(mp4_co64_atom) - 8 > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| copied_size = IOBufferReaderCopy(meta_reader, &co64, sizeof(mp4_co64_atom)); |
| entries = copied_size > 0 ? mp4_get_32value(co64.entries) : 0; |
| esize = entries * sizeof(int64_t); |
| |
| if (sizeof(mp4_co64_atom) - 8 + esize > static_cast<size_t>(atom_data_size)) { |
| return -1; |
| } |
| |
| trak = trak_vec[trak_num - 1].get(); |
| trak->chunks = entries; |
| |
| trak->atoms[MP4_CO64_ATOM].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_CO64_ATOM].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_CO64_ATOM].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_CO64_ATOM].buffer, meta_reader, sizeof(mp4_co64_atom), 0); |
| |
| trak->atoms[MP4_CO64_DATA].buffer = TSIOBufferCreate(); |
| trak->atoms[MP4_CO64_DATA].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_CO64_DATA].buffer); |
| TSIOBufferCopy(trak->atoms[MP4_CO64_DATA].buffer, meta_reader, esize, sizeof(mp4_co64_atom)); |
| |
| mp4_meta_consume(atom_data_size + atom_header_size); |
| |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_read_mdat_atom(int64_t /* atom_header_size ATS_UNUSED */, int64_t /* atom_data_size ATS_UNUSED */) |
| { |
| mdat_atom.buffer = TSIOBufferCreate(); |
| mdat_atom.reader = TSIOBufferReaderAlloc(mdat_atom.buffer); |
| |
| meta_complete = true; |
| return 1; |
| } |
| |
| int |
| Mp4Meta::mp4_update_stts_atom(Mp4Trak *trak) |
| { |
| uint32_t i, entries, count, duration, pass; |
| uint32_t start_sample, left; |
| uint32_t key_sample, old_sample; |
| uint64_t start_time, sum; |
| int64_t atom_size; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_STTS_DATA].buffer == nullptr) { |
| return -1; |
| } |
| |
| sum = 0; |
| |
| entries = trak->time_to_sample_entries; |
| start_time = this->start * trak->timescale / 1000; |
| if (this->rs > 0) { |
| start_time = static_cast<uint64_t>(this->rs * trak->timescale / 1000); |
| } |
| |
| start_sample = 0; |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STTS_DATA].reader); |
| |
| for (i = 0; i < entries; i++) { |
| duration = mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, duration)); |
| count = mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, count)); |
| |
| if (start_time < static_cast<uint64_t>(count) * duration) { |
| pass = static_cast<uint32_t>(start_time / duration); |
| start_sample += pass; |
| |
| goto found; |
| } |
| |
| start_sample += count; |
| start_time -= static_cast<uint64_t>(count) * duration; |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_stts_entry)); |
| } |
| |
| found: |
| |
| TSIOBufferReaderFree(readerp); |
| |
| old_sample = start_sample; |
| key_sample = this->mp4_find_key_sample(start_sample, trak); // find the last key frame before start_sample |
| |
| if (old_sample != key_sample) { |
| start_sample = key_sample - 1; |
| } |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STTS_DATA].reader); |
| |
| trak->start_sample = start_sample; |
| |
| for (i = 0; i < entries; i++) { |
| duration = mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, duration)); |
| count = mp4_reader_get_32value(readerp, offsetof(mp4_stts_entry, count)); |
| |
| if (start_sample < count) { |
| count -= start_sample; |
| mp4_reader_set_32value(readerp, offsetof(mp4_stts_entry, count), count); |
| |
| sum += static_cast<uint64_t>(start_sample) * duration; |
| break; |
| } |
| |
| start_sample -= count; |
| sum += static_cast<uint64_t>(count) * duration; |
| |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_stts_entry)); |
| } |
| |
| if (this->rs == 0) { |
| this->rs = (static_cast<double>(sum) / trak->duration) * (static_cast<double>(trak->duration) / trak->timescale) * 1000; |
| } |
| |
| left = entries - i; |
| |
| atom_size = sizeof(mp4_stts_atom) + left * sizeof(mp4_stts_entry); |
| trak->size += atom_size; |
| |
| mp4_reader_set_32value(trak->atoms[MP4_STTS_ATOM].reader, offsetof(mp4_stts_atom, size), atom_size); |
| mp4_reader_set_32value(trak->atoms[MP4_STTS_ATOM].reader, offsetof(mp4_stts_atom, entries), left); |
| |
| TSIOBufferReaderConsume(trak->atoms[MP4_STTS_DATA].reader, i * sizeof(mp4_stts_entry)); |
| TSIOBufferReaderFree(readerp); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_stss_atom(Mp4Trak *trak) |
| { |
| int64_t atom_size; |
| uint32_t i, j, entries, sample, start_sample, left; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_STSS_DATA].buffer == nullptr) { |
| return 0; |
| } |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STSS_DATA].reader); |
| |
| start_sample = trak->start_sample + 1; |
| entries = trak->sync_samples_entries; |
| |
| for (i = 0; i < entries; i++) { |
| sample = mp4_reader_get_32value(readerp, 0); |
| |
| if (sample >= start_sample) { |
| goto found; |
| } |
| |
| TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); |
| } |
| |
| TSIOBufferReaderFree(readerp); |
| return -1; |
| |
| found: |
| |
| left = entries - i; |
| |
| start_sample = trak->start_sample; |
| for (j = 0; j < left; j++) { |
| sample = mp4_reader_get_32value(readerp, 0); |
| sample -= start_sample; |
| mp4_reader_set_32value(readerp, 0, sample); |
| TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); |
| } |
| |
| atom_size = sizeof(mp4_stss_atom) + left * sizeof(uint32_t); |
| trak->size += atom_size; |
| |
| mp4_reader_set_32value(trak->atoms[MP4_STSS_ATOM].reader, offsetof(mp4_stss_atom, size), atom_size); |
| |
| mp4_reader_set_32value(trak->atoms[MP4_STSS_ATOM].reader, offsetof(mp4_stss_atom, entries), left); |
| |
| TSIOBufferReaderConsume(trak->atoms[MP4_STSS_DATA].reader, i * sizeof(uint32_t)); |
| TSIOBufferReaderFree(readerp); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_ctts_atom(Mp4Trak *trak) |
| { |
| int64_t atom_size; |
| uint32_t i, entries, start_sample, left; |
| uint32_t count; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_CTTS_DATA].buffer == nullptr) { |
| return 0; |
| } |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_CTTS_DATA].reader); |
| |
| start_sample = trak->start_sample + 1; |
| entries = trak->composition_offset_entries; |
| |
| for (i = 0; i < entries; i++) { |
| count = mp4_reader_get_32value(readerp, offsetof(mp4_ctts_entry, count)); |
| |
| if (start_sample <= count) { |
| count -= (start_sample - 1); |
| mp4_reader_set_32value(readerp, offsetof(mp4_ctts_entry, count), count); |
| goto found; |
| } |
| |
| start_sample -= count; |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_ctts_entry)); |
| } |
| |
| if (trak->atoms[MP4_CTTS_ATOM].reader) { |
| TSIOBufferReaderFree(trak->atoms[MP4_CTTS_ATOM].reader); |
| TSIOBufferDestroy(trak->atoms[MP4_CTTS_ATOM].buffer); |
| |
| trak->atoms[MP4_CTTS_ATOM].buffer = nullptr; |
| trak->atoms[MP4_CTTS_ATOM].reader = nullptr; |
| } |
| |
| TSIOBufferReaderFree(trak->atoms[MP4_CTTS_DATA].reader); |
| TSIOBufferDestroy(trak->atoms[MP4_CTTS_DATA].buffer); |
| |
| trak->atoms[MP4_CTTS_DATA].reader = nullptr; |
| trak->atoms[MP4_CTTS_DATA].buffer = nullptr; |
| |
| TSIOBufferReaderFree(readerp); |
| return 0; |
| |
| found: |
| |
| left = entries - i; |
| atom_size = sizeof(mp4_ctts_atom) + left * sizeof(mp4_ctts_entry); |
| trak->size += atom_size; |
| |
| mp4_reader_set_32value(trak->atoms[MP4_CTTS_ATOM].reader, offsetof(mp4_ctts_atom, size), atom_size); |
| mp4_reader_set_32value(trak->atoms[MP4_CTTS_ATOM].reader, offsetof(mp4_ctts_atom, entries), left); |
| |
| TSIOBufferReaderConsume(trak->atoms[MP4_CTTS_DATA].reader, i * sizeof(mp4_ctts_entry)); |
| TSIOBufferReaderFree(readerp); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_stsc_atom(Mp4Trak *trak) |
| { |
| int64_t atom_size; |
| uint32_t i, entries, samples, start_sample; |
| uint32_t chunk, next_chunk, n, id, j; |
| mp4_stsc_entry *first; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_STSC_DATA].buffer == nullptr) { |
| return -1; |
| } |
| |
| if (trak->sample_to_chunk_entries == 0) { |
| return -1; |
| } |
| |
| start_sample = trak->start_sample; |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STSC_DATA].reader); |
| |
| chunk = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, chunk)); |
| samples = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, samples)); |
| id = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, id)); |
| |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); |
| |
| for (i = 1; i < trak->sample_to_chunk_entries; i++) { |
| next_chunk = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, chunk)); |
| |
| n = (next_chunk - chunk) * samples; |
| |
| if (start_sample <= n) { |
| goto found; |
| } |
| |
| start_sample -= n; |
| |
| chunk = next_chunk; |
| samples = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, samples)); |
| id = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, id)); |
| |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); |
| } |
| |
| next_chunk = trak->chunks; |
| |
| n = (next_chunk - chunk) * samples; |
| if (start_sample > n) { |
| TSIOBufferReaderFree(readerp); |
| return -1; |
| } |
| |
| found: |
| |
| TSIOBufferReaderFree(readerp); |
| |
| entries = trak->sample_to_chunk_entries - i + 1; |
| if (samples == 0) { |
| return -1; |
| } |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STSC_DATA].reader); |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry) * (i - 1)); |
| |
| trak->start_chunk = chunk - 1; |
| trak->start_chunk += start_sample / samples; |
| trak->chunk_samples = start_sample % samples; |
| |
| atom_size = sizeof(mp4_stsc_atom) + entries * sizeof(mp4_stsc_entry); |
| |
| mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, chunk), 1); |
| |
| if (trak->chunk_samples && next_chunk - trak->start_chunk == 2) { |
| mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, samples), samples - trak->chunk_samples); |
| |
| } else if (trak->chunk_samples) { |
| first = &trak->stsc_chunk_entry; |
| mp4_set_32value(first->chunk, 1); |
| mp4_set_32value(first->samples, samples - trak->chunk_samples); |
| mp4_set_32value(first->id, id); |
| |
| trak->atoms[MP4_STSC_CHUNK].buffer = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_128); |
| trak->atoms[MP4_STSC_CHUNK].reader = TSIOBufferReaderAlloc(trak->atoms[MP4_STSC_CHUNK].buffer); |
| TSIOBufferWrite(trak->atoms[MP4_STSC_CHUNK].buffer, first, sizeof(mp4_stsc_entry)); |
| |
| mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, chunk), 2); |
| |
| entries++; |
| atom_size += sizeof(mp4_stsc_entry); |
| } |
| |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); |
| |
| for (j = i; j < trak->sample_to_chunk_entries; j++) { |
| chunk = mp4_reader_get_32value(readerp, offsetof(mp4_stsc_entry, chunk)); |
| chunk -= trak->start_chunk; |
| mp4_reader_set_32value(readerp, offsetof(mp4_stsc_entry, chunk), chunk); |
| TSIOBufferReaderConsume(readerp, sizeof(mp4_stsc_entry)); |
| } |
| |
| trak->size += atom_size; |
| |
| mp4_reader_set_32value(trak->atoms[MP4_STSC_ATOM].reader, offsetof(mp4_stsc_atom, size), atom_size); |
| mp4_reader_set_32value(trak->atoms[MP4_STSC_ATOM].reader, offsetof(mp4_stsc_atom, entries), entries); |
| |
| TSIOBufferReaderConsume(trak->atoms[MP4_STSC_DATA].reader, (i - 1) * sizeof(mp4_stsc_entry)); |
| TSIOBufferReaderFree(readerp); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_stsz_atom(Mp4Trak *trak) |
| { |
| uint32_t i; |
| int64_t atom_size, avail; |
| uint32_t pass; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_STSZ_DATA].buffer == nullptr) { |
| return 0; |
| } |
| |
| if (trak->start_sample > trak->sample_sizes_entries) { |
| return -1; |
| } |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STSZ_DATA].reader); |
| avail = TSIOBufferReaderAvail(readerp); |
| |
| pass = trak->start_sample * sizeof(uint32_t); |
| |
| TSIOBufferReaderConsume(readerp, pass - sizeof(uint32_t) * (trak->chunk_samples)); |
| |
| for (i = 0; i < trak->chunk_samples; i++) { |
| trak->chunk_samples_size += mp4_reader_get_32value(readerp, 0); |
| TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); |
| } |
| |
| atom_size = sizeof(mp4_stsz_atom) + avail - pass; |
| trak->size += atom_size; |
| |
| mp4_reader_set_32value(trak->atoms[MP4_STSZ_ATOM].reader, offsetof(mp4_stsz_atom, size), atom_size); |
| mp4_reader_set_32value(trak->atoms[MP4_STSZ_ATOM].reader, offsetof(mp4_stsz_atom, entries), |
| trak->sample_sizes_entries - trak->start_sample); |
| |
| TSIOBufferReaderConsume(trak->atoms[MP4_STSZ_DATA].reader, pass); |
| TSIOBufferReaderFree(readerp); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_co64_atom(Mp4Trak *trak) |
| { |
| int64_t atom_size, avail, pass; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_CO64_DATA].buffer == nullptr) { |
| return -1; |
| } |
| |
| if (trak->start_chunk > trak->chunks) { |
| return -1; |
| } |
| |
| readerp = trak->atoms[MP4_CO64_DATA].reader; |
| avail = TSIOBufferReaderAvail(readerp); |
| |
| pass = trak->start_chunk * sizeof(uint64_t); |
| atom_size = sizeof(mp4_co64_atom) + avail - pass; |
| trak->size += atom_size; |
| |
| TSIOBufferReaderConsume(readerp, pass); |
| trak->start_offset = mp4_reader_get_64value(readerp, 0); |
| trak->start_offset += trak->chunk_samples_size; |
| mp4_reader_set_64value(readerp, 0, trak->start_offset); |
| |
| mp4_reader_set_32value(trak->atoms[MP4_CO64_ATOM].reader, offsetof(mp4_co64_atom, size), atom_size); |
| mp4_reader_set_32value(trak->atoms[MP4_CO64_ATOM].reader, offsetof(mp4_co64_atom, entries), trak->chunks - trak->start_chunk); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_stco_atom(Mp4Trak *trak) |
| { |
| int64_t atom_size, avail; |
| uint32_t pass; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_STCO_DATA].buffer == nullptr) { |
| return -1; |
| } |
| |
| if (trak->start_chunk > trak->chunks) { |
| return -1; |
| } |
| |
| readerp = trak->atoms[MP4_STCO_DATA].reader; |
| avail = TSIOBufferReaderAvail(readerp); |
| |
| pass = trak->start_chunk * sizeof(uint32_t); |
| atom_size = sizeof(mp4_stco_atom) + avail - pass; |
| trak->size += atom_size; |
| |
| TSIOBufferReaderConsume(readerp, pass); |
| |
| trak->start_offset = mp4_reader_get_32value(readerp, 0); |
| trak->start_offset += trak->chunk_samples_size; |
| mp4_reader_set_32value(readerp, 0, trak->start_offset); |
| |
| mp4_reader_set_32value(trak->atoms[MP4_STCO_ATOM].reader, offsetof(mp4_stco_atom, size), atom_size); |
| mp4_reader_set_32value(trak->atoms[MP4_STCO_ATOM].reader, offsetof(mp4_stco_atom, entries), trak->chunks - trak->start_chunk); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_stbl_atom(Mp4Trak *trak) |
| { |
| trak->size += sizeof(mp4_atom_header); |
| mp4_reader_set_32value(trak->atoms[MP4_STBL_ATOM].reader, 0, trak->size); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_minf_atom(Mp4Trak *trak) |
| { |
| trak->size += sizeof(mp4_atom_header) + trak->vmhd_size + trak->smhd_size + trak->dinf_size; |
| |
| mp4_reader_set_32value(trak->atoms[MP4_MINF_ATOM].reader, 0, trak->size); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_mdia_atom(Mp4Trak *trak) |
| { |
| trak->size += sizeof(mp4_atom_header); |
| mp4_reader_set_32value(trak->atoms[MP4_MDIA_ATOM].reader, 0, trak->size); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_update_trak_atom(Mp4Trak *trak) |
| { |
| trak->size += sizeof(mp4_atom_header); |
| mp4_reader_set_32value(trak->atoms[MP4_TRAK_ATOM].reader, 0, trak->size); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_adjust_co64_atom(Mp4Trak *trak, off_t adjustment) |
| { |
| int64_t pos, avail, offset; |
| TSIOBufferReader readerp; |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_CO64_DATA].reader); |
| avail = TSIOBufferReaderAvail(readerp); |
| |
| for (pos = 0; pos < avail; pos += sizeof(uint64_t)) { |
| offset = mp4_reader_get_64value(readerp, 0); |
| offset += adjustment; |
| mp4_reader_set_64value(readerp, 0, offset); |
| TSIOBufferReaderConsume(readerp, sizeof(uint64_t)); |
| } |
| |
| TSIOBufferReaderFree(readerp); |
| |
| return 0; |
| } |
| |
| int |
| Mp4Meta::mp4_adjust_stco_atom(Mp4Trak *trak, int32_t adjustment) |
| { |
| int64_t pos, avail, offset; |
| TSIOBufferReader readerp; |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STCO_DATA].reader); |
| avail = TSIOBufferReaderAvail(readerp); |
| |
| for (pos = 0; pos < avail; pos += sizeof(uint32_t)) { |
| offset = mp4_reader_get_32value(readerp, 0); |
| offset += adjustment; |
| mp4_reader_set_32value(readerp, 0, offset); |
| TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); |
| } |
| |
| TSIOBufferReaderFree(readerp); |
| |
| return 0; |
| } |
| |
| int64_t |
| Mp4Meta::mp4_update_mdat_atom(int64_t start_offset) |
| { |
| int64_t atom_data_size; |
| int64_t atom_size; |
| int64_t atom_header_size; |
| u_char *atom_header; |
| |
| atom_data_size = this->cl - start_offset; |
| this->start_pos = start_offset; |
| |
| atom_header = mdat_atom_header; |
| |
| if (atom_data_size > 0xffffffff) { |
| atom_size = 1; |
| atom_header_size = sizeof(mp4_atom_header64); |
| mp4_set_64value(atom_header + sizeof(mp4_atom_header), sizeof(mp4_atom_header64) + atom_data_size); |
| |
| } else { |
| atom_size = sizeof(mp4_atom_header) + atom_data_size; |
| atom_header_size = sizeof(mp4_atom_header); |
| } |
| |
| this->content_length += atom_header_size + atom_data_size; |
| |
| mp4_set_32value(atom_header, atom_size); |
| mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't'); |
| |
| mdat_atom.buffer = TSIOBufferSizedCreate(TS_IOBUFFER_SIZE_INDEX_128); |
| mdat_atom.reader = TSIOBufferReaderAlloc(mdat_atom.buffer); |
| |
| TSIOBufferWrite(mdat_atom.buffer, atom_header, atom_header_size); |
| |
| return atom_header_size; |
| } |
| |
| uint32_t |
| Mp4Meta::mp4_find_key_sample(uint32_t start_sample, Mp4Trak *trak) |
| { |
| uint32_t i; |
| uint32_t sample, prev_sample, entries; |
| TSIOBufferReader readerp; |
| |
| if (trak->atoms[MP4_STSS_DATA].buffer == nullptr) { |
| return start_sample; |
| } |
| |
| prev_sample = 1; |
| entries = trak->sync_samples_entries; |
| |
| readerp = TSIOBufferReaderClone(trak->atoms[MP4_STSS_DATA].reader); |
| |
| for (i = 0; i < entries; i++) { |
| sample = mp4_reader_get_32value(readerp, 0); |
| |
| if (sample > start_sample) { |
| goto found; |
| } |
| |
| prev_sample = sample; |
| TSIOBufferReaderConsume(readerp, sizeof(uint32_t)); |
| } |
| |
| found: |
| |
| TSIOBufferReaderFree(readerp); |
| return prev_sample; |
| } |
| |
| void |
| Mp4Meta::mp4_update_mvhd_duration() |
| { |
| int64_t need; |
| uint64_t duration, cut; |
| mp4_mvhd_atom *mvhd; |
| mp4_mvhd64_atom mvhd64; |
| |
| need = TSIOBufferReaderAvail(mvhd_atom.reader); |
| |
| if (need > static_cast<int64_t>(sizeof(mp4_mvhd64_atom))) { |
| need = sizeof(mp4_mvhd64_atom); |
| } |
| |
| memset(&mvhd64, 0, sizeof(mvhd64)); |
| IOBufferReaderCopy(mvhd_atom.reader, &mvhd64, need); |
| mvhd = reinterpret_cast<mp4_mvhd_atom *>(&mvhd64); |
| |
| if (this->rs > 0) { |
| cut = static_cast<uint64_t>(this->rs * this->timescale / 1000); |
| |
| } else { |
| cut = this->start * this->timescale / 1000; |
| } |
| |
| if (mvhd->version[0] == 0) { |
| duration = mp4_get_32value(mvhd->duration); |
| duration -= cut; |
| mp4_reader_set_32value(mvhd_atom.reader, offsetof(mp4_mvhd_atom, duration), duration); |
| |
| } else { // 64-bit duration |
| duration = mp4_get_64value(mvhd64.duration); |
| duration -= cut; |
| mp4_reader_set_64value(mvhd_atom.reader, offsetof(mp4_mvhd64_atom, duration), duration); |
| } |
| } |
| |
| void |
| Mp4Meta::mp4_update_tkhd_duration(Mp4Trak *trak) |
| { |
| int64_t need, cut; |
| mp4_tkhd_atom *tkhd_atom; |
| mp4_tkhd64_atom tkhd64_atom; |
| int64_t duration; |
| |
| need = TSIOBufferReaderAvail(trak->atoms[MP4_TKHD_ATOM].reader); |
| |
| if (need > static_cast<int64_t>(sizeof(mp4_tkhd64_atom))) { |
| need = sizeof(mp4_tkhd64_atom); |
| } |
| |
| memset(&tkhd64_atom, 0, sizeof(tkhd64_atom)); |
| IOBufferReaderCopy(trak->atoms[MP4_TKHD_ATOM].reader, &tkhd64_atom, need); |
| tkhd_atom = reinterpret_cast<mp4_tkhd_atom *>(&tkhd64_atom); |
| |
| if (this->rs > 0) { |
| cut = static_cast<uint64_t>(this->rs * this->timescale / 1000); |
| |
| } else { |
| cut = this->start * this->timescale / 1000; |
| } |
| |
| if (tkhd_atom->version[0] == 0) { |
| duration = mp4_get_32value(tkhd_atom->duration); |
| duration -= cut; |
| mp4_reader_set_32value(trak->atoms[MP4_TKHD_ATOM].reader, offsetof(mp4_tkhd_atom, duration), duration); |
| |
| } else { |
| duration = mp4_get_64value(tkhd64_atom.duration); |
| duration -= cut; |
| mp4_reader_set_64value(trak->atoms[MP4_TKHD_ATOM].reader, offsetof(mp4_tkhd64_atom, duration), duration); |
| } |
| } |
| |
| void |
| Mp4Meta::mp4_update_mdhd_duration(Mp4Trak *trak) |
| { |
| int64_t duration, need, cut; |
| mp4_mdhd_atom *mdhd; |
| mp4_mdhd64_atom mdhd64; |
| |
| memset(&mdhd64, 0, sizeof(mp4_mdhd64_atom)); |
| |
| need = TSIOBufferReaderAvail(trak->atoms[MP4_MDHD_ATOM].reader); |
| |
| if (need > static_cast<int64_t>(sizeof(mp4_mdhd64_atom))) { |
| need = sizeof(mp4_mdhd64_atom); |
| } |
| |
| IOBufferReaderCopy(trak->atoms[MP4_MDHD_ATOM].reader, &mdhd64, need); |
| mdhd = reinterpret_cast<mp4_mdhd_atom *>(&mdhd64); |
| |
| if (this->rs > 0) { |
| cut = static_cast<uint64_t>(this->rs * trak->timescale / 1000); |
| } else { |
| cut = this->start * trak->timescale / 1000; |
| } |
| |
| if (mdhd->version[0] == 0) { |
| duration = mp4_get_32value(mdhd->duration); |
| duration -= cut; |
| mp4_reader_set_32value(trak->atoms[MP4_MDHD_ATOM].reader, offsetof(mp4_mdhd_atom, duration), duration); |
| } else { |
| duration = mp4_get_64value(mdhd64.duration); |
| duration -= cut; |
| mp4_reader_set_64value(trak->atoms[MP4_MDHD_ATOM].reader, offsetof(mp4_mdhd64_atom, duration), duration); |
| } |
| } |
| |
| static void |
| mp4_reader_set_32value(TSIOBufferReader readerp, int64_t offset, uint32_t n) |
| { |
| int pos; |
| int64_t avail, left; |
| TSIOBufferBlock blk; |
| const char *start; |
| u_char *ptr; |
| |
| pos = 0; |
| blk = TSIOBufferReaderStart(readerp); |
| |
| while (blk) { |
| start = TSIOBufferBlockReadStart(blk, readerp, &avail); |
| |
| if (avail <= offset) { |
| offset -= avail; |
| |
| } else { |
| left = avail - offset; |
| ptr = reinterpret_cast<u_char *>(const_cast<char *>(start) + offset); |
| |
| while (pos < 4 && left > 0) { |
| *ptr++ = static_cast<u_char>((n) >> ((3 - pos) * 8)); |
| pos++; |
| left--; |
| } |
| |
| if (pos >= 4) { |
| return; |
| } |
| |
| offset = 0; |
| } |
| |
| blk = TSIOBufferBlockNext(blk); |
| } |
| } |
| |
| static void |
| mp4_reader_set_64value(TSIOBufferReader readerp, int64_t offset, uint64_t n) |
| { |
| int pos; |
| int64_t avail, left; |
| TSIOBufferBlock blk; |
| const char *start; |
| u_char *ptr; |
| |
| pos = 0; |
| blk = TSIOBufferReaderStart(readerp); |
| |
| while (blk) { |
| start = TSIOBufferBlockReadStart(blk, readerp, &avail); |
| |
| if (avail <= offset) { |
| offset -= avail; |
| |
| } else { |
| left = avail - offset; |
| ptr = reinterpret_cast<u_char *>(const_cast<char *>(start) + offset); |
| |
| while (pos < 8 && left > 0) { |
| *ptr++ = static_cast<u_char>((n) >> ((7 - pos) * 8)); |
| pos++; |
| left--; |
| } |
| |
| if (pos >= 4) { |
| return; |
| } |
| |
| offset = 0; |
| } |
| |
| blk = TSIOBufferBlockNext(blk); |
| } |
| } |
| |
| static uint32_t |
| mp4_reader_get_32value(TSIOBufferReader readerp, int64_t offset) |
| { |
| int pos; |
| int64_t avail, left; |
| TSIOBufferBlock blk; |
| const char *start; |
| const u_char *ptr; |
| u_char res[4]; |
| |
| pos = 0; |
| blk = TSIOBufferReaderStart(readerp); |
| |
| while (blk) { |
| start = TSIOBufferBlockReadStart(blk, readerp, &avail); |
| |
| if (avail <= offset) { |
| offset -= avail; |
| |
| } else { |
| left = avail - offset; |
| ptr = (u_char *)(start + offset); |
| |
| while (pos < 4 && left > 0) { |
| res[3 - pos] = *ptr++; |
| pos++; |
| left--; |
| } |
| |
| if (pos >= 4) { |
| return *reinterpret_cast<uint32_t *>(res); |
| } |
| |
| offset = 0; |
| } |
| |
| blk = TSIOBufferBlockNext(blk); |
| } |
| |
| return -1; |
| } |
| |
| static uint64_t |
| mp4_reader_get_64value(TSIOBufferReader readerp, int64_t offset) |
| { |
| int pos; |
| int64_t avail, left; |
| TSIOBufferBlock blk; |
| const char *start; |
| u_char *ptr; |
| u_char res[8]; |
| |
| pos = 0; |
| blk = TSIOBufferReaderStart(readerp); |
| |
| while (blk) { |
| start = TSIOBufferBlockReadStart(blk, readerp, &avail); |
| |
| if (avail <= offset) { |
| offset -= avail; |
| |
| } else { |
| left = avail - offset; |
| ptr = (u_char *)(start + offset); |
| |
| while (pos < 8 && left > 0) { |
| res[7 - pos] = *ptr++; |
| pos++; |
| left--; |
| } |
| |
| if (pos >= 8) { |
| return *reinterpret_cast<uint64_t *>(res); |
| } |
| |
| offset = 0; |
| } |
| |
| blk = TSIOBufferBlockNext(blk); |
| } |
| |
| return -1; |
| } |
| |
| static int64_t |
| IOBufferReaderCopy(TSIOBufferReader readerp, void *buf, int64_t length) |
| { |
| int64_t avail, need, n; |
| const char *start; |
| TSIOBufferBlock blk; |
| |
| n = 0; |
| blk = TSIOBufferReaderStart(readerp); |
| |
| while (blk) { |
| start = TSIOBufferBlockReadStart(blk, readerp, &avail); |
| need = length < avail ? length : avail; |
| |
| if (need > 0) { |
| memcpy(static_cast<char *>(buf) + n, start, need); |
| length -= need; |
| n += need; |
| } |
| |
| if (length == 0) { |
| break; |
| } |
| |
| blk = TSIOBufferBlockNext(blk); |
| } |
| |
| return n; |
| } |