summaryrefslogtreecommitdiff
path: root/gpr/source/lib/vc5_common/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpr/source/lib/vc5_common/stream.c')
-rwxr-xr-xgpr/source/lib/vc5_common/stream.c524
1 files changed, 524 insertions, 0 deletions
diff --git a/gpr/source/lib/vc5_common/stream.c b/gpr/source/lib/vc5_common/stream.c
new file mode 100755
index 0000000..59ecc7d
--- /dev/null
+++ b/gpr/source/lib/vc5_common/stream.c
@@ -0,0 +1,524 @@
+/*! @file stream.h
+ *
+ * @brief This module implements a byte stream abstraction that hides the details
+ * of how a stream of bytes is read or written on demand by the bitstream.
+ * The byte stream can be bound to a binary file opened for reading (writing),
+ * to a buffer in memory, or to a module that reads (writes) a video track
+ * in a media container.
+ *
+ * @version 1.0.0
+ *
+ * (C) Copyright 2018 GoPro Inc (http://gopro.com/).
+ *
+ * Licensed under either:
+ * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
+ * - MIT license, http://opensource.org/licenses/MIT
+ * at your option.
+ *
+ * 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 "common.h"
+
+// Local functions
+CODEC_ERROR GetBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset);
+CODEC_ERROR PutBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset);
+CODEC_ERROR GetBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset);
+CODEC_ERROR PutBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset);
+
+/*!
+ @brief Open a stream for reading bytes from the specified file
+
+*/
+CODEC_ERROR OpenStream(STREAM *stream, const char *pathname)
+{
+ assert(stream != NULL);
+ if (! (stream != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ // Clear all members of the stream data structure
+ memset(stream, 0, sizeof(STREAM));
+
+ // Open the file and bind it to the stream
+ stream->location.file.iobuf = fopen(pathname, "rb");
+ assert(stream->location.file.iobuf != NULL);
+ if (! (stream->location.file.iobuf != NULL)) {
+ return CODEC_ERROR_OPEN_FILE_FAILED;
+ }
+
+ // Set the stream type and access
+ stream->type = STREAM_TYPE_FILE;
+ stream->access = STREAM_ACCESS_READ;
+
+ // Clear the number of bytes read from the stream
+ stream->byte_count = 0;
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Create a stream for writing bytes to a specified file
+
+*/
+CODEC_ERROR CreateStream(STREAM *stream, const char *pathname)
+{
+ assert(stream != NULL);
+ if (! (stream != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ // Clear all members of the stream data structure
+ memset(stream, 0, sizeof(STREAM));
+
+ // Open the file and bind it to the stream
+ stream->location.file.iobuf = fopen(pathname, "wb+");
+ assert(stream->location.file.iobuf != NULL);
+ if (! (stream->location.file.iobuf != NULL)) {
+ return CODEC_ERROR_CREATE_FILE_FAILED;
+ }
+
+ // Set the stream type and access
+ stream->type = STREAM_TYPE_FILE;
+ stream->access = STREAM_ACCESS_WRITE;
+
+ // Clear the number of bytes written to the stream
+ stream->byte_count = 0;
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Read a word from a byte stream
+
+ This routine is used by the bitstream to read a word from a byte stream.
+ A word is the number of bytes that can be stored in the internal buffer
+ used by the bitstream.
+
+ @todo Need to modify the routine to return an indication of end of file
+ or an error reading from the byte stream.
+*/
+BITWORD GetWord(STREAM *stream)
+{
+ BITWORD buffer = 0;
+ size_t bytes_read = sizeof(buffer);
+
+ assert(stream != NULL);
+
+ switch (stream->type)
+ {
+ case STREAM_TYPE_FILE:
+ bytes_read = fread(&buffer, 1, sizeof(buffer), stream->location.file.iobuf);
+ assert(bytes_read == sizeof(buffer));
+ break;
+
+ case STREAM_TYPE_MEMORY:
+ memcpy(&buffer, (uint8_t *)stream->location.memory.buffer + stream->byte_count, sizeof(buffer));
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ if (bytes_read > 0)
+ stream->byte_count += sizeof(buffer);
+
+ return buffer;
+}
+
+/*!
+ @brief Read a byte from a byte stream
+*/
+uint8_t GetByte(STREAM *stream)
+{
+ assert(stream != NULL);
+ int byte = 0;
+
+ switch (stream->type)
+ {
+ case STREAM_TYPE_FILE:
+ byte = fgetc(stream->location.file.iobuf);
+ break;
+
+ case STREAM_TYPE_MEMORY:
+ byte = ((uint8_t *)stream->location.memory.buffer)[stream->byte_count];
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ stream->byte_count++;
+ assert(byte >= 0 && (byte & ~0xFF) == 0);
+
+ return (uint8_t)byte;
+}
+
+/*!
+ @brief Write a word to a byte stream
+
+ This routine is used by the bitstream to write a word to a byte stream.
+ A word is the number of bytes that can be stored in the internal buffer
+ used by the bitstream.
+
+ @todo Need to modify the routine to return an indication of an error
+ writing to the byte stream.
+*/
+CODEC_ERROR PutWord(STREAM *stream, BITWORD word)
+{
+ size_t written;
+
+ word = Swap32(word);
+
+ assert(stream != NULL);
+
+ switch (stream->type)
+ {
+ case STREAM_TYPE_FILE:
+ written = fwrite(&word, sizeof(word), 1, stream->location.file.iobuf);
+ if (written == 0)
+ return CODEC_ERROR_FILE_WRITE_FAILED;
+ break;
+
+ case STREAM_TYPE_MEMORY:
+ {
+ uint8_t* buffer = (uint8_t *)stream->location.memory.buffer + stream->byte_count;
+
+ memcpy(buffer, &word, sizeof(word));
+ }
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ stream->byte_count += sizeof(word);
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write a byte to a byte stream
+*/
+CODEC_ERROR PutByte(STREAM *stream, uint8_t byte)
+{
+ assert(stream != NULL);
+
+ //assert(byte >= 0 && (byte & ~0xFF) == 0);
+
+ switch (stream->type)
+ {
+ case STREAM_TYPE_FILE:
+ if (fputc(byte, stream->location.file.iobuf) == EOF)
+ return CODEC_ERROR_FILE_WRITE_FAILED;
+ break;
+
+ case STREAM_TYPE_MEMORY:
+ ((uint8_t *)stream->location.memory.buffer)[stream->byte_count] = byte;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ stream->byte_count++;
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Rewind the stream to the beginning of the buffer or file
+*/
+CODEC_ERROR RewindStream(STREAM *stream)
+{
+ assert(stream != NULL);
+ if (! (stream != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ if( stream->type == STREAM_TYPE_FILE )
+ {
+ if (stream->location.file.iobuf != NULL) {
+ assert(fseek(stream->location.file.iobuf, 0, SEEK_SET) == 0);
+ return CODEC_ERROR_BITSTREAM;
+ }
+ }
+
+ stream->byte_count = 0;
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Skip the specified number of bytes in the stream
+*/
+CODEC_ERROR SkipBytes(STREAM *stream, size_t size)
+{
+ for (; size > 0; size--)
+ {
+ (void)GetByte(stream);
+ }
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Pad the specified number of bytes in the stream
+*/
+CODEC_ERROR PadBytes(STREAM *stream, size_t size)
+{
+ const uint8_t byte = 0;
+ for (; size > 0; size--)
+ {
+ PutByte(stream, byte);
+ }
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the stream buffer to the file
+*/
+CODEC_ERROR FlushStream(STREAM *stream)
+{
+ assert(stream != NULL);
+ if (! (stream != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ if (stream->type == STREAM_TYPE_FILE)
+ {
+ int result = fflush(stream->location.file.iobuf);
+ if (result != 0) {
+ return CODEC_ERROR_FILE_FLUSH_FAILED;
+ }
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Create a byte stream for reading from a memory location
+ */
+CODEC_ERROR OpenStreamBuffer(STREAM *stream, void *buffer, size_t size)
+{
+ assert(stream != NULL);
+ if (! (stream != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ // Clear all members of the stream data structure
+ memset(stream, 0, sizeof(STREAM));
+
+ // Bind the stream to the buffer
+ stream->location.memory.buffer = buffer;
+ stream->location.memory.size = size;
+
+ // Set the stream type and access
+ stream->type = STREAM_TYPE_MEMORY;
+ stream->access = STREAM_ACCESS_READ;
+
+ // Clear the number of bytes written to the stream
+ stream->byte_count = 0;
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Create a byte stream for writing to a memory location
+*/
+CODEC_ERROR CreateStreamBuffer(STREAM *stream, void *buffer, size_t size)
+{
+ assert(stream != NULL);
+ if (! (stream != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ // Clear all members of the stream data structure
+ memset(stream, 0, sizeof(STREAM));
+
+ // Bind the stream to the buffer
+ stream->location.memory.buffer = buffer;
+ stream->location.memory.size = size;
+
+ // Set the stream type and access
+ stream->type = STREAM_TYPE_MEMORY;
+ stream->access = STREAM_ACCESS_WRITE;
+
+ // Clear the number of bytes written to the stream
+ stream->byte_count = 0;
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Return the starting address and number of bytes in a buffer
+
+ This routine is used to get the address and count of the bytes written
+ to a memory stream (buffer).
+*/
+CODEC_ERROR GetStreamBuffer(STREAM *stream, void **buffer_out, size_t *size_out)
+{
+ assert(stream != NULL);
+ if (! (stream != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ assert(stream->type == STREAM_TYPE_MEMORY);
+
+ if (buffer_out != NULL) {
+ *buffer_out = stream->location.memory.buffer;
+ }
+
+ if (size_out != NULL) {
+ *size_out = stream->byte_count;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Read a block of data at the specified offset in the byte stream
+*/
+CODEC_ERROR GetBlock(STREAM *stream, void *buffer, size_t size, size_t offset)
+{
+ switch (stream->type)
+ {
+ case STREAM_TYPE_FILE:
+ return GetBlockFile(stream, buffer, size, offset);
+ break;
+
+ case STREAM_TYPE_MEMORY:
+ return GetBlockMemory(stream, buffer, size, offset);
+ break;
+
+ case STREAM_TYPE_UNKNOWN:
+ assert(0);
+ break;
+ }
+
+ return CODEC_ERROR_UNEXPECTED;
+}
+
+/*!
+ @brief Read a block of data from a file stream
+*/
+CODEC_ERROR GetBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset)
+{
+ FILE *file = stream->location.file.iobuf;
+ fpos_t position;
+
+ (void)file;
+ (void)position;
+
+ // Save the current position in the file
+ if (fgetpos(file, &position) != 0) {
+ return CODEC_ERROR_FILE_GET_POSITION;
+ }
+
+ // Seek to the specified offset
+ assert(offset <= LONG_MAX);
+ if (fseek(file, (long)offset, SEEK_SET) != 0) {
+ return CODEC_ERROR_FILE_SEEK;
+ }
+
+ // Read data from the file
+ if (fread(buffer, size, 1, file) != 1) {
+ return CODEC_ERROR_FILE_READ;
+ }
+
+ // Return to the previous position in the file
+ // if (fseek(file, (long)position, SEEK_SET) != 0) {
+ if (fsetpos(file, &position) != 0) {
+ return CODEC_ERROR_FILE_SEEK;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Read a block of data from a memory stream (buffer)
+*/
+CODEC_ERROR GetBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset)
+{
+ uint8_t *block = (uint8_t *)stream->location.memory.buffer + offset;
+ memcpy(buffer, block, size);
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write a block of data at the specified offset in the byte stream
+*/
+CODEC_ERROR PutBlock(STREAM *stream, void *buffer, size_t size, size_t offset)
+{
+ switch (stream->type)
+ {
+ case STREAM_TYPE_FILE:
+ return PutBlockFile(stream, buffer, size, offset);
+ break;
+
+ case STREAM_TYPE_MEMORY:
+ return PutBlockMemory(stream, buffer, size, offset);
+ break;
+
+ case STREAM_TYPE_UNKNOWN:
+ assert(0);
+ break;
+ }
+
+ return CODEC_ERROR_UNEXPECTED;
+}
+
+/*!
+ @brief Write a block of data at the specified offset in a file stream
+*/
+CODEC_ERROR PutBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset)
+{
+ FILE *file = stream->location.file.iobuf;
+ fpos_t position;
+
+ (void)file;
+ (void)position;
+
+ // Save the current position in the file
+ if (fgetpos(file, &position) != 0) {
+ return CODEC_ERROR_FILE_GET_POSITION;
+ }
+
+ // Seek to the specified offset and write to the file
+ assert(offset <= LONG_MAX);
+ if (fseek(file, (long)offset, SEEK_SET) != 0) {
+ return CODEC_ERROR_FILE_SEEK;
+ }
+
+ // Write data to the file
+ if (fwrite(buffer, size, 1, file) != 1) {
+ return CODEC_ERROR_FILE_WRITE;
+ }
+
+ // Return to the previous position in the file
+ // if (fseek(file, (long)position, SEEK_SET) != 0) {
+ if (fsetpos(file, &position) != 0) {
+ return CODEC_ERROR_FILE_SEEK;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write a block of data at the specified offset in a memory stream
+*/
+CODEC_ERROR PutBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset)
+{
+ uint8_t *block = (uint8_t *)stream->location.memory.buffer + offset;
+ memcpy(block, buffer, size);
+ return CODEC_ERROR_OKAY;
+}
+
+