| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2023 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / common tools sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/bitstream.h> | |
| /*the default size for new streams allocation...*/ | |
| #define BS_MEM_BLOCK_ALLOC_SIZE 512 | |
| /*private types*/ | |
| enum | |
| { | |
| GF_BITSTREAM_FILE_READ = GF_BITSTREAM_WRITE_DYN + 1, | |
| GF_BITSTREAM_FILE_WRITE, | |
| }; | |
| struct __tag_bitstream | |
| { | |
| /*original stream data*/ | |
| FILE *stream; | |
| /*or original data*/ | |
| char *original; | |
| /*the size of our buffer in bytes*/ | |
| u64 size; | |
| /*current position in BYTES*/ | |
| u64 position; | |
| /*the byte readen/written*/ | |
| u32 current; | |
| /*the number of bits in the current byte*/ | |
| u32 nbBits; | |
| /*the bitstream mode*/ | |
| u32 bsmode; | |
| void (*EndOfStream)(void *par); | |
| void *par; | |
| char *cache_write; | |
| u32 cache_write_size, buffer_written; | |
| Bool remove_emul_prevention_byte; | |
| u32 nb_zeros, nb_removed; | |
| GF_Err (*on_block_out)(void *cbk, u8 *data, u32 block_size); | |
| void *usr_data; | |
| u64 bytes_out; | |
| u32 prevent_dispatch; | |
| u64 cookie; | |
| u8 *cache_read; | |
| u32 cache_read_size, cache_read_pos, cache_read_alloc; | |
| void (*on_log)(void *udta, const char *field_name, u32 nb_bits, u64 field_val, s32 idx1, s32 idx2, s32 idx3); | |
| void *log_udta; | |
| u32 total_bits_read; | |
| u32 overflow_state; | |
| u64 o_size; | |
| #ifdef GPAC_HAS_FD | |
| int fd; | |
| #endif | |
| }; | |
| GF_EXPORT | |
| GF_Err gf_bs_reassign_buffer(GF_BitStream *bs, const u8 *buffer, u64 BufferSize) | |
| { | |
| if (!bs) return GF_BAD_PARAM; | |
| bs->total_bits_read = 0; | |
| if (bs->bsmode == GF_BITSTREAM_READ) { | |
| bs->original = (char*)buffer; | |
| bs->size = BufferSize; | |
| bs->position = 0; | |
| bs->current = 0; | |
| bs->nbBits = 8; | |
| bs->current = 0; | |
| bs->nb_zeros = bs->nb_removed = 0; | |
| return GF_OK; | |
| } | |
| if (bs->bsmode==GF_BITSTREAM_WRITE) { | |
| if (!buffer || !BufferSize) return GF_BAD_PARAM; | |
| bs->original = (char*)buffer; | |
| bs->size = BufferSize; | |
| bs->position = 0; | |
| bs->current = 0; | |
| bs->nbBits = 0; | |
| bs->current = 0; | |
| return GF_OK; | |
| } | |
| if (bs->bsmode!=GF_BITSTREAM_WRITE_DYN) return GF_BAD_PARAM; | |
| if (bs->original) return GF_BAD_PARAM; | |
| bs->position = 0; | |
| bs->current = 0; | |
| bs->nbBits = 0; | |
| bs->current = 0; | |
| bs->size = BufferSize ? BufferSize : BS_MEM_BLOCK_ALLOC_SIZE; | |
| if (buffer) { | |
| bs->original = (char *) buffer; | |
| } else { | |
| bs->original = (char *) gf_malloc(sizeof(char) * ((u32) bs->size)); | |
| if (! bs->original) { | |
| return GF_OUT_OF_MEM; | |
| } | |
| } | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| GF_BitStream *gf_bs_new(const u8 *buffer, u64 BufferSize, u32 mode) | |
| { | |
| GF_BitStream *tmp = (GF_BitStream *)gf_malloc(sizeof(GF_BitStream)); | |
| if (!tmp) return NULL; | |
| memset(tmp, 0, sizeof(GF_BitStream)); | |
| #ifdef GPAC_HAS_FD | |
| tmp->fd = -1; | |
| #endif | |
| tmp->original = (char*)buffer; | |
| tmp->size = BufferSize; | |
| tmp->bsmode = mode; | |
| switch (tmp->bsmode) { | |
| case GF_BITSTREAM_READ: | |
| tmp->nbBits = 8; | |
| tmp->current = 0; | |
| break; | |
| case GF_BITSTREAM_WRITE: | |
| case GF_BITSTREAM_WRITE_DYN: | |
| tmp->nbBits = 0; | |
| if (! buffer) { | |
| /*if BufferSize is specified, use it. This is typically used when AvgSize of | |
| some buffers is known, but some exceed it.*/ | |
| if (BufferSize) { | |
| if (BufferSize > GF_UINT_MAX) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| tmp->size = BufferSize; | |
| } else { | |
| tmp->size = BS_MEM_BLOCK_ALLOC_SIZE; | |
| } | |
| tmp->original = (char *) gf_malloc(sizeof(char) * ((u32) tmp->size)); | |
| if (! tmp->original) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| tmp->bsmode = GF_BITSTREAM_WRITE_DYN; | |
| } else { | |
| tmp->original = (char*)buffer; | |
| tmp->size = BufferSize; | |
| } | |
| break; | |
| default: | |
| /*the stream constructor is not the same...*/ | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| return tmp; | |
| } | |
| GF_EXPORT | |
| GF_BitStream *gf_bs_from_file(FILE *f, u32 mode) | |
| { | |
| GF_BitStream *tmp; | |
| if (!f) return NULL; | |
| tmp = (GF_BitStream *)gf_malloc(sizeof(GF_BitStream)); | |
| if (!tmp) return NULL; | |
| memset(tmp, 0, sizeof(GF_BitStream)); | |
| /*switch to internal mode*/ | |
| mode = (mode==GF_BITSTREAM_READ) ? GF_BITSTREAM_FILE_READ : GF_BITSTREAM_FILE_WRITE; | |
| tmp->bsmode = mode; | |
| tmp->current = 0; | |
| tmp->nbBits = (mode == GF_BITSTREAM_FILE_READ) ? 8 : 0; | |
| tmp->original = NULL; | |
| tmp->position = 0; | |
| tmp->stream = f; | |
| #ifdef GPAC_HAS_FD | |
| tmp->fd = -1; | |
| #endif | |
| /*get the size of this file (for read streams)*/ | |
| tmp->position = gf_ftell(f); | |
| tmp->size = gf_fsize(f); | |
| gf_fseek(f, tmp->position, SEEK_SET); | |
| if (mode==GF_BITSTREAM_FILE_READ) { | |
| tmp->cache_read_alloc = gf_opts_get_int("core", "bs-cache-size"); | |
| if (tmp->cache_read_alloc) { | |
| tmp->cache_read_pos = tmp->cache_read_size = tmp->cache_read_alloc; | |
| tmp->cache_read = gf_malloc(tmp->cache_read_alloc); | |
| if (!tmp->cache_read) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| } | |
| } | |
| if (mode == GF_BITSTREAM_FILE_WRITE) { | |
| tmp->cache_write_size = gf_opts_get_int("core", "bs-cache-size"); | |
| if (tmp->cache_write_size) { | |
| tmp->cache_write = (char*)gf_malloc(tmp->cache_write_size); | |
| if (!tmp->cache_write) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| tmp->buffer_written = 0; | |
| } | |
| } | |
| return tmp; | |
| } | |
| #ifdef GPAC_HAS_FD | |
| GF_EXPORT | |
| GF_BitStream *gf_bs_from_fd(int fd, u32 mode) | |
| { | |
| GF_BitStream *tmp; | |
| if (fd<0) return NULL; | |
| tmp = (GF_BitStream *)gf_malloc(sizeof(GF_BitStream)); | |
| if (!tmp) return NULL; | |
| memset(tmp, 0, sizeof(GF_BitStream)); | |
| /*switch to internal mode*/ | |
| mode = (mode==GF_BITSTREAM_READ) ? GF_BITSTREAM_FILE_READ : GF_BITSTREAM_FILE_WRITE; | |
| tmp->bsmode = mode; | |
| tmp->current = 0; | |
| tmp->nbBits = (mode == GF_BITSTREAM_FILE_READ) ? 8 : 0; | |
| tmp->original = NULL; | |
| tmp->position = 0; | |
| tmp->fd = fd; | |
| /*get the size of this file (for read streams)*/ | |
| tmp->position = lseek_64(fd, 0, SEEK_CUR); | |
| tmp->size = gf_fd_fsize(fd); | |
| if (mode == GF_BITSTREAM_FILE_READ) { | |
| tmp->cache_read_alloc = gf_opts_get_int("core", "bs-cache-size"); | |
| if (tmp->cache_read_alloc) { | |
| tmp->cache_read_pos = tmp->cache_read_size = tmp->cache_read_alloc; | |
| tmp->cache_read = gf_malloc(tmp->cache_read_alloc); | |
| if (!tmp->cache_read) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| } | |
| } | |
| if (mode == GF_BITSTREAM_FILE_WRITE) { | |
| tmp->cache_write_size = gf_opts_get_int("core", "bs-cache-size"); | |
| if (tmp->cache_write_size) { | |
| tmp->cache_write = (char*)gf_malloc(tmp->cache_write_size); | |
| if (!tmp->cache_write) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| tmp->buffer_written = 0; | |
| } | |
| } | |
| return tmp; | |
| } | |
| #endif | |
| GF_BitStream *gf_bs_new_cbk_buffer(GF_Err (*on_block_out)(void *cbk, u8 *data, u32 block_size), void *usr_data, u8 *buffer, u32 buffer_size) | |
| { | |
| GF_BitStream *tmp; | |
| if (!on_block_out) return NULL; | |
| tmp = (GF_BitStream *)gf_malloc(sizeof(GF_BitStream)); | |
| if (!tmp) return NULL; | |
| memset(tmp, 0, sizeof(GF_BitStream)); | |
| if (buffer && buffer_size) { | |
| tmp->size = buffer_size; | |
| tmp->original = buffer; | |
| } else { | |
| tmp->size = buffer_size ? buffer_size : 10*BS_MEM_BLOCK_ALLOC_SIZE; | |
| tmp->original = (char *) gf_malloc(sizeof(char) * ((u32) tmp->size)); | |
| if (! tmp->original) { | |
| gf_free(tmp); | |
| return NULL; | |
| } | |
| } | |
| tmp->bsmode = GF_BITSTREAM_WRITE_DYN; | |
| tmp->on_block_out = on_block_out; | |
| tmp->usr_data = usr_data; | |
| return tmp; | |
| } | |
| GF_BitStream *gf_bs_new_cbk(GF_Err (*on_block_out)(void *cbk, u8 *data, u32 block_size), void *usr_data, u32 block_size) | |
| { | |
| return gf_bs_new_cbk_buffer(on_block_out, usr_data, NULL, block_size); | |
| } | |
| void gf_bs_prevent_dispatch(GF_BitStream *bs, Bool prevent_dispatch) | |
| { | |
| if (!bs) return; | |
| if (prevent_dispatch) { | |
| bs->prevent_dispatch ++; | |
| return; | |
| } | |
| if (!bs->prevent_dispatch) return; | |
| bs->prevent_dispatch --; | |
| if (bs->on_block_out && !bs->prevent_dispatch) { | |
| gf_assert(bs->position >= bs->bytes_out); | |
| if (bs->position > bs->bytes_out) { | |
| bs->on_block_out(bs->usr_data, bs->original, (u32) (bs->position - bs->bytes_out)); | |
| bs->bytes_out = bs->position; | |
| } | |
| } | |
| } | |
| static Bool bs_flush_write_cache(GF_BitStream *bs) | |
| { | |
| Bool res = GF_TRUE; | |
| if (bs->buffer_written) { | |
| u32 nb_write; | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| nb_write = (u32) write(bs->fd, bs->cache_write, bs->buffer_written); | |
| } else | |
| #endif | |
| { | |
| nb_write = (u32) gf_fwrite(bs->cache_write, bs->buffer_written, bs->stream); | |
| } | |
| if (nb_write != bs->buffer_written) | |
| res = GF_FALSE; | |
| //check we didn't rewind the bitstream | |
| if (bs->size == bs->position) | |
| bs->size += nb_write; | |
| bs->position += nb_write; | |
| bs->buffer_written = 0; | |
| } | |
| return res; | |
| } | |
| GF_EXPORT | |
| void gf_bs_del(GF_BitStream *bs) | |
| { | |
| if (!bs) return; | |
| if (bs->on_block_out && bs->position>bs->bytes_out) { | |
| bs->on_block_out(bs->usr_data, bs->original, (u32) (bs->position - bs->bytes_out) ); | |
| } | |
| /*if we are in dynamic mode (alloc done by the bitstream), free the buffer if still present*/ | |
| if ((bs->bsmode == GF_BITSTREAM_WRITE_DYN) && bs->original) gf_free(bs->original); | |
| if (bs->cache_write) { | |
| bs_flush_write_cache(bs); | |
| gf_free(bs->cache_write); | |
| } | |
| if (bs->cache_read) | |
| gf_free(bs->cache_read); | |
| gf_free(bs); | |
| } | |
| GF_EXPORT | |
| void gf_bs_enable_emulation_byte_removal(GF_BitStream *bs, Bool do_remove) | |
| { | |
| if (bs) { | |
| bs->remove_emul_prevention_byte = do_remove; | |
| bs->nb_zeros = bs->nb_removed = 0; | |
| } | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_get_emulation_byte_removed(GF_BitStream *bs) | |
| { | |
| if (bs) return bs->nb_removed; | |
| return 0; | |
| } | |
| /*returns 1 if aligned wrt current mode, 0 otherwise*/ | |
| Bool gf_bs_is_align(GF_BitStream *bs) | |
| { | |
| switch (bs->bsmode) { | |
| case GF_BITSTREAM_READ: | |
| case GF_BITSTREAM_FILE_READ: | |
| return ( (8 == bs->nbBits) ? GF_TRUE : GF_FALSE); | |
| default: | |
| return !bs->nbBits; | |
| } | |
| } | |
| static GFINLINE u8 gf_bs_load_byte(GF_BitStream *bs, Bool *is_eos) | |
| { | |
| u8 res; | |
| if (bs->cache_read) { | |
| if (bs->cache_read_pos == bs->cache_read_size) { | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| bs->cache_read_size = (u32) read(bs->fd, bs->cache_read, bs->cache_read_alloc); | |
| } else | |
| #endif | |
| { | |
| bs->cache_read_size = (u32) gf_fread(bs->cache_read, bs->cache_read_alloc, bs->stream); | |
| } | |
| bs->cache_read_pos = 0; | |
| if (!bs->cache_read_size) { | |
| *is_eos = GF_TRUE; | |
| return 0; | |
| } | |
| } | |
| res = bs->cache_read[bs->cache_read_pos]; | |
| bs->cache_read_pos++; | |
| } else { | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| if (read(bs->fd, &res, 1)<=0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Failed to read byte from file\n")); | |
| } | |
| } else | |
| #endif | |
| res = gf_fgetc(bs->stream); | |
| } | |
| return res; | |
| } | |
| /*fetch a new byte in the bitstream switch between packets*/ | |
| static u8 BS_ReadByte(GF_BitStream *bs) | |
| { | |
| Bool is_eos; | |
| if (bs->bsmode == GF_BITSTREAM_READ) { | |
| u8 res; | |
| if (bs->position >= bs->size) { | |
| if (bs->EndOfStream) bs->EndOfStream(bs->par); | |
| if (!bs->overflow_state) bs->overflow_state = 1; | |
| return 0; | |
| } | |
| res = bs->original[bs->position++]; | |
| if (bs->remove_emul_prevention_byte) { | |
| if ((bs->nb_zeros==2) && (res==0x03) && (bs->position<bs->size) && (bs->original[bs->position]<0x04)) { | |
| bs->nb_zeros = 0; | |
| bs->nb_removed++; | |
| res = bs->original[bs->position++]; | |
| } | |
| if (!res) bs->nb_zeros++; | |
| else bs->nb_zeros = 0; | |
| } | |
| return res; | |
| } | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) | |
| is_eos = (bs->position<bs->size) ? GF_FALSE : GF_TRUE; | |
| else | |
| #endif | |
| is_eos = bs->stream ? gf_feof(bs->stream) : GF_TRUE; | |
| //cache not fully read, reset EOS | |
| if (bs->cache_read && (bs->cache_read_pos<bs->cache_read_size)) | |
| is_eos = GF_FALSE; | |
| /*we are in FILE mode, test for end of file*/ | |
| if (!is_eos) { | |
| u8 res; | |
| Bool loc_eos=GF_FALSE; | |
| gf_assert(bs->position<=bs->size); | |
| bs->position++; | |
| res = gf_bs_load_byte(bs, &loc_eos); | |
| if (loc_eos) goto bs_eof; | |
| if (bs->remove_emul_prevention_byte) { | |
| if ((bs->nb_zeros==2) && (res==0x03) && (bs->position<bs->size)) { | |
| u8 next = gf_bs_load_byte(bs, &loc_eos); | |
| if (next < 0x04) { | |
| bs->nb_zeros = 0; | |
| bs->nb_removed++; | |
| res = next; | |
| bs->position++; | |
| } else { | |
| gf_bs_seek(bs, bs->position); | |
| } | |
| } | |
| if (!res) bs->nb_zeros++; | |
| else bs->nb_zeros = 0; | |
| } | |
| return res; | |
| } | |
| bs_eof: | |
| if (bs->EndOfStream) { | |
| bs->EndOfStream(bs->par); | |
| if (!bs->overflow_state) bs->overflow_state = 1; | |
| } else { | |
| if (!bs->overflow_state) { | |
| bs->overflow_state = 1; | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to overread bitstream\n")); | |
| } | |
| } | |
| gf_assert(bs->position <= 1+bs->size); | |
| return 0; | |
| } | |
| #define NO_OPTS | |
| #ifndef NO_OPTS | |
| static u32 bit_mask[] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}; | |
| static u32 bits_mask[] = {0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F}; | |
| #endif | |
| GF_EXPORT | |
| u8 gf_bs_read_bit(GF_BitStream *bs) | |
| { | |
| if (bs->nbBits == 8) { | |
| bs->current = BS_ReadByte(bs); | |
| bs->nbBits = 0; | |
| } | |
| #ifdef NO_OPTS | |
| { | |
| s32 ret; | |
| bs->current <<= 1; | |
| bs->nbBits++; | |
| ret = (bs->current & 0x100) >> 8; | |
| return (u8) ret; | |
| } | |
| #else | |
| return (u8) (bs->current & bit_mask[bs->nbBits++]) ? 1 : 0; | |
| #endif | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_read_int(GF_BitStream *bs, u32 nBits) | |
| { | |
| u32 ret; | |
| bs->total_bits_read+= nBits; | |
| #ifndef NO_OPTS | |
| if (nBits + bs->nbBits <= 8) { | |
| bs->nbBits += nBits; | |
| ret = (bs->current >> (8 - bs->nbBits) ) & bits_mask[nBits]; | |
| return ret; | |
| } | |
| #endif | |
| ret = 0; | |
| while (nBits-- > 0) { | |
| ret <<= 1; | |
| ret |= gf_bs_read_bit(bs); | |
| } | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_read_u8(GF_BitStream *bs) | |
| { | |
| gf_fatal_assert(bs->nbBits==8); | |
| if (bs->cache_read && (bs->cache_read_pos+1<bs->cache_read_size) ) { | |
| u32 ret = bs->cache_read[bs->cache_read_pos]; | |
| bs->cache_read_pos+=1; | |
| bs->position+=1; | |
| return ret; | |
| } | |
| return (u32) BS_ReadByte(bs); | |
| } | |
| #if 0 | |
| GF_EXPORT | |
| u32 gf_bs_read_u8_until_delimiter(GF_BitStream *bs, u8 delimiter, u8* out, u32 max_length) { | |
| u32 i = 0; | |
| char token=0; | |
| u64 cur_pos = gf_bs_get_position(bs); | |
| if (!max_length) out = NULL; | |
| while(gf_bs_available(bs) && (!max_length || i < max_length)) { | |
| gf_bs_read_data(bs, &token, 1); | |
| if (token == delimiter) goto found; | |
| if (out) out[i] = token; | |
| i++; | |
| } | |
| /* Delimiter not found */ | |
| gf_bs_seek(bs, cur_pos); | |
| return 0; | |
| found: | |
| return i; | |
| } | |
| #endif | |
| GF_EXPORT | |
| u32 gf_bs_read_u16(GF_BitStream *bs) | |
| { | |
| u32 ret; | |
| gf_fatal_assert(bs->nbBits==8); | |
| if (bs->cache_read && (bs->cache_read_pos+2<bs->cache_read_size) ) { | |
| ret = bs->cache_read[bs->cache_read_pos]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+1]; | |
| bs->cache_read_pos+=2; | |
| bs->position+=2; | |
| return ret; | |
| } | |
| ret = BS_ReadByte(bs); | |
| ret<<=8; | |
| ret |= BS_ReadByte(bs); | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_read_u24(GF_BitStream *bs) | |
| { | |
| u32 ret; | |
| gf_fatal_assert(bs->nbBits==8); | |
| if (bs->cache_read && (bs->cache_read_pos+3<bs->cache_read_size) ) { | |
| ret = bs->cache_read[bs->cache_read_pos]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+1]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+2]; | |
| bs->cache_read_pos+=3; | |
| bs->position+=3; | |
| return ret; | |
| } | |
| ret = BS_ReadByte(bs); | |
| ret<<=8; | |
| ret |= BS_ReadByte(bs); | |
| ret<<=8; | |
| ret |= BS_ReadByte(bs); | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_read_u32(GF_BitStream *bs) | |
| { | |
| u32 ret; | |
| gf_fatal_assert(bs->nbBits==8); | |
| if (bs->cache_read && (bs->cache_read_pos+4<bs->cache_read_size) ) { | |
| ret = bs->cache_read[bs->cache_read_pos]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+1]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+2]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+3]; | |
| bs->cache_read_pos+=4; | |
| bs->position+=4; | |
| return ret; | |
| } | |
| ret = BS_ReadByte(bs); | |
| ret<<=8; | |
| ret |= BS_ReadByte(bs); | |
| ret<<=8; | |
| ret |= BS_ReadByte(bs); | |
| ret<<=8; | |
| ret |= BS_ReadByte(bs); | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u64 gf_bs_read_u64(GF_BitStream *bs) | |
| { | |
| u64 ret; | |
| if (bs->cache_read && (bs->cache_read_pos+8<bs->cache_read_size) ) { | |
| ret = bs->cache_read[bs->cache_read_pos]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+1]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+2]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+3]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+4]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+5]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+6]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+7]; | |
| bs->cache_read_pos+=8; | |
| bs->position+=8; | |
| return ret; | |
| } | |
| ret = gf_bs_read_u32(bs); | |
| ret<<=32; | |
| ret |= gf_bs_read_u32(bs); | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u64 gf_bs_read_long_int(GF_BitStream *bs, u32 nBits) | |
| { | |
| u64 ret = 0; | |
| if (nBits>64) { | |
| u32 skip = nBits-64; | |
| if (gf_bs_available(bs) * 8 < nBits-8) { | |
| if (bs->EndOfStream) bs->EndOfStream(bs->par); | |
| bs->position = bs->size; | |
| if (!bs->overflow_state) bs->overflow_state = 1; | |
| return 0; | |
| } | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Reading %d bits but max should be 64, skipping %d most significants bits\n", nBits, nBits-64)); | |
| //avoid recursion | |
| while (skip) { | |
| if (skip>32) { | |
| gf_bs_read_int(bs, 32); | |
| skip-=32; | |
| } else { | |
| gf_bs_read_int(bs, skip); | |
| skip=0; | |
| } | |
| } | |
| ret = gf_bs_read_long_int(bs, 64); | |
| } else { | |
| while (nBits-- > 0) { | |
| ret <<= 1; | |
| ret |= gf_bs_read_bit(bs); | |
| } | |
| } | |
| return ret; | |
| } | |
| GF_EXPORT | |
| Float gf_bs_read_float(GF_BitStream *bs) | |
| { | |
| char buf [4] = "\0\0\0"; | |
| #ifdef NO_OPTS | |
| s32 i; | |
| for (i = 0; i < 32; i++) | |
| buf[3-i/8] |= gf_bs_read_bit(bs) << (7 - i%8); | |
| #else | |
| buf[3] = gf_bs_read_int(bs, 8); | |
| buf[2] = gf_bs_read_int(bs, 8); | |
| buf[1] = gf_bs_read_int(bs, 8); | |
| buf[0] = gf_bs_read_int(bs, 8); | |
| #endif | |
| return (* (Float *) buf); | |
| } | |
| GF_EXPORT | |
| Double gf_bs_read_double(GF_BitStream *bs) | |
| { | |
| char buf [8] = "\0\0\0\0\0\0\0"; | |
| s32 i; | |
| for (i = 0; i < 64; i++) | |
| buf[7-i/8] |= gf_bs_read_bit(bs) << (7 - i%8); | |
| return (* (Double *) buf); | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_read_data(GF_BitStream *bs, u8 *data, u32 nbBytes) | |
| { | |
| u64 orig = bs->position; | |
| if (bs->position+nbBytes > bs->size) { | |
| if (!bs->overflow_state) { | |
| bs->overflow_state = 1; | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to overread bitstream\n")); | |
| } | |
| return 0; | |
| } | |
| if (gf_bs_is_align(bs) ) { | |
| s32 bytes_read, bytes_read_cache; | |
| switch (bs->bsmode) { | |
| case GF_BITSTREAM_READ: | |
| case GF_BITSTREAM_WRITE: | |
| case GF_BITSTREAM_WRITE_DYN: | |
| memcpy(data, bs->original + bs->position, nbBytes); | |
| bs->position += nbBytes; | |
| return nbBytes; | |
| case GF_BITSTREAM_FILE_READ: | |
| case GF_BITSTREAM_FILE_WRITE: | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| bytes_read = bytes_read_cache = 0; | |
| if (bs->cache_read) { | |
| u32 csize = bs->cache_read_size-bs->cache_read_pos; | |
| if (csize>nbBytes) csize = nbBytes; | |
| memcpy(data, bs->cache_read + bs->cache_read_pos, csize); | |
| bs->cache_read_pos += csize; | |
| nbBytes -= csize; | |
| bytes_read_cache = csize; | |
| } | |
| if (nbBytes) { | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| bytes_read = (s32) read(bs->fd, data + bytes_read_cache, nbBytes); | |
| } else | |
| #endif | |
| { | |
| bytes_read = (s32) gf_fread(data + bytes_read_cache, nbBytes, bs->stream); | |
| } | |
| if (bytes_read<0) return bytes_read_cache; | |
| } | |
| bs->position += bytes_read + bytes_read_cache; | |
| return bytes_read + bytes_read_cache; | |
| default: | |
| return 0; | |
| } | |
| } | |
| while (nbBytes-- > 0) { | |
| *data++ = gf_bs_read_int(bs, 8); | |
| } | |
| return (u32) (bs->position - orig); | |
| } | |
| static void BS_WriteByte(GF_BitStream *bs, u8 val) | |
| { | |
| /*we don't allow write on READ buffers*/ | |
| if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_FILE_READ) ) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to write on read bitstream\n")); | |
| return; | |
| } | |
| if (!bs->original && !bs->stream | |
| #ifdef GPAC_HAS_FD | |
| && (bs->fd==-1) | |
| #endif | |
| ) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to write on unassigned bitstream\n")); | |
| return; | |
| } | |
| /*we are in MEM mode*/ | |
| if ( (bs->bsmode == GF_BITSTREAM_WRITE) || (bs->bsmode == GF_BITSTREAM_WRITE_DYN) ) { | |
| //if callback mode and dispatch is not blocked, dispatch | |
| if (bs->on_block_out && !bs->prevent_dispatch) { | |
| gf_assert(bs->position >= bs->bytes_out); | |
| if (bs->position - bs->bytes_out == bs->size) { | |
| bs->on_block_out(bs->usr_data, bs->original, (u32) (bs->position - bs->bytes_out)); | |
| bs->bytes_out = bs->position; | |
| } | |
| if (bs->original) | |
| bs->original[bs->position - bs->bytes_out] = val; | |
| bs->position++; | |
| gf_assert(bs->position >= bs->bytes_out); | |
| return; | |
| } | |
| //otherwise store | |
| if (bs->position - bs->bytes_out == bs->size) { | |
| /*no more space...*/ | |
| if (bs->bsmode != GF_BITSTREAM_WRITE_DYN) return; | |
| /*gf_realloc if enough space...*/ | |
| if (bs->size > 0xFFFFFFFF) return; | |
| bs->size = bs->size ? (bs->size * 2) : BS_MEM_BLOCK_ALLOC_SIZE; | |
| bs->original = (char*)gf_realloc(bs->original, (u32)bs->size); | |
| if (!bs->original) return; | |
| } | |
| if (bs->original) | |
| bs->original[bs->position - bs->bytes_out] = val; | |
| bs->position++; | |
| return; | |
| } | |
| if (bs->cache_write) { | |
| if (bs->buffer_written == bs->cache_write_size) { | |
| bs_flush_write_cache(bs); | |
| } | |
| bs->cache_write[bs->buffer_written] = val; | |
| bs->buffer_written++; | |
| if (bs->buffer_written == bs->cache_write_size) { | |
| bs_flush_write_cache(bs); | |
| } | |
| return; | |
| } | |
| /*we are in FILE mode, no pb for any gf_realloc...*/ | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| if (write(bs->fd, &val, 1)<=0) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Failed to write byte to file\n")); | |
| } | |
| } else | |
| #endif | |
| gf_fputc(val, bs->stream); | |
| /*check we didn't rewind the stream*/ | |
| if (bs->size == bs->position) bs->size++; | |
| bs->position += 1; | |
| } | |
| static void BS_WriteBit(GF_BitStream *bs, u32 bit) | |
| { | |
| bs->current <<= 1; | |
| bs->current |= bit; | |
| if (++ bs->nbBits == 8) { | |
| bs->nbBits = 0; | |
| BS_WriteByte(bs, (u8) bs->current); | |
| bs->current = 0; | |
| } | |
| } | |
| static s32 bs_handle_nbits_overflow(GF_BitStream* bs, s32 nBits, s32 max_shift) | |
| { | |
| if (nBits > max_shift) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to write %d bits, when max is %d\n", nBits, max_shift)); | |
| } | |
| while (nBits > max_shift) { | |
| gf_bs_write_long_int(bs, 0, max_shift); | |
| nBits -= max_shift; | |
| } | |
| return nBits; | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_int(GF_BitStream *bs, s32 _value, s32 nBits) | |
| { | |
| u32 value, nb_shift; | |
| s32 max_shift = sizeof(s32) * 8; | |
| if (!nBits) return; | |
| nBits = bs_handle_nbits_overflow(bs, nBits, max_shift); | |
| //move to unsigned to avoid sanitizer warnings when we pass a value not codable on the given number of bits | |
| //we do this when setting bit fields to all 1's | |
| value = (u32) _value; | |
| nb_shift = max_shift - nBits; | |
| if (nb_shift) | |
| value <<= nb_shift; | |
| while (--nBits >= 0) { | |
| //but check value as signed | |
| BS_WriteBit (bs, ((s32)value) < 0); | |
| value <<= 1; | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_long_int(GF_BitStream *bs, s64 _value, s32 nBits) | |
| { | |
| s32 max_shift = sizeof(s64) * 8; | |
| if (!nBits) return; | |
| nBits = bs_handle_nbits_overflow(bs, nBits, max_shift); | |
| //cf note in gf_bs_write_int | |
| u64 value = (u64) _value; | |
| value <<= max_shift - nBits; | |
| while (--nBits >= 0) { | |
| BS_WriteBit (bs, ((s64)value) < 0); | |
| value <<= 1; | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u8(GF_BitStream *bs, u32 value) | |
| { | |
| gf_fatal_assert(!bs->nbBits); | |
| if (bs->cache_write && (bs->buffer_written+1 < bs->cache_write_size) ) { | |
| bs->cache_write[bs->buffer_written] = (u8) value; | |
| bs->buffer_written += 1; | |
| } else { | |
| BS_WriteByte(bs, (u8) value); | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u16(GF_BitStream *bs, u32 value) | |
| { | |
| gf_fatal_assert(!bs->nbBits); | |
| if (bs->cache_write && (bs->buffer_written+2 < bs->cache_write_size) ) { | |
| bs->cache_write[bs->buffer_written] = (u8) ((value>>8)&0xff); | |
| bs->cache_write[bs->buffer_written+1] = (u8) ((value)&0xff); | |
| bs->buffer_written += 2; | |
| } else { | |
| BS_WriteByte(bs, (u8) ((value>>8)&0xff)); | |
| BS_WriteByte(bs, (u8) ((value)&0xff)); | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u24(GF_BitStream *bs, u32 value) | |
| { | |
| gf_fatal_assert(!bs->nbBits); | |
| if (bs->cache_write && (bs->buffer_written+3 < bs->cache_write_size) ) { | |
| bs->cache_write[bs->buffer_written] = (u8) ((value>>16)&0xff); | |
| bs->cache_write[bs->buffer_written+1] = (u8) ((value>>8)&0xff); | |
| bs->cache_write[bs->buffer_written+2] = (u8) ((value)&0xff); | |
| bs->buffer_written += 3; | |
| } else { | |
| BS_WriteByte(bs, (u8) ((value>>16)&0xff)); | |
| BS_WriteByte(bs, (u8) ((value>>8)&0xff)); | |
| BS_WriteByte(bs, (u8) ((value)&0xff)); | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u32(GF_BitStream *bs, u32 value) | |
| { | |
| gf_fatal_assert(!bs->nbBits); | |
| if (bs->cache_write && (bs->buffer_written+4 < bs->cache_write_size) ) { | |
| bs->cache_write[bs->buffer_written] = (u8) ((value>>24)&0xff); | |
| bs->cache_write[bs->buffer_written+1] = (u8) ((value>>16)&0xff); | |
| bs->cache_write[bs->buffer_written+2] = (u8) ((value>>8)&0xff); | |
| bs->cache_write[bs->buffer_written+3] = (u8) ((value)&0xff); | |
| bs->buffer_written += 4; | |
| } else { | |
| BS_WriteByte(bs, (u8) ((value>>24)&0xff)); | |
| BS_WriteByte(bs, (u8) ((value>>16)&0xff)); | |
| BS_WriteByte(bs, (u8) ((value>>8)&0xff)); | |
| BS_WriteByte(bs, (u8) ((value)&0xff)); | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u64(GF_BitStream *bs, u64 value) | |
| { | |
| gf_fatal_assert(!bs->nbBits); | |
| gf_bs_write_u32(bs, (u32) ((value>>32)&0xffffffff)); | |
| gf_bs_write_u32(bs, (u32) (value&0xffffffff)); | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_write_byte(GF_BitStream *bs, u8 byte, u32 repeat_count) | |
| { | |
| if (!gf_bs_is_align(bs) || bs->cache_write) { | |
| u32 count = 0; | |
| while (count<repeat_count) { | |
| gf_bs_write_int(bs, byte, 8); | |
| count++; | |
| } | |
| return count; | |
| } | |
| switch (bs->bsmode) { | |
| case GF_BITSTREAM_WRITE: | |
| if (bs->position + repeat_count > bs->size) | |
| return 0; | |
| memset(bs->original + bs->position, byte, repeat_count); | |
| bs->position += repeat_count; | |
| return repeat_count; | |
| case GF_BITSTREAM_WRITE_DYN: | |
| /*need to gf_realloc ...*/ | |
| if (bs->position+repeat_count> bs->size) { | |
| u32 new_size = (u32) (bs->size*2); | |
| if (!new_size) new_size = BS_MEM_BLOCK_ALLOC_SIZE; | |
| if (bs->size + repeat_count > 0xFFFFFFFF) | |
| return 0; | |
| while (new_size < (u32) ( bs->size + repeat_count)) | |
| new_size *= 2; | |
| bs->original = (char*)gf_realloc(bs->original, sizeof(u32)*new_size); | |
| if (!bs->original) | |
| return 0; | |
| bs->size = new_size; | |
| } | |
| memset(bs->original + bs->position, byte, repeat_count); | |
| bs->position += repeat_count; | |
| return repeat_count; | |
| case GF_BITSTREAM_FILE_READ: | |
| case GF_BITSTREAM_FILE_WRITE: | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| if (write(bs->fd, &byte, repeat_count) != repeat_count) | |
| return 0; | |
| } else | |
| #endif | |
| { | |
| if (gf_fwrite(&byte, repeat_count, bs->stream) != repeat_count) | |
| return 0; | |
| } | |
| if (bs->size == bs->position) bs->size += repeat_count; | |
| bs->position += repeat_count; | |
| return repeat_count; | |
| default: | |
| return 0; | |
| } | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_float(GF_BitStream *bs, Float value) | |
| { | |
| u32 i; | |
| union | |
| { float f; | |
| char sz [4]; | |
| } float_value; | |
| float_value.f = value; | |
| for (i = 0; i < 32; i++) | |
| BS_WriteBit(bs, (float_value.sz [3 - i / 8] & 1 << (7 - i % 8)) != 0); | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_double (GF_BitStream *bs, Double value) | |
| { | |
| u32 i; | |
| union | |
| { Double d; | |
| char sz [8]; | |
| } double_value; | |
| double_value.d = value; | |
| for (i = 0; i < 64; i++) { | |
| BS_WriteBit(bs, (double_value.sz [7 - i / 8] & 1 << (7 - i % 8)) != 0); | |
| } | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_write_data(GF_BitStream *bs, const u8 *data, u32 nbBytes) | |
| { | |
| /*we need some feedback for this guy...*/ | |
| u64 begin = bs->position; | |
| if (!nbBytes) return 0; | |
| if (gf_bs_is_align(bs)) { | |
| switch (bs->bsmode) { | |
| case GF_BITSTREAM_WRITE: | |
| if (bs->position+nbBytes > bs->size) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to overwrite bitstream by %d bytes\n", bs->position + nbBytes - bs->size)); | |
| return 0; | |
| } | |
| memcpy(bs->original + bs->position, data, nbBytes); | |
| bs->position += nbBytes; | |
| return nbBytes; | |
| case GF_BITSTREAM_WRITE_DYN: | |
| //if callback mode and dispatch not disabled, dispatch bytes | |
| if (bs->on_block_out && !bs->prevent_dispatch) { | |
| gf_assert(bs->position >= bs->bytes_out); | |
| if (bs->position - bs->bytes_out + nbBytes <= bs->size) { | |
| memcpy(bs->original + bs->position - bs->bytes_out, data, nbBytes); | |
| bs->position += nbBytes; | |
| } else { | |
| if (bs->position > bs->bytes_out) | |
| bs->on_block_out(bs->usr_data, bs->original, (u32) (bs->position - bs->bytes_out) ); | |
| if (nbBytes) | |
| bs->on_block_out(bs->usr_data, (char *) data, nbBytes); | |
| bs->position += nbBytes; | |
| bs->bytes_out = bs->position; | |
| } | |
| gf_assert(bs->position >= bs->bytes_out); | |
| return nbBytes; | |
| } | |
| //otherwise store | |
| /*need to gf_realloc ...*/ | |
| if (bs->position + nbBytes - bs->bytes_out > bs->size) { | |
| u32 new_size = (u32) (bs->size*2); | |
| if (!new_size) new_size = BS_MEM_BLOCK_ALLOC_SIZE; | |
| if (bs->size + nbBytes > 0xFFFFFFFF) | |
| return 0; | |
| while (new_size < (u32) ( bs->size + nbBytes)) | |
| new_size *= 2; | |
| new_size = MIN(new_size, GF_UINT_MAX); | |
| bs->original = (char*)gf_realloc(bs->original, sizeof(u32)*new_size); | |
| if (!bs->original) | |
| return 0; | |
| bs->size = new_size; | |
| } | |
| memcpy(bs->original + bs->position - bs->bytes_out, data, nbBytes); | |
| bs->position += nbBytes; | |
| return nbBytes; | |
| case GF_BITSTREAM_FILE_READ: | |
| case GF_BITSTREAM_FILE_WRITE: | |
| if (bs->cache_write) { | |
| //if block fits in our write cache, write it | |
| if (bs->buffer_written + nbBytes < bs->cache_write_size) { | |
| memcpy(bs->cache_write+bs->buffer_written, data, nbBytes); | |
| bs->buffer_written+=nbBytes; | |
| return nbBytes; | |
| } | |
| //otherwise flush cache and use file write | |
| if (!bs_flush_write_cache(bs)) | |
| return 0; | |
| } | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| if (write(bs->fd, data, nbBytes) != nbBytes) return 0; | |
| } else | |
| #endif | |
| { | |
| if (gf_fwrite(data, nbBytes, bs->stream) != nbBytes) return 0; | |
| } | |
| if (bs->size == bs->position) bs->size += nbBytes; | |
| bs->position += nbBytes; | |
| return nbBytes; | |
| default: | |
| return 0; | |
| } | |
| } | |
| while (nbBytes) { | |
| gf_bs_write_int(bs, (s32) *data, 8); | |
| data++; | |
| nbBytes--; | |
| } | |
| return (u32) (bs->position - begin); | |
| } | |
| /*align return the num of bits read in READ mode, 0 in WRITE*/ | |
| GF_EXPORT | |
| u8 gf_bs_align(GF_BitStream *bs) | |
| { | |
| u8 res = 8 - bs->nbBits; | |
| if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_FILE_READ)) { | |
| if (res > 0) { | |
| gf_bs_read_int(bs, res); | |
| } | |
| return res; | |
| } | |
| if (bs->nbBits > 0) { | |
| gf_bs_write_int (bs, 0, res); | |
| return res; | |
| } | |
| return 0; | |
| } | |
| /*size available in the bitstream*/ | |
| GF_EXPORT | |
| u64 gf_bs_available(GF_BitStream *bs) | |
| { | |
| s64 cur, end; | |
| /*in WRITE mode only, this should not be called, but return something big in case ...*/ | |
| if ( (bs->bsmode == GF_BITSTREAM_WRITE) | |
| || (bs->bsmode == GF_BITSTREAM_WRITE_DYN) | |
| ) | |
| return (u64) -1; | |
| /*we are in MEM mode*/ | |
| if (bs->bsmode == GF_BITSTREAM_READ) { | |
| if (bs->size < bs->position) | |
| return 0; | |
| else | |
| return (bs->size - bs->position); | |
| } | |
| /*FILE READ: assume size hasn't changed, otherwise the user shall call gf_bs_get_refreshed_size*/ | |
| if (bs->bsmode==GF_BITSTREAM_FILE_READ) { | |
| if (bs->position>bs->size) return 0; | |
| return (bs->size - bs->position); | |
| } | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| cur = lseek_64(bs->fd, 0, SEEK_CUR); | |
| end = bs->position; | |
| } else | |
| #endif | |
| { | |
| cur = gf_ftell(bs->stream); | |
| end = gf_fsize(bs->stream); | |
| gf_fseek(bs->stream, cur, SEEK_SET); | |
| } | |
| return (u64) (end - cur); | |
| } | |
| /*call this funct to set the buffer size to the nb of bytes written | |
| Used only in WRITE mode, as we don't know the real size during allocation... | |
| return -1 for bad param or gf_malloc failed | |
| return nbBytes cut*/ | |
| static s32 BS_CutBuffer(GF_BitStream *bs) | |
| { | |
| s32 nbBytes; | |
| if ( (bs->bsmode != GF_BITSTREAM_WRITE_DYN) && (bs->bsmode != GF_BITSTREAM_WRITE)) return (u32) -1; | |
| /*Align our buffer or we're dead!*/ | |
| gf_bs_align(bs); | |
| nbBytes = (u32) (bs->size - bs->position); | |
| if (!nbBytes || (nbBytes == 0xFFFFFFFF) || (bs->position >= 0xFFFFFFFF)) return 0; | |
| /* | |
| bs->original = (char*)gf_realloc(bs->original, (u32) bs->position); | |
| if (! bs->original) return (u32) -1; | |
| */ | |
| /*just in case, re-adjust..*/ | |
| bs->size = bs->position; | |
| return nbBytes; | |
| } | |
| /*For DYN mode, this gets the content out without cutting the buffer to the number of written bytes*/ | |
| GF_EXPORT | |
| void gf_bs_get_content_no_truncate(GF_BitStream *bs, u8 **output, u32 *outSize, u32 *alloc_size) | |
| { | |
| /*only in WRITE MEM mode*/ | |
| if (!bs || bs->bsmode != GF_BITSTREAM_WRITE_DYN) return; | |
| if (bs->on_block_out && bs->position>bs->bytes_out) { | |
| bs->on_block_out(bs->usr_data, bs->original, (u32) (bs->position - bs->bytes_out) ); | |
| } | |
| if (!bs->position && !bs->nbBits) { | |
| if (!alloc_size) { | |
| *output = NULL; | |
| gf_free(bs->original); | |
| } else { | |
| *alloc_size = (u32) bs->size; | |
| *output = bs->original; | |
| } | |
| *outSize = 0; | |
| } else { | |
| if (alloc_size) { | |
| /*Align our buffer or we're dead!*/ | |
| gf_bs_align(bs); | |
| *alloc_size = (u32) bs->size; | |
| *outSize = (u32) bs->position; | |
| *output = bs->original; | |
| } else { | |
| s32 copy = BS_CutBuffer(bs); | |
| if (copy < 0) { | |
| *output = NULL; | |
| } else | |
| *output = bs->original; | |
| *outSize = (u32) bs->size; | |
| } | |
| } | |
| bs->original = NULL; | |
| bs->size = 0; | |
| bs->position = 0; | |
| } | |
| /*For DYN mode, this gets the content out*/ | |
| GF_EXPORT | |
| void gf_bs_get_content(GF_BitStream *bs, u8 **output, u32 *outSize) | |
| { | |
| gf_bs_get_content_no_truncate(bs, output, outSize, NULL); | |
| } | |
| GF_Err gf_bs_grow(GF_BitStream *bs, u32 addSize) | |
| { | |
| if (!bs || (bs->bsmode != GF_BITSTREAM_WRITE_DYN) || bs->cache_write) return GF_OK; | |
| if (bs->position + addSize <= bs->size) return GF_OK; | |
| bs->size += addSize; | |
| bs->original = gf_realloc(bs->original, (u32) bs->size); | |
| if (!bs->original) return GF_OUT_OF_MEM; | |
| return GF_OK; | |
| } | |
| /* Skip nbytes. | |
| Align | |
| If READ (MEM or FILE) mode, just read n times 8 bit | |
| If WRITE (MEM or FILE) mode, write n times 0 on 8 bit | |
| */ | |
| GF_EXPORT | |
| void gf_bs_skip_bytes(GF_BitStream *bs, u64 nbBytes) | |
| { | |
| if (!bs || !nbBytes) return; | |
| gf_bs_align(bs); | |
| /*special case for file skipping...*/ | |
| if ((bs->bsmode == GF_BITSTREAM_FILE_WRITE) || (bs->bsmode == GF_BITSTREAM_FILE_READ)) { | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| if (bs->cache_read) { | |
| u32 csize = bs->cache_read_size - bs->cache_read_pos; | |
| if (csize>=nbBytes) { | |
| bs->cache_read_pos += (u32) nbBytes; | |
| bs->position += nbBytes; | |
| return; | |
| } | |
| nbBytes -= csize; | |
| bs->position += csize; | |
| bs->cache_read_pos = bs->cache_read_size; | |
| } | |
| //weird msys2 bug resulting in broken seek on some files ?!? -the bug is not happening when doing absolute seek | |
| // gf_fseek(bs->stream, nbBytes, SEEK_CUR); | |
| bs->position += nbBytes; | |
| if (bs->bsmode == GF_BITSTREAM_FILE_READ) { | |
| if (bs->position > bs->size) bs->position = bs->size; | |
| } | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| lseek_64(bs->fd, bs->position, SEEK_SET); | |
| } else | |
| #endif | |
| { | |
| gf_fseek(bs->stream, bs->position, SEEK_SET); | |
| } | |
| return; | |
| } | |
| /*special case for reading*/ | |
| if (bs->bsmode == GF_BITSTREAM_READ) { | |
| if (bs->remove_emul_prevention_byte) { | |
| while (nbBytes) { | |
| gf_bs_read_u8(bs); | |
| nbBytes--; | |
| } | |
| } else { | |
| bs->position += nbBytes; | |
| if (bs->position>bs->size) { | |
| bs->position = bs->size; | |
| bs->overflow_state = 1; | |
| } | |
| } | |
| return; | |
| } | |
| /*for writing we must do it this way, otherwise pb in dynamic buffers*/ | |
| while (nbBytes) { | |
| gf_bs_write_int(bs, 0, 8); | |
| nbBytes--; | |
| } | |
| } | |
| #ifdef GPAC_ENABLE_BIFS_PMF | |
| void gf_bs_rewind_bits(GF_BitStream *bs, u64 nbBits) | |
| { | |
| u64 nbBytes; | |
| if (bs->bsmode != GF_BITSTREAM_READ) return; | |
| nbBits -= (bs->nbBits); | |
| nbBytes = (nbBits+8)>>3; | |
| nbBits = nbBytes*8 - nbBits; | |
| gf_bs_align(bs); | |
| gf_assert(bs->position >= nbBytes); | |
| bs->position -= nbBytes + 1; | |
| gf_bs_read_int(bs, (u32)nbBits); | |
| return; | |
| } | |
| #endif | |
| /*seek from beginning of stream: use internally even when non aligned!*/ | |
| static GF_Err BS_SeekIntern(GF_BitStream *bs, u64 offset) | |
| { | |
| u32 i; | |
| /*if mem, do it */ | |
| if ((bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_WRITE) || (bs->bsmode == GF_BITSTREAM_WRITE_DYN)) { | |
| if (offset > 0xFFFFFFFF) return GF_IO_ERR; | |
| if (!bs->original) return GF_BAD_PARAM; | |
| /*0 for write, read will be done automatically*/ | |
| if (offset >= bs->size) { | |
| if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_WRITE) ) { | |
| if (offset > bs->size) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[BS] Attempt to seek to %d after end of bitstream %d, assuming seek to end\n", offset, bs->size)); | |
| } | |
| bs->position = bs->size; | |
| bs->nbBits = (bs->bsmode == GF_BITSTREAM_READ) ? 8 : 0; | |
| return GF_OK; | |
| } | |
| /*in DYN, gf_realloc ...*/ | |
| bs->original = (char*)gf_realloc(bs->original, (u32) (offset + 1)); | |
| if (!bs->original) | |
| return GF_OUT_OF_MEM; | |
| for (i = 0; i < (u32) (offset + 1 - bs->size); i++) { | |
| bs->original[bs->size + i] = 0; | |
| } | |
| bs->size = offset + 1; | |
| } | |
| bs->current = bs->original[offset]; | |
| bs->position = offset; | |
| bs->nbBits = (bs->bsmode == GF_BITSTREAM_READ) ? 8 : 0; | |
| return GF_OK; | |
| } | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| if (bs->cache_read) { | |
| bs->cache_read_pos = bs->cache_read_size; | |
| } | |
| s64 res; | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| res = lseek_64(bs->fd, offset, SEEK_SET); | |
| if (res>=0) res = 0; | |
| } else | |
| #endif | |
| { | |
| res = gf_fseek(bs->stream, offset, SEEK_SET); | |
| } | |
| bs->position = offset; | |
| bs->current = 0; | |
| /*setup NbBits so that next acccess to the buffer will trigger read/write*/ | |
| bs->nbBits = (bs->bsmode == GF_BITSTREAM_FILE_READ) ? 8 : 0; | |
| return res ? GF_IO_ERR : GF_OK; | |
| } | |
| /*seek from beginning of stream: align before anything else*/ | |
| GF_EXPORT | |
| GF_Err gf_bs_seek(GF_BitStream *bs, u64 offset) | |
| { | |
| bs->overflow_state = 0; | |
| if (bs->on_block_out) { | |
| GF_Err e; | |
| if (offset < bs->bytes_out) { | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[BS] Attempt to seek on byte range already forwarded\n")); | |
| return GF_BAD_PARAM; | |
| } | |
| /*warning: we allow offset = bs->size for WRITE buffers*/ | |
| if (offset - bs->bytes_out > bs->size) | |
| return GF_BAD_PARAM; | |
| gf_bs_align(bs); | |
| e = BS_SeekIntern(bs, offset - bs->bytes_out); | |
| bs->position += bs->bytes_out; | |
| return e; | |
| } | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| /*warning: we allow offset = bs->size for WRITE buffers*/ | |
| if (offset > bs->size) return GF_BAD_PARAM; | |
| gf_bs_align(bs); | |
| return BS_SeekIntern(bs, offset); | |
| } | |
| /*peek bits (as int!!) from orig position (ON BYTE BOUNDARIES, from 0) - only for read ...*/ | |
| GF_EXPORT | |
| u32 gf_bs_peek_bits(GF_BitStream *bs, u32 numBits, u64 byte_offset) | |
| { | |
| u64 curPos; | |
| u32 curBits, ret, current, nb_zeros, nb_removed; | |
| if ( (bs->bsmode != GF_BITSTREAM_READ) && (bs->bsmode != GF_BITSTREAM_FILE_READ)) return 0; | |
| if (!numBits || (bs->size < bs->position + byte_offset)) return 0; | |
| if (bs->cache_read) { | |
| if ((numBits==32) && (bs->cache_read_pos+byte_offset+4<bs->cache_read_size) ) { | |
| ret = bs->cache_read[bs->cache_read_pos+byte_offset]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+byte_offset+1]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+byte_offset+2]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+byte_offset+3]; | |
| return ret; | |
| } | |
| if ((numBits==24) && (bs->cache_read_pos+byte_offset+3<bs->cache_read_size) ) { | |
| ret = bs->cache_read[bs->cache_read_pos+byte_offset]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+byte_offset+1]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+byte_offset+2]; | |
| return ret; | |
| } | |
| if ((numBits==16) && (bs->cache_read_pos+byte_offset+2<bs->cache_read_size) ) { | |
| ret = bs->cache_read[bs->cache_read_pos+byte_offset]; | |
| ret<<=8; | |
| ret |= bs->cache_read[bs->cache_read_pos+byte_offset+1]; | |
| return ret; | |
| } | |
| if ((numBits==8) && (bs->cache_read_pos+byte_offset+1<bs->cache_read_size) ) { | |
| return bs->cache_read[bs->cache_read_pos+byte_offset]; | |
| } | |
| } | |
| /*store our state*/ | |
| curPos = bs->position; | |
| curBits = bs->nbBits; | |
| current = bs->current; | |
| nb_zeros = bs->nb_zeros; | |
| nb_removed = bs->nb_removed; | |
| if (byte_offset) { | |
| if (bs->remove_emul_prevention_byte) { | |
| while (byte_offset) { | |
| gf_bs_read_int(bs, 8); | |
| byte_offset--; | |
| } | |
| } else { | |
| gf_bs_seek(bs, bs->position + byte_offset); | |
| } | |
| } | |
| ret = gf_bs_read_int(bs, numBits); | |
| /*restore our cache - position*/ | |
| gf_bs_seek(bs, curPos); | |
| /*to avoid re-reading our bits ...*/ | |
| bs->nbBits = curBits; | |
| bs->current = current; | |
| bs->nb_zeros = nb_zeros; | |
| bs->nb_removed = nb_removed; | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u64 gf_bs_get_refreshed_size(GF_BitStream *bs) | |
| { | |
| s64 offset; | |
| switch (bs->bsmode) { | |
| case GF_BITSTREAM_READ: | |
| case GF_BITSTREAM_WRITE: | |
| return bs->size; | |
| default: | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd>=0) { | |
| bs->size = gf_fd_fsize(bs->fd); | |
| return bs->size; | |
| } | |
| #endif | |
| if (bs->stream) { | |
| offset = gf_ftell(bs->stream); | |
| bs->size = gf_fsize(bs->stream); | |
| gf_fseek(bs->stream, offset, SEEK_SET); | |
| } | |
| return bs->size; | |
| } | |
| } | |
| GF_EXPORT | |
| u64 gf_bs_get_size(GF_BitStream *bs) | |
| { | |
| if (bs->cache_write) { | |
| if (bs->size == bs->position) | |
| return bs->size + bs->buffer_written; | |
| else | |
| return bs->size; | |
| } | |
| if (bs->on_block_out) | |
| return bs->position; | |
| return bs->size; | |
| } | |
| GF_EXPORT | |
| u64 gf_bs_get_position(GF_BitStream *bs) | |
| { | |
| if (bs->cache_write) | |
| return bs->position + bs->buffer_written; | |
| return bs->position; | |
| } | |
| GF_EXPORT | |
| u8 gf_bs_bits_available(GF_BitStream *bs) | |
| { | |
| if (bs->size > bs->position) return 8; | |
| if (bs->nbBits < 8) return (8-bs->nbBits); | |
| return 0; | |
| } | |
| GF_EXPORT | |
| void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par) | |
| { | |
| bs->EndOfStream = EndOfStream; | |
| bs->par = par; | |
| } | |
| GF_EXPORT | |
| u64 gf_bs_read_u64_le(GF_BitStream *bs) | |
| { | |
| u64 ret, v; | |
| ret = gf_bs_read_int(bs, 8); | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=8; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=16; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=24; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=32; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=40; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=48; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=56; | |
| ret |= v; | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_read_u32_le(GF_BitStream *bs) | |
| { | |
| u32 ret, v; | |
| ret = gf_bs_read_int(bs, 8); | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=8; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=16; | |
| ret |= v; | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=24; | |
| ret |= v; | |
| return ret; | |
| } | |
| GF_EXPORT | |
| u16 gf_bs_read_u16_le(GF_BitStream *bs) | |
| { | |
| u32 ret, v; | |
| ret = gf_bs_read_int(bs, 8); | |
| v = gf_bs_read_int(bs, 8); | |
| v<<=8; | |
| ret |= v; | |
| return ret; | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u64_le(GF_BitStream *bs, u64 val) | |
| { | |
| gf_bs_write_int(bs, val & 0xFF, 8); | |
| gf_bs_write_int(bs, (val>>8) & 0xFF, 8); | |
| gf_bs_write_int(bs, (val>>16) & 0xFF, 8); | |
| gf_bs_write_int(bs, (val>>24) & 0xFF, 8); | |
| gf_bs_write_int(bs, (val>>32) & 0xFF, 8); | |
| gf_bs_write_int(bs, (val>>40) & 0xFF, 8); | |
| gf_bs_write_int(bs, (val>>48) & 0xFF, 8); | |
| gf_bs_write_int(bs, (val>>56) & 0xFF, 8); | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u32_le(GF_BitStream *bs, u32 val) | |
| { | |
| gf_bs_write_int(bs, val & 0xFF, 8); | |
| gf_bs_write_int(bs, val>>8, 8); | |
| gf_bs_write_int(bs, val>>16, 8); | |
| gf_bs_write_int(bs, val>>24, 8); | |
| } | |
| GF_EXPORT | |
| void gf_bs_write_u16_le(GF_BitStream *bs, u32 val) | |
| { | |
| gf_bs_write_int(bs, val & 0xFF, 8); | |
| gf_bs_write_int(bs, val>>8, 8); | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_get_bit_offset(GF_BitStream *bs) | |
| { | |
| if (bs->bsmode==GF_BITSTREAM_READ) return (u32) ( (bs->position - 1) * 8 + bs->nbBits); | |
| return (u32) ( (bs->position ) * 8 + bs->nbBits); | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_get_bit_position(GF_BitStream *bs) | |
| { | |
| return bs->nbBits; | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_read_vluimsbf5(GF_BitStream *bs) | |
| { | |
| u32 nb_words = 0; | |
| while (gf_bs_read_int(bs, 1)) nb_words++; | |
| nb_words++; | |
| return gf_bs_read_int(bs, 4*nb_words); | |
| } | |
| GF_EXPORT | |
| void gf_bs_truncate(GF_BitStream *bs) | |
| { | |
| bs->o_size = bs->size; | |
| bs->size = bs->position; | |
| } | |
| void gf_bs_untruncate(GF_BitStream *bs) | |
| { | |
| bs->size = bs->o_size; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_bs_transfer(GF_BitStream *dst, GF_BitStream *src, Bool keep_src) | |
| { | |
| u8 *data; | |
| u32 data_len, written; | |
| data = NULL; | |
| data_len = 0; | |
| gf_bs_get_content(src, &data, &data_len); | |
| if (!data || !data_len) | |
| { | |
| if (data) { | |
| if (keep_src) { | |
| src->original = data; | |
| src->size = data_len; | |
| } else { | |
| gf_free(data); | |
| } | |
| return GF_IO_ERR; | |
| } | |
| return GF_OK; | |
| } | |
| written = gf_bs_write_data(dst, data, data_len); | |
| if (keep_src) { | |
| src->original = data; | |
| src->size = data_len; | |
| } else { | |
| gf_free(data); | |
| } | |
| if (written<data_len) return GF_IO_ERR; | |
| return GF_OK; | |
| } | |
| GF_EXPORT | |
| void gf_bs_flush(GF_BitStream *bs) | |
| { | |
| if (!bs->stream) return; | |
| if (bs->bsmode != GF_BITSTREAM_FILE_WRITE) return; | |
| if (bs->cache_write) | |
| bs_flush_write_cache(bs); | |
| gf_fflush(bs->stream); | |
| } | |
| #if 0 //unused | |
| /*! | |
| \brief Reassigns FILE object for stream-based bitstreams | |
| * | |
| *Reassigns FILE object for stream-based bitstreams. Automatically sets the stream position to the bitstream position | |
| \param bs the target bitstream | |
| \param stream the new stream to assign | |
| */ | |
| void gf_bs_reassign(GF_BitStream *bs, FILE *stream) | |
| { | |
| if (!bs) return; | |
| switch (bs->bsmode) { | |
| case GF_BITSTREAM_FILE_WRITE: | |
| case GF_BITSTREAM_FILE_READ: | |
| bs->stream = stream; | |
| if (gf_ftell(stream) != bs->position) | |
| gf_bs_seek(bs, bs->position); | |
| break; | |
| } | |
| } | |
| #endif | |
| u64 gf_bs_set_cookie(GF_BitStream *bs, u64 cookie) | |
| { | |
| u64 res = 0; | |
| if (bs) { | |
| res = bs->cookie; | |
| bs->cookie = cookie; | |
| } | |
| return res; | |
| } | |
| u64 gf_bs_get_cookie(GF_BitStream *bs) | |
| { | |
| if (!bs) return 0; | |
| return bs->cookie; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_bs_insert_data(GF_BitStream *bs, u8 *data, u32 size, u64 offset) | |
| { | |
| u64 cur_r, cur_w, pos; | |
| u32 nb_io; | |
| u8 *block=NULL; | |
| u32 bk_size = 8196; | |
| if (bs->on_block_out) return GF_BAD_PARAM; | |
| #ifdef GPAC_HAS_FD | |
| if (bs->fd) bk_size = 100000; | |
| #endif | |
| block = gf_malloc(sizeof(u8)*bk_size); | |
| if (!block) return GF_OUT_OF_MEM; | |
| pos = bs->position; | |
| nb_io = gf_bs_write_data(bs, data, size); | |
| if (nb_io != size) goto exit; | |
| cur_w = bs->position; | |
| gf_bs_seek(bs, pos); | |
| cur_r = pos; | |
| pos = cur_w; | |
| while (cur_r > offset) { | |
| u32 move_bytes = bk_size; | |
| if (cur_r - offset < move_bytes) | |
| move_bytes = (u32) (cur_r - offset); | |
| gf_bs_seek(bs, cur_r - move_bytes); | |
| nb_io = gf_bs_read_data(bs, block, move_bytes); | |
| if (nb_io != move_bytes) goto exit; | |
| gf_bs_seek(bs, cur_w - move_bytes); | |
| nb_io = gf_bs_write_data(bs, block, move_bytes); | |
| if (nb_io != move_bytes) goto exit; | |
| cur_r -= move_bytes; | |
| cur_w -= move_bytes; | |
| } | |
| gf_bs_seek(bs, offset); | |
| nb_io = gf_bs_write_data(bs, data, size); | |
| if (nb_io != size) goto exit; | |
| gf_bs_seek(bs, pos); | |
| if (block) gf_free(block); | |
| return GF_OK; | |
| exit: | |
| if (block) gf_free(block); | |
| gf_bs_seek(bs, pos); | |
| return GF_IO_ERR; | |
| } | |
| GF_Err gf_bs_set_logger(GF_BitStream *bs, void (*on_bs_log)(void *udta, const char *field_name, u32 nb_bits, u64 field_val, s32 idx1, s32 idx2, s32 idx3), void *udta) | |
| { | |
| if (!bs) return GF_BAD_PARAM; | |
| bs->on_log = on_bs_log; | |
| bs->log_udta = udta; | |
| return GF_OK; | |
| } | |
| #ifndef GPAC_DISABLE_AVPARSE_LOGS | |
| void gf_bs_log_idx(GF_BitStream *bs, u32 nBits, const char *fname, s64 val, s32 idx1, s32 idx2, s32 idx3) | |
| { | |
| gf_assert(bs); | |
| if (bs->on_log) bs->on_log(bs->log_udta, fname, nBits, val, idx1, idx2, idx3); | |
| } | |
| #endif | |
| void gf_bs_mark_overflow(GF_BitStream *bs, Bool reset) | |
| { | |
| bs->overflow_state = reset ? 0 : 2; | |
| } | |
| GF_EXPORT | |
| u32 gf_bs_is_overflow(GF_BitStream *bs) | |
| { | |
| return bs->overflow_state; | |
| } | |
| GF_EXPORT | |
| char *gf_bs_read_utf8(GF_BitStream *bs) | |
| { | |
| char szC[2]; | |
| char *res = NULL; | |
| if (!bs || !gf_bs_is_align(bs)) | |
| return NULL; | |
| szC[1] = 0; | |
| while (gf_bs_available(bs)) { | |
| u8 c = gf_bs_read_u8(bs); | |
| if (!c) break; | |
| szC[0] = c; | |
| gf_dynstrcat(&res, szC, NULL); | |
| } | |
| return res; | |
| } | |
| GF_EXPORT | |
| GF_Err gf_bs_write_utf8(GF_BitStream *bs, const char *str) | |
| { | |
| if (!bs || !gf_bs_is_align(bs)) | |
| return GF_BAD_PARAM; | |
| u32 i, len = str ? (u32) strlen(str) : 0; | |
| for (i=0; i<len; i++) | |
| gf_bs_write_u8(bs, str[i]); | |
| gf_bs_write_u8(bs, 0); | |
| return GF_OK; | |
| } |