diff options
Diffstat (limited to 'src/lib/util/aviio.cpp')
-rw-r--r-- | src/lib/util/aviio.cpp | 3369 |
1 files changed, 1629 insertions, 1740 deletions
diff --git a/src/lib/util/aviio.cpp b/src/lib/util/aviio.cpp index a448128070b..8483adb24f6 100644 --- a/src/lib/util/aviio.cpp +++ b/src/lib/util/aviio.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** aviio.c @@ -8,8 +8,10 @@ ***************************************************************************/ -#include <stdlib.h> -#include <assert.h> +#include <array> +#include <cassert> +#include <cstdlib> +#include <cstring> #include "aviio.h" @@ -45,7 +47,7 @@ * @brief A macro that defines four gigabytes. */ -#define FOUR_GB ((UINT64)1 << 32) +#define FOUR_GB (std::uint64_t(1) << 32) /** * @def MAX_SOUND_CHANNELS @@ -222,7 +224,7 @@ #define HUFFYUV_PREDICT_DECORR 0x40 - +namespace { /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ @@ -235,14 +237,12 @@ struct avi_chunk { - /** @brief The offset. */ - UINT64 offset; /* file offset of chunk header */ - /** @brief The size. */ - UINT64 size; /* size of this chunk */ - /** @brief The type. */ - UINT32 type; /* type of this chunk */ - /** @brief The listtype. */ - UINT32 listtype; /* type of this list (if we are a list) */ + avi_chunk() : offset(0), size(0), type(0), listtype(0) { } + + std::uint64_t offset; /* file offset of chunk header */ + std::uint64_t size; /* size of this chunk */ + std::uint32_t type; /* type of this chunk */ + std::uint32_t listtype; /* type of this list (if we are a list) */ }; /** @@ -253,211 +253,297 @@ struct avi_chunk struct avi_chunk_list { - /** @brief The offset. */ - UINT64 offset; /* offset in the file of header */ - /** @brief The length. */ - UINT32 length; /* length of the chunk including header */ + std::uint64_t offset; /* offset in the file of header */ + std::uint32_t length; /* length of the chunk including header */ }; /** - * @struct huffyuv_table + * @struct avi_stream * - * @brief A huffyuv table. + * @brief An avi stream. */ -struct huffyuv_table -{ - /** @brief The shift[ 256]. */ - UINT8 shift[256]; /* bit shift amounts */ - /** @brief The bits[ 256]. */ - UINT32 bits[256]; /* bit match values */ - /** @brief The mask[ 256]. */ - UINT32 mask[256]; /* bit mask values */ - /** @brief The baselookup[ 65536]. */ - UINT16 baselookup[65536]; /* base lookup table */ - /** @brief The extralookup. */ - UINT16 * extralookup; /* extra lookup tables */ -}; +class avi_stream +{ +public: + avi_stream() + : m_type(0) + , m_format(0) + , m_rate(0) + , m_scale(0) + , m_samples(0) + , m_chunks() + , m_width(0) + , m_height(0) + , m_depth(0) + , m_interlace(0) + , m_huffyuv() + , m_channels(0) + , m_samplebits(0) + , m_samplerate(0) + , m_saved_strh_offset(0) + , m_saved_indx_offset(0) + { + } -/** - * @struct huffyuv_data - * - * @brief A huffyuv data. - */ + void initialize_video(avi_file::movie_info const &info) + { + m_type = STREAMTYPE_VIDS; + m_format = info.video_format; + m_rate = info.video_timescale; + m_scale = info.video_sampletime; + m_width = info.video_width; + m_height = info.video_height; + m_depth = info.video_depth; + } -struct huffyuv_data -{ - /** @brief The predictor. */ - UINT8 predictor; /* predictor */ - /** @brief The table[ 3]. */ - huffyuv_table table[3]; /* array of tables */ -}; + void initialize_audio(avi_file::movie_info const &info) + { + m_type = STREAMTYPE_AUDS; + m_format = info.audio_format; + m_rate = info.audio_timescale; + m_scale = info.audio_sampletime; + m_channels = info.audio_channels; + m_samplebits = info.audio_samplebits; + m_samplerate = info.audio_samplerate; + } -/** - * @struct avi_stream - * - * @brief An avi stream. - */ + std::uint32_t type() const { return m_type; } + std::uint32_t format() const { return m_format; } + + std::uint32_t rate() const { return m_rate; } + std::uint32_t scale() const { return m_scale; } + std::uint32_t samples() const { return m_samples; } + void set_samples(std::uint32_t value) { m_samples = value; } + std::uint32_t add_samples(std::uint32_t value) { return m_samples += value; } + + std::uint32_t chunks() const { return m_chunks.size(); } + avi_chunk_list const &chunk(std::uint32_t index) const { assert(index < m_chunks.size()); return m_chunks[index]; } + avi_chunk_list &chunk(std::uint32_t index) { assert(index < m_chunks.size()); return m_chunks[index]; } + avi_file::error set_chunk_info(std::uint32_t index, std::uint64_t offset, std::uint32_t length); + void set_chunks(std::uint32_t count) { assert(count <= m_chunks.size()); m_chunks.resize(count); } + + std::uint32_t width() const { return m_width; } + std::uint32_t height() const { return m_height; } + std::uint32_t depth() const { return m_depth; } + + std::uint16_t channels() const { return m_channels; } + std::uint16_t samplebits() const { return m_samplebits; } + std::uint32_t samplerate() const { return m_samplerate; } + std::uint32_t bytes_per_sample() const { return (m_samplebits / 8) * m_channels; } + + avi_file::error set_strh_data(std::uint8_t const *data, std::uint32_t size); + avi_file::error set_strf_data(std::uint8_t const *data, std::uint32_t size); + + std::uint64_t &saved_strh_offset() { return m_saved_strh_offset; } + std::uint64_t &saved_indx_offset() { return m_saved_indx_offset; } + + // RGB helpers + avi_file::error rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const; + + // YUY helpers + avi_file::error yuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const; + avi_file::error yuy16_compress_to_yuy(const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const; + + // HuffYUV helpers + avi_file::error huffyuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const; + +private: + struct huffyuv_table + { + std::uint8_t shift[256]; /* bit shift amounts */ + std::uint32_t bits[256]; /* bit match values */ + std::uint32_t mask[256]; /* bit mask values */ + std::uint16_t baselookup[65536]; /* base lookup table */ + std::vector<std::uint16_t> extralookup; /* extra lookup tables */ + }; + + struct huffyuv_data + { + std::uint8_t predictor; /* predictor */ + huffyuv_table table[3]; /* array of tables */ + }; -struct avi_stream -{ - /** @brief The type. */ - UINT32 type; /* subtype of stream */ - /** @brief Describes the format to use. */ - UINT32 format; /* format of stream data */ - - /** @brief The rate. */ - UINT32 rate; /* timescale for stream */ - /** @brief The scale. */ - UINT32 scale; /* duration of one sample in the stream */ - /** @brief The samples. */ - UINT32 samples; /* number of samples */ - - /** @brief The chunk. */ - avi_chunk_list * chunk; /* list of chunks */ - /** @brief The chunks. */ - UINT32 chunks; /* chunks currently known */ - /** @brief The chunksalloc. */ - UINT32 chunksalloc; /* number of chunks allocated */ - - /** @brief The width. */ - UINT32 width; /* width of video */ - /** @brief The height. */ - UINT32 height; /* height of video */ - /** @brief The depth. */ - UINT32 depth; /* depth of video */ - /** @brief The interlace. */ - UINT8 interlace; /* interlace parameters */ - /** @brief The huffyuv. */ - huffyuv_data * huffyuv; /* huffyuv decompression data */ - - /** @brief The channels. */ - UINT16 channels; /* audio channels */ - /** @brief The samplebits. */ - UINT16 samplebits; /* audio bits per sample */ - /** @brief The samplerate. */ - UINT32 samplerate; /* audio sample rate */ + avi_file::error huffyuv_extract_tables(const std::uint8_t *chunkdata, std::uint32_t size); + + std::uint32_t m_type; /* subtype of stream */ + std::uint32_t m_format; /* format of stream data */ + + std::uint32_t m_rate; /* timescale for stream */ + std::uint32_t m_scale; /* duration of one sample in the stream */ + std::uint32_t m_samples; /* number of samples */ + + std::vector<avi_chunk_list> m_chunks; /* list of chunks */ + + std::uint32_t m_width; /* width of video */ + std::uint32_t m_height; /* height of video */ + std::uint32_t m_depth; /* depth of video */ + std::uint8_t m_interlace; /* interlace parameters */ + std::unique_ptr<huffyuv_data const> m_huffyuv; /* huffyuv decompression data */ + + std::uint16_t m_channels; /* audio channels */ + std::uint16_t m_samplebits; /* audio bits per sample */ + std::uint32_t m_samplerate; /* audio sample rate */ /* only used when creating */ - /** @brief The saved strh offset. */ - UINT64 saved_strh_offset; /* writeoffset of strh chunk */ - /** @brief The saved indx offset. */ - UINT64 saved_indx_offset; /* writeoffset of indx chunk */ + std::uint64_t m_saved_strh_offset; /* writeoffset of strh chunk */ + std::uint64_t m_saved_indx_offset; /* writeoffset of indx chunk */ }; /** - * @struct avi_file + * @class avi_file_impl * * @brief An avi file. */ -struct avi_file +class avi_file_impl : public avi_file { - /* shared data */ - /** @brief The file. */ - osd_file * file; /* pointer to open file */ - /** @brief The type. */ - int type; /* type of access (read/create) */ - /** @brief The information. */ - avi_movie_info info; /* movie info structure */ - /** @brief The tempbuffer. */ - UINT8 * tempbuffer; /* temporary buffer */ - /** @brief The tempbuffersize. */ - UINT32 tempbuffersize; /* size of the temporary buffer */ - - /* only used when reading */ - /** @brief The streams. */ - int streams; /* number of streams */ - /** @brief The stream. */ - avi_stream * stream; /* allocated array of stream information */ - /** @brief The rootchunk. */ - avi_chunk rootchunk; /* dummy root chunk that wraps the whole file */ - - /* only used when creating */ - /** @brief The writeoffs. */ - UINT64 writeoffs; /* current file write offset */ - /** @brief The riffbase. */ - UINT64 riffbase; /* base of the current RIFF */ - - /** @brief The chunkstack[ 8]. */ - avi_chunk chunkstack[8]; /* stack of chunks we are writing */ - /** @brief The chunksp. */ - int chunksp; /* stack pointer for the current chunk */ - - /** @brief The saved movi offset. */ - UINT64 saved_movi_offset; /* writeoffset of movi list */ - /** @brief The saved avih offset. */ - UINT64 saved_avih_offset; /* writeoffset of avih chunk */ - - /** @brief The soundbuf. */ - INT16 * soundbuf; /* buffer for sound data */ - /** @brief The soundbuf samples. */ - UINT32 soundbuf_samples; /* length of sound buffer in samples */ - /** @brief The soundbuf chansamples[ maximum sound channels]. */ - UINT32 soundbuf_chansamples[MAX_SOUND_CHANNELS]; /* samples in buffer for each channel */ - /** @brief The soundbuf chunks. */ - UINT32 soundbuf_chunks; /* number of chunks completed so far */ - /** @brief The soundbuf frames. */ - UINT32 soundbuf_frames; /* number of frames ahead of the video */ -}; - +public: + avi_file_impl(osd_file::ptr &&file, std::uint64_t length) : avi_file_impl() + { + m_file = std::move(file); + m_type = FILETYPE_READ; + + // make a root atom + m_rootchunk.offset = 0; + m_rootchunk.size = length; + m_rootchunk.type = 0; + m_rootchunk.listtype = 0; + } + avi_file_impl(osd_file::ptr &&file, movie_info const &info) : avi_file_impl() + { + m_file = std::move(file); + m_type = FILETYPE_CREATE; -/*************************************************************************** - PROTOTYPES -***************************************************************************/ + // copy the movie info + m_info = info; + m_info.video_numsamples = 0; + m_info.audio_numsamples = 0; -/* stream helpers */ + // initialize the video track + m_streams.emplace_back(); + m_streams.back().initialize_video(m_info); -/* core chunk read routines */ -static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk); -static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk); -static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result); -static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result); -static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset); -static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer); + // initialize the audio track + if (m_info.audio_channels > 0) + { + m_streams.emplace_back(); + m_streams.back().initialize_audio(m_info); + } + } -/* chunk read helpers */ -static avi_error read_movie_data(avi_file *file); -static avi_error extract_movie_info(avi_file *file); -static avi_error parse_avih_chunk(avi_file *file, avi_chunk *avih); -static avi_error parse_strh_chunk(avi_file *file, avi_stream *stream, avi_chunk *strh); -static avi_error parse_strf_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf); -static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf); -static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1); + virtual ~avi_file_impl() override; + + virtual void printf_chunks() override; + + virtual movie_info const &get_movie_info() const override; + virtual std::uint32_t first_sample_in_frame(std::uint32_t framenum) const override; + + virtual error read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) override; + virtual error read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) override; + + virtual error append_video_frame(bitmap_yuy16 &bitmap) override; + virtual error append_video_frame(bitmap_rgb32 &bitmap) override; + virtual error append_sound_samples(int channel, std::int16_t const *samples, std::uint32_t numsamples, std::uint32_t sampleskip) override; + + error read_movie_data(); + error write_initial_headers(); + error soundbuf_initialize(); + +private: + avi_file_impl() + : m_file() + , m_type(0) + , m_info({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) + , m_tempbuffer() + , m_streams() + , m_rootchunk() + , m_writeoffs(0) + , m_riffbase(0) + , m_chunkstack() + , m_chunksp(0) + , m_saved_movi_offset(0) + , m_saved_avih_offset(0) + , m_soundbuf() + , m_soundbuf_samples(0) + , m_soundbuf_chansamples{ 0 } + , m_soundbuf_chunks(0) + , m_soundbuf_frames(0) + { + } -/* core chunk write routines */ -static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength); -static avi_error chunk_close(avi_file *file); -static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length); -static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write); + avi_stream *get_video_stream(); + avi_stream *get_audio_stream(int channel, int &offset); + std::uint32_t compute_idx1_size() const; + std::uint32_t get_chunkid_for_stream(const avi_stream *stream) const; + std::uint32_t framenum_to_samplenum(std::uint32_t framenum) const; + error expand_tempbuffer(std::uint32_t length); + + // core chunk read routines + error get_first_chunk(avi_chunk const *parent, avi_chunk &newchunk); + error get_next_chunk(avi_chunk const *parent, avi_chunk &newchunk); + error find_first_chunk(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error find_next_chunk(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error find_first_list(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error find_next_list(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error get_next_chunk_internal(avi_chunk const *parent, avi_chunk &newchunk, std::uint64_t offset); + error read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer); + + // chunk read helpers + error extract_movie_info(); + error parse_avih_chunk(avi_chunk const &avih); + error parse_strh_chunk(avi_stream &stream, avi_chunk const &strh); + error parse_strf_chunk(avi_stream &stream, avi_chunk const &strf); + error parse_indx_chunk(avi_stream &stream, avi_chunk const &strf); + error parse_idx1_chunk(std::uint64_t baseoffset, avi_chunk const &idx1); + + // core chunk write routines + error chunk_open(std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength); + error chunk_close(); + error chunk_write(std::uint32_t type, const void *data, std::uint32_t length); + error chunk_overwrite(std::uint32_t type, const void *data, std::uint32_t length, std::uint64_t &offset, bool initial_write); + + // chunk write helpers + error write_avih_chunk(bool initial_write); + error write_strh_chunk(avi_stream &stream, bool initial_write); + error write_strf_chunk(avi_stream const &stream); + error write_indx_chunk(avi_stream &stream, bool initial_write); + error write_idx1_chunk(); + + // sound buffering helpers + error soundbuf_write_chunk(std::uint32_t framenum); + error soundbuf_flush(bool only_flush_full); + + // debugging + void printf_chunk_recursive(avi_chunk const *chunk, int indent); -/* chunk write helpers */ -static avi_error write_initial_headers(avi_file *file); -static avi_error write_avih_chunk(avi_file *file, int initial_write); -static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initial_write); -static avi_error write_strf_chunk(avi_file *file, avi_stream *stream); -static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initial_write); -static avi_error write_idx1_chunk(avi_file *file); + /* shared data */ + osd_file::ptr m_file; /* pointer to open file */ + int m_type; /* type of access (read/create) */ + movie_info m_info; /* movie info structure */ + std::vector<std::uint8_t> m_tempbuffer; /* temporary buffer */ -/* sound buffering helpers */ -static avi_error soundbuf_initialize(avi_file *file); -static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum); -static avi_error soundbuf_flush(avi_file *file, int only_flush_full); + /* only used when reading */ + std::vector<avi_stream> m_streams; /* allocated array of stream information */ + avi_chunk m_rootchunk; /* dummy root chunk that wraps the whole file */ -/* RGB helpers */ -static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, UINT8 *data, UINT32 numbytes); + /* only used when creating */ + std::uint64_t m_writeoffs; /* current file write offset */ + std::uint64_t m_riffbase; /* base of the current RIFF */ -/* YUY helpers */ -static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap); -static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, UINT8 *data, UINT32 numbytes); + std::array<avi_chunk, 8> m_chunkstack; /* stack of chunks we are writing */ + int m_chunksp; /* stack pointer for the current chunk */ -/* HuffYUV helpers */ -static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size); -static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap); + std::uint64_t m_saved_movi_offset; /* writeoffset of movi list */ + std::uint64_t m_saved_avih_offset; /* writeoffset of avih chunk */ -/* debugging */ -static void printf_chunk_recursive(avi_file *file, avi_chunk *chunk, int indent); + std::vector<std::int16_t> m_soundbuf; /* buffer for sound data */ + std::uint32_t m_soundbuf_samples; /* length of sound buffer in samples */ + std::uint32_t m_soundbuf_chansamples[MAX_SOUND_CHANNELS]; /* samples in buffer for each channel */ + std::uint32_t m_soundbuf_chunks; /* number of chunks completed so far */ + std::uint32_t m_soundbuf_frames; /* number of frames ahead of the video */ +}; @@ -471,7 +557,7 @@ static void printf_chunk_recursive(avi_file *file, avi_chunk *chunk, int indent) -------------------------------------------------*/ /** - * @fn static inline UINT16 fetch_16bits(const UINT8 *data) + * @fn static inline std::uint16_t fetch_16bits(const std::uint8_t *data) * * @brief Fetches the 16bits. * @@ -480,7 +566,7 @@ static void printf_chunk_recursive(avi_file *file, avi_chunk *chunk, int indent) * @return The 16bits. */ -static inline UINT16 fetch_16bits(const UINT8 *data) +inline std::uint16_t fetch_16bits(const std::uint8_t *data) { return data[0] | (data[1] << 8); } @@ -492,7 +578,7 @@ static inline UINT16 fetch_16bits(const UINT8 *data) -------------------------------------------------*/ /** - * @fn static inline UINT32 fetch_32bits(const UINT8 *data) + * @fn static inline std::uint32_t fetch_32bits(const std::uint8_t *data) * * @brief Fetches the 32bits. * @@ -501,7 +587,7 @@ static inline UINT16 fetch_16bits(const UINT8 *data) * @return The 32bits. */ -static inline UINT32 fetch_32bits(const UINT8 *data) +inline std::uint32_t fetch_32bits(const std::uint8_t *data) { return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); } @@ -513,7 +599,7 @@ static inline UINT32 fetch_32bits(const UINT8 *data) -------------------------------------------------*/ /** - * @fn static inline UINT64 fetch_64bits(const UINT8 *data) + * @fn static inline std::uint64_t fetch_64bits(const std::uint8_t *data) * * @brief Fetches the 64bits. * @@ -522,12 +608,12 @@ static inline UINT32 fetch_32bits(const UINT8 *data) * @return The 64bits. */ -static inline UINT64 fetch_64bits(const UINT8 *data) +inline std::uint64_t fetch_64bits(const std::uint8_t *data) { - return (UINT64)data[0] | ((UINT64)data[1] << 8) | - ((UINT64)data[2] << 16) | ((UINT64)data[3] << 24) | - ((UINT64)data[4] << 32) | ((UINT64)data[5] << 40) | - ((UINT64)data[6] << 48) | ((UINT64)data[7] << 56); + return std::uint64_t(data[0]) | (std::uint64_t(data[1]) << 8) | + (std::uint64_t(data[2]) << 16) | (std::uint64_t(data[3]) << 24) | + (std::uint64_t(data[4]) << 32) | (std::uint64_t(data[5]) << 40) | + (std::uint64_t(data[6]) << 48) | (std::uint64_t(data[7]) << 56); } @@ -537,7 +623,7 @@ static inline UINT64 fetch_64bits(const UINT8 *data) -------------------------------------------------*/ /** - * @fn static inline void put_16bits(UINT8 *data, UINT16 value) + * @fn static inline void put_16bits(std::uint8_t *data, std::uint16_t value) * * @brief Puts the 16bits. * @@ -545,7 +631,7 @@ static inline UINT64 fetch_64bits(const UINT8 *data) * @param value The value. */ -static inline void put_16bits(UINT8 *data, UINT16 value) +inline void put_16bits(std::uint8_t *data, std::uint16_t value) { data[0] = value >> 0; data[1] = value >> 8; @@ -558,7 +644,7 @@ static inline void put_16bits(UINT8 *data, UINT16 value) -------------------------------------------------*/ /** - * @fn static inline void put_32bits(UINT8 *data, UINT32 value) + * @fn static inline void put_32bits(std::uint8_t *data, std::uint32_t value) * * @brief Puts the 32bits. * @@ -566,7 +652,7 @@ static inline void put_16bits(UINT8 *data, UINT16 value) * @param value The value. */ -static inline void put_32bits(UINT8 *data, UINT32 value) +inline void put_32bits(std::uint8_t *data, std::uint32_t value) { data[0] = value >> 0; data[1] = value >> 8; @@ -581,7 +667,7 @@ static inline void put_32bits(UINT8 *data, UINT32 value) -------------------------------------------------*/ /** - * @fn static inline void put_64bits(UINT8 *data, UINT64 value) + * @fn static inline void put_64bits(std::uint8_t *data, std::uint64_t value) * * @brief Puts the 64bits. * @@ -589,7 +675,7 @@ static inline void put_32bits(UINT8 *data, UINT32 value) * @param value The value. */ -static inline void put_64bits(UINT8 *data, UINT64 value) +inline void put_64bits(std::uint8_t *data, std::uint64_t value) { data[0] = value >> 0; data[1] = value >> 8; @@ -602,29 +688,120 @@ static inline void put_64bits(UINT8 *data, UINT64 value) } +/** + * @fn static void u64toa(std::uint64_t val, char *output) + * + * @brief 64toas. + * + * @param val The value. + * @param [in,out] output If non-null, the output. + */ + +inline void u64toa(std::uint64_t val, char *output) +{ + std::uint32_t lo = std::uint32_t(val & 0xffffffff); + std::uint32_t hi = std::uint32_t(val >> 32); + if (hi != 0) + sprintf(output, "%X%08X", hi, lo); + else + sprintf(output, "%X", lo); +} + + +/*------------------------------------------------- + set_stream_chunk_info - set the chunk info + for a given chunk within a stream +-------------------------------------------------*/ + +/** + * @fn static inline avi_error set_stream_chunk_info(avi_stream *stream, std::uint32_t index, std::uint64_t offset, std::uint32_t length) + * + * @brief Sets stream chunk information. + * + * @param [in,out] stream If non-null, the stream. + * @param index Zero-based index of the. + * @param offset The offset. + * @param length The length. + * + * @return An avi_error. + */ + +inline avi_file::error avi_stream::set_chunk_info(std::uint32_t index, std::uint64_t offset, std::uint32_t length) +{ + /* if we need to allocate more, allocate more */ + if (index >= m_chunks.capacity()) + { + try { m_chunks.reserve((std::max<std::size_t>)(index, m_chunks.capacity() + 1000)); } + catch (...) { return avi_file::error::NO_MEMORY; } + } + + /* update the number of chunks */ + m_chunks.resize((std::max<std::size_t>)(m_chunks.size(), index + 1)); + + /* set the data */ + m_chunks[index].offset = offset; + m_chunks[index].length = length; + + return avi_file::error::NONE; +} + + +inline avi_file::error avi_stream::set_strh_data(std::uint8_t const *data, std::uint32_t size) +{ + m_type = fetch_32bits(&data[0]); + m_scale = fetch_32bits(&data[20]); + m_rate = fetch_32bits(&data[24]); + m_samples = fetch_32bits(&data[32]); + return avi_file::error::NONE; +} + + +inline avi_file::error avi_stream::set_strf_data(std::uint8_t const *data, std::uint32_t size) +{ + /* audio and video streams have differing headers */ + if (m_type == STREAMTYPE_VIDS) + { + m_width = fetch_32bits(&data[4]); + m_height = fetch_32bits(&data[8]); + m_depth = fetch_16bits(&data[14]); + m_format = fetch_32bits(&data[16]); + + /* extra extraction for HuffYUV data */ + if ((m_format == FORMAT_HFYU) && (size >= 56)) + { + avi_file::error const avierr = huffyuv_extract_tables(data, size); + if (avierr != avi_file::error::NONE) + return avierr; + } + } + else if (m_type == STREAMTYPE_AUDS) + { + m_channels = fetch_16bits(&data[2]); + m_samplebits = fetch_16bits(&data[14]); + m_samplerate = fetch_32bits(&data[4]); + } + return avi_file::error::NONE; +} + + /*------------------------------------------------- get_video_stream - return a pointer to the video stream -------------------------------------------------*/ /** - * @fn static inline avi_stream *get_video_stream(avi_file *file) + * @fn static inline avi_stream *get_video_stream() * * @brief Gets video stream. * - * @param [in,out] file If non-null, the file. - * * @return null if it fails, else the video stream. */ -static inline avi_stream *get_video_stream(avi_file *file) +inline avi_stream *avi_file_impl::get_video_stream() { - int streamnum; - - /* find the video stream */ - for (streamnum = 0; streamnum < file->streams; streamnum++) - if (file->stream[streamnum].type == STREAMTYPE_VIDS) - return &file->stream[streamnum]; + for (int streamnum = 0; streamnum < m_streams.size(); streamnum++) + if (m_streams[streamnum].type() == STREAMTYPE_VIDS) + return &m_streams[streamnum]; return nullptr; } @@ -647,21 +824,18 @@ static inline avi_stream *get_video_stream(avi_file *file) * @return null if it fails, else the audio stream. */ -static inline avi_stream *get_audio_stream(avi_file *file, int channel, int *offset) +inline avi_stream *avi_file_impl::get_audio_stream(int channel, int &offset) { - int streamnum; - /* find the audios stream */ - for (streamnum = 0; streamnum < file->streams; streamnum++) - if (file->stream[streamnum].type == STREAMTYPE_AUDS) + for (int streamnum = 0; streamnum < m_streams.size(); streamnum++) + if (m_streams[streamnum].type() == STREAMTYPE_AUDS) { - if (channel < file->stream[streamnum].channels) + if (channel < m_streams[streamnum].channels()) { - if (offset != nullptr) - *offset = channel; - return &file->stream[streamnum]; + offset = channel; + return &m_streams[streamnum]; } - channel -= file->stream[streamnum].channels; + channel -= m_streams[streamnum].channels(); } return nullptr; @@ -669,58 +843,12 @@ static inline avi_stream *get_audio_stream(avi_file *file, int channel, int *off /*------------------------------------------------- - set_stream_chunk_info - set the chunk info - for a given chunk within a stream --------------------------------------------------*/ - -/** - * @fn static inline avi_error set_stream_chunk_info(avi_stream *stream, UINT32 index, UINT64 offset, UINT32 length) - * - * @brief Sets stream chunk information. - * - * @param [in,out] stream If non-null, the stream. - * @param index Zero-based index of the. - * @param offset The offset. - * @param length The length. - * - * @return An avi_error. - */ - -static inline avi_error set_stream_chunk_info(avi_stream *stream, UINT32 index, UINT64 offset, UINT32 length) -{ - /* if we need to allocate more, allocate more */ - if (index >= stream->chunksalloc) - { - UINT32 newcount = MAX(index, stream->chunksalloc + 1000); - avi_chunk_list *newchunks = (avi_chunk_list *)malloc(newcount * sizeof(stream->chunk[0])); - if (newchunks == nullptr) - return AVIERR_NO_MEMORY; - if (stream->chunk != nullptr) - { - memcpy(newchunks, stream->chunk, stream->chunksalloc * sizeof(stream->chunk[0])); - free(stream->chunk); - } - stream->chunk = newchunks; - stream->chunksalloc = newcount; - } - - /* set the data */ - stream->chunk[index].offset = offset; - stream->chunk[index].length = length; - - /* update the number of chunks */ - stream->chunks = MAX(stream->chunks, index + 1); - return AVIERR_NONE; -} - - -/*------------------------------------------------- compute_idx1_size - compute the size of the idx1 chunk -------------------------------------------------*/ /** - * @fn static inline UINT32 compute_idx1_size(avi_file *file) + * @fn static inline std::uint32_t compute_idx1_size(avi_file *file) * * @brief Calculates the index 1 size. * @@ -729,14 +857,13 @@ static inline avi_error set_stream_chunk_info(avi_stream *stream, UINT32 index, * @return The calculated index 1 size. */ -static inline UINT32 compute_idx1_size(avi_file *file) +inline std::uint32_t avi_file_impl::compute_idx1_size() const { int chunks = 0; - int strnum; /* count chunks in streams */ - for (strnum = 0; strnum < file->streams; strnum++) - chunks += file->stream[strnum].chunks; + for (int strnum = 0; strnum < m_streams.size(); strnum++) + chunks += m_streams[strnum].chunks(); return chunks * 16 + 8; } @@ -748,7 +875,7 @@ static inline UINT32 compute_idx1_size(avi_file *file) -------------------------------------------------*/ /** - * @fn static inline UINT32 get_chunkid_for_stream(avi_file *file, avi_stream *stream) + * @fn static inline std::uint32_t get_chunkid_for_stream(avi_file *file, avi_stream *stream) * * @brief Gets chunkid for stream. * @@ -758,14 +885,14 @@ static inline UINT32 compute_idx1_size(avi_file *file) * @return The chunkid for stream. */ -static inline UINT32 get_chunkid_for_stream(avi_file *file, avi_stream *stream) +inline std::uint32_t avi_file_impl::get_chunkid_for_stream(const avi_stream *stream) const { - UINT32 chunkid; + std::uint32_t chunkid; - chunkid = AVI_FOURCC('0' + (stream - file->stream) / 10, '0' + (stream - file->stream) % 10, 0, 0); - if (stream->type == STREAMTYPE_VIDS) - chunkid |= (stream->format == 0) ? CHUNKTYPE_XXDB : CHUNKTYPE_XXDC; - else if (stream->type == STREAMTYPE_AUDS) + chunkid = AVI_FOURCC('0' + (stream - &m_streams[0]) / 10, '0' + (stream - &m_streams[0]) % 10, 0, 0); + if (stream->type() == STREAMTYPE_VIDS) + chunkid |= (stream->format() == 0) ? CHUNKTYPE_XXDB : CHUNKTYPE_XXDC; + else if (stream->type() == STREAMTYPE_AUDS) chunkid |= CHUNKTYPE_XXWB; return chunkid; @@ -778,19 +905,19 @@ static inline UINT32 get_chunkid_for_stream(avi_file *file, avi_stream *stream) -------------------------------------------------*/ /** - * @fn static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) + * @fn static inline std::uint32_t framenum_to_samplenum(avi_file *file, std::uint32_t framenum) * * @brief Framenum to samplenum. * * @param [in,out] file If non-null, the file. * @param framenum The framenum. * - * @return An UINT32. + * @return An std::uint32_t. */ -static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) +inline std::uint32_t avi_file_impl::framenum_to_samplenum(std::uint32_t framenum) const { - return ((UINT64)file->info.audio_samplerate * (UINT64)framenum * (UINT64)file->info.video_sampletime + file->info.video_timescale - 1) / (UINT64)file->info.video_timescale; + return (std::uint64_t(m_info.audio_samplerate) * std::uint64_t(framenum) * std::uint64_t(m_info.video_sampletime) + m_info.video_timescale - 1) / std::uint64_t(m_info.video_timescale); } @@ -801,7 +928,7 @@ static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) -------------------------------------------------*/ /** - * @fn static inline avi_error expand_tempbuffer(avi_file *file, UINT32 length) + * @fn static inline avi_error expand_tempbuffer(avi_file *file, std::uint32_t length) * * @brief Expand tempbuffer. * @@ -811,207 +938,532 @@ static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) * @return An avi_error. */ -static inline avi_error expand_tempbuffer(avi_file *file, UINT32 length) +inline avi_file::error avi_file_impl::expand_tempbuffer(std::uint32_t length) { /* expand the tempbuffer to hold the data if necessary */ - if (length > file->tempbuffersize) + if (length > m_tempbuffer.size()) { - file->tempbuffersize = 2 * length; - UINT8 *newbuffer = (UINT8 *)malloc(file->tempbuffersize); - if (newbuffer == nullptr) - return AVIERR_NO_MEMORY; - if (file->tempbuffer != nullptr) - free(file->tempbuffer); - file->tempbuffer = newbuffer; + try { m_tempbuffer.resize(2 * length); } + catch (...) { return error::NO_MEMORY; } } - return AVIERR_NONE; + return error::NONE; } +/*------------------------------------------------- + rgb32_compress_to_rgb - "compress" an RGB32 + bitmap to an RGB encoded frame +-------------------------------------------------*/ + +/** + * @fn static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) + * + * @brief RGB 32 compress to RGB. + * + * @param [in,out] stream If non-null, the stream. + * @param bitmap The bitmap. + * @param [in,out] data If non-null, the data. + * @param numbytes The numbytes. + * + * @return An avi_error. + */ + +avi_file::error avi_stream::rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const +{ + int const height = (std::min<int>)(m_height, bitmap.height()); + int const width = (std::min<int>)(m_width, bitmap.width()); + std::uint8_t *const dataend = data + numbytes; + int x, y; + + /* compressed video */ + for (y = 0; y < height; y++) + { + const std::uint32_t *source = &bitmap.pix32(y); + std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3; + + for (x = 0; x < width && dest < dataend; x++) + { + rgb_t pix = *source++; + *dest++ = pix.b(); + *dest++ = pix.g(); + *dest++ = pix.r(); + } + + /* fill in any blank space on the right */ + for ( ; x < m_width && dest < dataend; x++) + { + *dest++ = 0; + *dest++ = 0; + *dest++ = 0; + } + } + + /* fill in any blank space on the bottom */ + for ( ; y < m_height; y++) + { + std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3; + for (x = 0; x < m_width && dest < dataend; x++) + { + *dest++ = 0; + *dest++ = 0; + *dest++ = 0; + } + } + + return avi_file::error::NONE; +} -/*************************************************************************** - IMPLEMENTATION -***************************************************************************/ /*------------------------------------------------- - avi_open - open an AVI movie file for read + yuv_decompress_to_yuy16 - decompress a YUV + encoded frame to a YUY16 bitmap -------------------------------------------------*/ /** - * @fn avi_error avi_open(const char *filename, avi_file **file) + * @fn static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) * - * @brief Queries if a given avi open. + * @brief Yuv decompress to yuy 16. * - * @param filename Filename of the file. - * @param [in,out] file If non-null, the file. + * @param [in,out] stream If non-null, the stream. + * @param data The data. + * @param numbytes The numbytes. + * @param [in,out] bitmap The bitmap. * * @return An avi_error. */ -avi_error avi_open(const char *filename, avi_file **file) +avi_file::error avi_stream::yuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const { - avi_file *newfile = nullptr; - file_error filerr; - avi_error avierr; - UINT64 length; - - /* allocate the file */ - newfile = (avi_file *)malloc(sizeof(*newfile)); - if (newfile == nullptr) - return AVIERR_NO_MEMORY; - memset(newfile, 0, sizeof(*newfile)); - newfile->type = FILETYPE_READ; + std::uint16_t const *const dataend = reinterpret_cast<const std::uint16_t *>(data + numbytes); + int x, y; - /* open the file */ - filerr = osd_open(filename, OPEN_FLAG_READ, &newfile->file, &length); - if (filerr != FILERR_NONE) + /* compressed video */ + for (y = 0; y < m_height; y++) { - avierr = AVIERR_CANT_OPEN_FILE; - goto error; + const std::uint16_t *source = reinterpret_cast<const std::uint16_t *>(data) + y * m_width; + std::uint16_t *dest = &bitmap.pix16(y); + + /* switch off the compression */ + switch (m_format) + { + case FORMAT_UYVY: + for (x = 0; x < m_width && source < dataend; x++) + *dest++ = *source++; + break; + + case FORMAT_VYUY: + case FORMAT_YUY2: + for (x = 0; x < m_width && source < dataend; x++) + { + std::uint16_t pix = *source++; + *dest++ = (pix >> 8) | (pix << 8); + } + break; + } } - /* make a root atom */ - newfile->rootchunk.offset = 0; - newfile->rootchunk.size = length; - newfile->rootchunk.type = 0; - newfile->rootchunk.listtype = 0; + return avi_file::error::NONE; +} - /* parse the data */ - avierr = read_movie_data(newfile); - if (avierr != AVIERR_NONE) - goto error; - *file = newfile; - return AVIERR_NONE; +/*------------------------------------------------- + yuy16_compress_to_yuy - "compress" a YUY16 + bitmap to a YUV encoded frame +-------------------------------------------------*/ -error: - /* clean up after an error */ - if (newfile != nullptr) +/** + * @fn static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) + * + * @brief Yuy 16 compress to yuy. + * + * @param [in,out] stream If non-null, the stream. + * @param bitmap The bitmap. + * @param [in,out] data If non-null, the data. + * @param numbytes The numbytes. + * + * @return An avi_error. + */ + +avi_file::error avi_stream::yuy16_compress_to_yuy(const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const +{ + std::uint16_t *const dataend = reinterpret_cast<std::uint16_t *>(data + numbytes); + int x, y; + + /* compressed video */ + for (y = 0; y < m_height; y++) { - if (newfile->file != nullptr) - osd_close(newfile->file); - free(newfile); + const std::uint16_t *source = &bitmap.pix16(y); + std::uint16_t *dest = reinterpret_cast<std::uint16_t *>(data) + y * m_width; + + /* switch off the compression */ + switch (m_format) + { + case FORMAT_UYVY: + for (x = 0; x < m_width && dest < dataend; x++) + *dest++ = *source++; + break; + + case FORMAT_VYUY: + case FORMAT_YUY2: + for (x = 0; x < m_width && dest < dataend; x++) + { + std::uint16_t pix = *source++; + *dest++ = (pix >> 8) | (pix << 8); + } + break; + } } - return avierr; + + return avi_file::error::NONE; } /*------------------------------------------------- - avi_create - create a new AVI movie file + huffyuv_extract_tables - extract HuffYUV + tables -------------------------------------------------*/ /** - * @fn avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file) + * @fn static avi_error huffyuv_extract_tables(avi_stream *stream, const std::uint8_t *chunkdata, std::uint32_t size) * - * @brief Avi create. + * @brief Huffyuv extract tables. * - * @param filename Filename of the file. - * @param info The information. - * @param [in,out] file If non-null, the file. + * @param [in,out] stream If non-null, the stream. + * @param chunkdata The chunkdata. + * @param size The size. * * @return An avi_error. */ -avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file) +avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata, std::uint32_t size) { - avi_file *newfile = nullptr; - file_error filerr; - avi_stream *stream; - avi_error avierr; - UINT64 length; + const std::uint8_t *const chunkend = chunkdata + size; - /* validate video info */ - if ((info->video_format != 0 && info->video_format != FORMAT_UYVY && info->video_format != FORMAT_VYUY && info->video_format != FORMAT_YUY2) || - info->video_width == 0 || - info->video_height == 0 || - info->video_depth == 0 || info->video_depth % 8 != 0) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + /* allocate memory for the data */ + std::unique_ptr<huffyuv_data> huffyuv; + try { huffyuv = std::make_unique<huffyuv_data>(); } + catch (...) { return avi_file::error::NO_MEMORY; } - /* validate audio info */ - if (info->audio_format != 0 || - info->audio_channels > MAX_SOUND_CHANNELS || - info->audio_samplebits != 16) - return AVIERR_UNSUPPORTED_AUDIO_FORMAT; + /* extract predictor information */ + if (&chunkdata[40] >= chunkend) + return avi_file::error::INVALID_DATA; + huffyuv->predictor = chunkdata[40]; - /* allocate the file */ - newfile = (avi_file *)malloc(sizeof(*newfile)); - if (newfile == nullptr) - return AVIERR_NO_MEMORY; - memset(newfile, 0, sizeof(*newfile)); - newfile->type = FILETYPE_CREATE; + /* make sure it's the left predictor */ + if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) != HUFFYUV_PREDICT_LEFT) + return avi_file::error::UNSUPPORTED_VIDEO_FORMAT; - /* open the file */ - filerr = osd_open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &newfile->file, &length); - if (filerr != FILERR_NONE) + /* make sure it's 16bpp YUV data */ + if (chunkdata[41] != 16) + return avi_file::error::UNSUPPORTED_VIDEO_FORMAT; + chunkdata += 44; + + /* loop over tables */ + for (int tabnum = 0; tabnum < 3; tabnum++) { - avierr = AVIERR_CANT_OPEN_FILE; - goto error; + huffyuv_table &table = huffyuv->table[tabnum]; + std::uint32_t curbits, bitadd; + std::uint16_t bitsat16 = 0; + int offset = 0, bits; + + /* loop until we populate the whole table */ + while (offset < 256) + { + int data, shift, count, i; + + /* extract the next run */ + if (chunkdata >= chunkend) + return avi_file::error::INVALID_DATA; + data = *chunkdata++; + shift = data & 0x1f; + count = data >> 5; + + /* zero count means next whole byte is a count */ + if (count == 0) + { + if (chunkdata >= chunkend) + return avi_file::error::INVALID_DATA; + count = *chunkdata++; + } + for (i = 0; i < count; i++) + table.shift[offset++] = shift; + } + + /* now determine bit patterns and masks */ + curbits = 0; + for (bits = 31; bits >= 0; bits--) + { + bitadd = 1 << (32 - bits); + + /* make sure we've cleared out all the bits below */ + if ((curbits & (bitadd - 1)) != 0) + return avi_file::error::INVALID_DATA; + + /* find all entries with this shift count and assign them */ + for (offset = 0; offset < 256; offset++) + if (table.shift[offset] == bits) + { + table.bits[offset] = curbits; + table.mask[offset] = ~(bitadd - 1); + curbits += bitadd; + } + + /* remember the bit pattern when we complete all the 17-bit codes */ + if (bits == 17) + bitsat16 = curbits >> 16; + } + + /* allocate the number of extra lookup tables we need */ + if (bitsat16 > 0) + { + try { table.extralookup.resize(bitsat16 * 65536); } + catch (...) { return avi_file::error::NO_MEMORY; } + for (offset = 0; offset < bitsat16; offset++) + table.baselookup[offset] = (offset << 8) | 0; + } + + /* then create lookup tables */ + for (offset = 0; offset < 256; offset++) + if (table.shift[offset] > 16) + { + std::uint16_t *tablebase = &table.extralookup[(table.bits[offset] >> 16) * 65536]; + std::uint32_t start = table.bits[offset] & 0xffff; + std::uint32_t end = start + ((1 << (32 - table.shift[offset])) - 1); + while (start <= end) + tablebase[start++] = (offset << 8) | (table.shift[offset] - 16); + } + else if (table.shift[offset] > 0) + { + std::uint32_t start = table.bits[offset] >> 16; + std::uint32_t end = start + ((1 << (16 - table.shift[offset])) - 1); + while (start <= end) + table.baselookup[start++] = (offset << 8) | table.shift[offset]; + } } - /* copy the movie info */ - newfile->info = *info; - newfile->info.video_numsamples = 0; - newfile->info.audio_numsamples = 0; + m_huffyuv = std::move(huffyuv); + return avi_file::error::NONE; +} + - /* allocate two streams */ - newfile->stream = (avi_stream *)malloc(2 * sizeof(newfile->stream[0])); - if (newfile->stream == nullptr) +/*------------------------------------------------- + huffyuv_decompress_to_yuy16 - decompress a + HuffYUV-encoded frame to a YUY16 bitmap +-------------------------------------------------*/ + +/** + * @fn static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) + * + * @brief Huffyuv decompress to yuy 16. + * + * @param [in,out] stream If non-null, the stream. + * @param data The data. + * @param numbytes The numbytes. + * @param [in,out] bitmap The bitmap. + * + * @return An avi_error. + */ + +avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const +{ + int prevlines = (m_height > 288) ? 2 : 1; + std::uint8_t lastprevy = 0, lastprevcb = 0, lastprevcr = 0; + std::uint8_t lasty = 0, lastcb = 0, lastcr = 0; + std::uint8_t bitsinbuffer = 0; + std::uint32_t bitbuffer = 0; + std::uint32_t dataoffs = 0; + int x, y; + + /* compressed video */ + for (y = 0; y < m_height; y++) { - avierr = AVIERR_NO_MEMORY; - goto error; + std::uint16_t *dest = &bitmap.pix16(y); + + /* handle the first four bytes independently */ + x = 0; + if (y == 0) + { + /* first DWORD is stored as YUY2 */ + lasty = data[dataoffs++]; + lastcb = data[dataoffs++]; + *dest++ = (lasty << 8) | lastcb; + lasty = data[dataoffs++]; + lastcr = data[dataoffs++]; + *dest++ = (lasty << 8) | lastcr; + x = 2; + } + + /* loop over pixels */ + for ( ; x < m_width; x++) + { + huffyuv_table const &ytable = m_huffyuv->table[0]; + huffyuv_table const &ctable = m_huffyuv->table[1 + (x & 1)]; + std::uint16_t huffdata; + int shift; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + /* look up the Y component */ + huffdata = ytable.baselookup[bitbuffer >> 16]; + shift = huffdata & 0xff; + if (shift == 0) + { + bitsinbuffer -= 16; + bitbuffer <<= 16; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + huffdata = ytable.extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; + shift = huffdata & 0xff; + } + bitsinbuffer -= shift; + bitbuffer <<= shift; + std::uint16_t const pixel = huffdata & 0xff00; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + /* look up the Cb/Cr component */ + huffdata = ctable.baselookup[bitbuffer >> 16]; + shift = huffdata & 0xff; + if (shift == 0) + { + bitsinbuffer -= 16; + bitbuffer <<= 16; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + huffdata = ctable.extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; + shift = huffdata & 0xff; + } + bitsinbuffer -= shift; + bitbuffer <<= shift; + *dest++ = pixel | (huffdata >> 8); + } } - memset(newfile->stream, 0, 2 * sizeof(newfile->stream[0])); - - /* initialize the video track */ - stream = &newfile->stream[newfile->streams++]; - stream->type = STREAMTYPE_VIDS; - stream->format = newfile->info.video_format; - stream->rate = newfile->info.video_timescale; - stream->scale = newfile->info.video_sampletime; - stream->width = newfile->info.video_width; - stream->height = newfile->info.video_height; - stream->depth = newfile->info.video_depth; - - /* initialize the audio track */ - if (newfile->info.audio_channels > 0) + + /* apply deltas */ + lastprevy = lastprevcb = lastprevcr = 0; + for (y = 0; y < m_height; y++) { - stream = &newfile->stream[newfile->streams++]; - stream->type = STREAMTYPE_AUDS; - stream->format = newfile->info.audio_format; - stream->rate = newfile->info.audio_timescale; - stream->scale = newfile->info.audio_sampletime; - stream->channels = newfile->info.audio_channels; - stream->samplebits = newfile->info.audio_samplebits; - stream->samplerate = newfile->info.audio_samplerate; - - /* initialize the sound buffering */ - avierr = soundbuf_initialize(newfile); - if (avierr != AVIERR_NONE) - goto error; - } + std::uint16_t *prevrow = &bitmap.pix16(y - prevlines); + std::uint16_t *dest = &bitmap.pix16(y); - /* write the initial headers */ - avierr = write_initial_headers(newfile); - if (avierr != AVIERR_NONE) - goto error; + /* handle the first four bytes independently */ + x = 0; + if (y == 0) + { + /* lasty, lastcr, lastcb are set up previously */ + x = 2; + } - *file = newfile; - return AVIERR_NONE; + /* left predict or gradient predict */ + if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_LEFT || + ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT)) + { + /* first do left deltas */ + for ( ; x < m_width; x += 2) + { + std::uint16_t pixel0 = dest[x + 0]; + std::uint16_t pixel1 = dest[x + 1]; -error: - /* clean up after an error */ - if (newfile != nullptr) - { - if (newfile->stream != nullptr) - free(newfile->stream); - if (newfile->file != nullptr) + lasty += pixel0 >> 8; + lastcb += pixel0; + dest[x + 0] = (lasty << 8) | lastcb; + + lasty += pixel1 >> 8; + lastcr += pixel1; + dest[x + 1] = (lasty << 8) | lastcr; + } + + /* for gradient, we then add in the previous row */ + if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT && y >= prevlines) + for (x = 0; x < m_width; x++) + { + std::uint16_t curpix = dest[x]; + std::uint16_t prevpix = prevrow[x]; + std::uint8_t ysum = (curpix >> 8) + (prevpix >> 8); + std::uint8_t csum = curpix + prevpix; + dest[x] = (ysum << 8) | csum; + } + } + + /* median predict on rows > 0 */ + else if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_MEDIAN && y >= prevlines) { - osd_close(newfile->file); - osd_rmfile(filename); + for ( ; x < m_width; x += 2) + { + std::uint16_t prevpixel0 = prevrow[x + 0]; + std::uint16_t prevpixel1 = prevrow[x + 1]; + std::uint16_t pixel0 = dest[x + 0]; + std::uint16_t pixel1 = dest[x + 1]; + std::uint8_t a, b, c; + + /* compute previous, above, and (prev + above - above-left) */ + a = lasty; + b = prevpixel0 >> 8; + c = lastprevy; + lastprevy = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lasty = (pixel0 >> 8) + b; + + /* compute previous, above, and (prev + above - above-left) */ + a = lastcb; + b = prevpixel0 & 0xff; + c = lastprevcb; + lastprevcb = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lastcb = (pixel0 & 0xff) + b; + dest[x + 0] = (lasty << 8) | lastcb; + + /* compute previous, above, and (prev + above - above-left) */ + a = lasty; + b = prevpixel1 >> 8; + c = lastprevy; + lastprevy = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lasty = (pixel1 >> 8) + b; + + /* compute previous, above, and (prev + above - above-left) */ + a = lastcr; + b = prevpixel1 & 0xff; + c = lastprevcr; + lastprevcr = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lastcr = (pixel1 & 0xff) + b; + dest[x + 1] = (lasty << 8) | lastcr; + } } - free(newfile); } - return avierr; + + return avi_file::error::NONE; } @@ -1029,73 +1481,46 @@ error: * @return An avi_error. */ -avi_error avi_close(avi_file *file) +avi_file_impl::~avi_file_impl() { - avi_error avierr = AVIERR_NONE; - int strnum; + error avierr = error::NONE; /* if we're creating a new file, finalize it by writing out the non-media chunks */ - if (file->type == FILETYPE_CREATE) + if (m_type == FILETYPE_CREATE) { /* flush any pending sound data */ - avierr = soundbuf_flush(file, FALSE); + avierr = soundbuf_flush(false); /* close the movi chunk */ - if (avierr == AVIERR_NONE) - avierr = chunk_close(file); + if (avierr == error::NONE) + avierr = chunk_close(); /* if this is the first RIFF chunk, write an idx1 */ - if (avierr == AVIERR_NONE && file->riffbase == 0) - avierr = write_idx1_chunk(file); + if (avierr == error::NONE && m_riffbase == 0) + avierr = write_idx1_chunk(); /* update the strh and indx chunks for each stream */ - for (strnum = 0; strnum < file->streams; strnum++) + for (int strnum = 0; strnum < m_streams.size(); strnum++) { - if (avierr == AVIERR_NONE) - avierr = write_strh_chunk(file, &file->stream[strnum], FALSE); - if (avierr == AVIERR_NONE) - avierr = write_indx_chunk(file, &file->stream[strnum], FALSE); + if (avierr == error::NONE) + avierr = write_strh_chunk(m_streams[strnum], false); + if (avierr == error::NONE) + avierr = write_indx_chunk(m_streams[strnum], false); } /* update the avih chunk */ - if (avierr == AVIERR_NONE) - avierr = write_avih_chunk(file, FALSE); + if (avierr == error::NONE) + avierr = write_avih_chunk(false); /* close the RIFF chunk */ - if (avierr == AVIERR_NONE) - avierr = chunk_close(file); + if (avierr == error::NONE) + avierr = chunk_close(); } /* close the file */ - osd_close(file->file); - - /* free the stream-specific data */ - for (strnum = 0; strnum < file->streams; strnum++) - { - avi_stream *stream = &file->stream[strnum]; - if (stream->huffyuv != nullptr) - { - huffyuv_data *huffyuv = stream->huffyuv; - int table; + m_file.reset(); - for (table = 0; table < ARRAY_LENGTH(huffyuv->table); table++) - if (huffyuv->table[table].extralookup != nullptr) - free(huffyuv->table[table].extralookup); - free(huffyuv); - } - if (stream->chunk != nullptr) - free(stream->chunk); - } - - /* free the file itself */ - if (file->soundbuf != nullptr) - free(file->soundbuf); - if (file->stream != nullptr) - free(file->stream); - if (file->tempbuffer != nullptr) - free(file->tempbuffer); - free(file); - return avierr; + //return avierr; } @@ -1111,55 +1536,14 @@ avi_error avi_close(avi_file *file) * @param [in,out] file If non-null, the file. */ -void avi_printf_chunks(avi_file *file) -{ - printf_chunk_recursive(file, &file->rootchunk, 0); -} - - -/*------------------------------------------------- - avi_error_string - get the error string for - an avi_error --------------------------------------------------*/ - -/** - * @fn const char *avi_error_string(avi_error err) - * - * @brief Avi error string. - * - * @param err The error. - * - * @return null if it fails, else a char*. - */ - -const char *avi_error_string(avi_error err) +void avi_file_impl::printf_chunks() { - switch (err) - { - case AVIERR_NONE: return "success"; - case AVIERR_END: return "hit end of file"; - case AVIERR_INVALID_DATA: return "invalid data"; - case AVIERR_NO_MEMORY: return "out of memory"; - case AVIERR_READ_ERROR: return "read error"; - case AVIERR_WRITE_ERROR: return "write error"; - case AVIERR_STACK_TOO_DEEP: return "stack overflow"; - case AVIERR_UNSUPPORTED_FEATURE: return "unsupported feature"; - case AVIERR_CANT_OPEN_FILE: return "unable to open file"; - case AVIERR_INCOMPATIBLE_AUDIO_STREAMS: return "found incompatible audio streams"; - case AVIERR_INVALID_SAMPLERATE: return "found invalid sample rate"; - case AVIERR_INVALID_STREAM: return "invalid stream"; - case AVIERR_INVALID_FRAME: return "invalid frame index"; - case AVIERR_INVALID_BITMAP: return "invalid bitmap"; - case AVIERR_UNSUPPORTED_VIDEO_FORMAT: return "unsupported video format"; - case AVIERR_UNSUPPORTED_AUDIO_FORMAT: return "unsupported audio format"; - case AVIERR_EXCEEDED_SOUND_BUFFER: return "sound buffer overflow"; - default: return "undocumented error"; - } + printf_chunk_recursive(&m_rootchunk, 0); } /*------------------------------------------------- - avi_get_movie_info - return a pointer to the + get_movie_info - return a pointer to the movie info -------------------------------------------------*/ @@ -1173,9 +1557,9 @@ const char *avi_error_string(avi_error err) * @return null if it fails, else an avi_movie_info*. */ -const avi_movie_info *avi_get_movie_info(avi_file *file) +avi_file::movie_info const &avi_file_impl::get_movie_info() const { - return &file->info; + return m_info; } @@ -1185,19 +1569,19 @@ const avi_movie_info *avi_get_movie_info(avi_file *file) -------------------------------------------------*/ /** - * @fn UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) + * @fn std::uint32_t avi_first_sample_in_frame(avi_file *file, std::uint32_t framenum) * * @brief Avi first sample in frame. * * @param [in,out] file If non-null, the file. * @param framenum The framenum. * - * @return An UINT32. + * @return An std::uint32_t. */ -UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) +std::uint32_t avi_file_impl::first_sample_in_frame(std::uint32_t framenum) const { - return framenum_to_samplenum(file, framenum); + return framenum_to_samplenum(framenum); } @@ -1208,7 +1592,7 @@ UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) -------------------------------------------------*/ /** - * @fn avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bitmap) + * @fn avi_error avi_read_video_frame(avi_file *file, std::uint32_t framenum, bitmap_yuy16 &bitmap) * * @brief Avi read video frame. * @@ -1219,54 +1603,51 @@ UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) * @return An avi_error. */ -avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bitmap) +avi_file::error avi_file_impl::read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) { - avi_error avierr = AVIERR_NONE; - UINT32 bytes_read, chunkid; - file_error filerr; - avi_stream *stream; - /* get the video stream */ - stream = get_video_stream(file); - if (stream == nullptr) - return AVIERR_INVALID_STREAM; + avi_stream *const stream = get_video_stream(); + if (!stream) + return error::INVALID_STREAM; /* validate our ability to handle the data */ - if (stream->format != FORMAT_UYVY && stream->format != FORMAT_VYUY && stream->format != FORMAT_YUY2 && stream->format != FORMAT_HFYU) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU) + return error::UNSUPPORTED_VIDEO_FORMAT; /* assume one chunk == one frame */ - if (framenum >= stream->chunks) - return AVIERR_INVALID_FRAME; + if (framenum >= stream->chunks()) + return error::INVALID_FRAME; /* we only support YUY-style bitmaps (16bpp) */ - if (bitmap.width() < stream->width || bitmap.height() < stream->height) - return AVIERR_INVALID_BITMAP; + if (bitmap.width() < stream->width() || bitmap.height() < stream->height()) + return error::INVALID_BITMAP; /* expand the tempbuffer to hold the data if necessary */ - avierr = expand_tempbuffer(file, stream->chunk[framenum].length); - if (avierr != AVIERR_NONE) + error avierr = error::NONE; + avierr = expand_tempbuffer(stream->chunk(framenum).length); + if (avierr != error::NONE) return avierr; /* read in the data */ - filerr = osd_read(file->file, file->tempbuffer, stream->chunk[framenum].offset, stream->chunk[framenum].length, &bytes_read); - if (filerr != FILERR_NONE || bytes_read != stream->chunk[framenum].length) - return AVIERR_READ_ERROR; + std::uint32_t bytes_read; + osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length) + return error::READ_ERROR; /* validate this is good data */ - chunkid = fetch_32bits(&file->tempbuffer[0]); - if (chunkid == get_chunkid_for_stream(file, stream)) + std::uint32_t const chunkid = fetch_32bits(&m_tempbuffer[0]); + if (chunkid == get_chunkid_for_stream(stream)) { /* HuffYUV-compressed */ - if (stream->format == FORMAT_HFYU) - avierr = huffyuv_decompress_to_yuy16(stream, file->tempbuffer + 8, stream->chunk[framenum].length - 8, bitmap); + if (stream->format() == FORMAT_HFYU) + avierr = stream->huffyuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap); /* other YUV-compressed */ else - avierr = yuv_decompress_to_yuy16(stream, file->tempbuffer + 8, stream->chunk[framenum].length - 8, bitmap); + avierr = stream->yuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap); } else - avierr = AVIERR_INVALID_DATA; + avierr = error::INVALID_DATA; return avierr; } @@ -1278,7 +1659,7 @@ avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bi -------------------------------------------------*/ /** - * @fn avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output) + * @fn avi_error avi_read_sound_samples(avi_file *file, int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) * * @brief Avi read sound samples. * @@ -1291,95 +1672,90 @@ avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bi * @return An avi_error. */ -avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output) +avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) { - avi_error avierr = AVIERR_NONE; - UINT32 bytes_per_sample; - file_error filerr; - avi_stream *stream; - int offset = 0; - /* get the audio stream */ - stream = get_audio_stream(file, channel, &offset); - if (stream == nullptr) - return AVIERR_INVALID_STREAM; + int offset = 0; + avi_stream *const stream = get_audio_stream(channel, offset); + if (!stream) + return error::INVALID_STREAM; /* validate our ability to handle the data */ - if (stream->format != 0 || (stream->samplebits != 8 && stream->samplebits != 16)) - return AVIERR_UNSUPPORTED_AUDIO_FORMAT; + if (stream->format() != 0 || (stream->samplebits() != 8 && stream->samplebits() != 16)) + return error::UNSUPPORTED_AUDIO_FORMAT; /* verify we are in range */ - if (firstsample >= stream->samples) - return AVIERR_INVALID_FRAME; - if (firstsample + numsamples > stream->samples) - numsamples = stream->samples - firstsample; + if (firstsample >= stream->samples()) + return error::INVALID_FRAME; + if (firstsample + numsamples > stream->samples()) + numsamples = stream->samples() - firstsample; /* determine bytes per sample */ - bytes_per_sample = (stream->samplebits / 8) * stream->channels; + std::uint32_t const bytes_per_sample = stream->bytes_per_sample(); /* loop until all samples have been extracted */ while (numsamples > 0) { - UINT32 chunkbase = 0, chunkend = 0, chunkid; - UINT32 bytes_read, samples_this_chunk; + std::uint32_t chunkbase = 0, chunkend = 0, chunkid; + std::uint32_t bytes_read, samples_this_chunk; int chunknum, sampnum; /* locate the chunk with the first sample */ - for (chunknum = 0; chunknum < stream->chunks; chunknum++) + for (chunknum = 0; chunknum < stream->chunks(); chunknum++) { - chunkend = chunkbase + (stream->chunk[chunknum].length - 8) / bytes_per_sample; + chunkend = chunkbase + (stream->chunk(chunknum).length - 8) / bytes_per_sample; if (firstsample < chunkend) break; chunkbase = chunkend; } /* if we hit the end, fill the rest with silence */ - if (chunknum == stream->chunks) + if (chunknum == stream->chunks()) { - memset(output, 0, numsamples * 2); + std::memset(output, 0, numsamples * 2); break; } /* expand the tempbuffer to hold the data if necessary */ - avierr = expand_tempbuffer(file, stream->chunk[chunknum].length); - if (avierr != AVIERR_NONE) + error avierr = expand_tempbuffer(stream->chunk(chunknum).length); + if (avierr != error::NONE) return avierr; /* read in the data */ - filerr = osd_read(file->file, file->tempbuffer, stream->chunk[chunknum].offset, stream->chunk[chunknum].length, &bytes_read); - if (filerr != FILERR_NONE || bytes_read != stream->chunk[chunknum].length) - return AVIERR_READ_ERROR; + auto const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(chunknum).offset, stream->chunk(chunknum).length, bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(chunknum).length) + return error::READ_ERROR; /* validate this is good data */ - chunkid = fetch_32bits(&file->tempbuffer[0]); - if (chunkid != get_chunkid_for_stream(file, stream)) - return AVIERR_INVALID_DATA; + chunkid = fetch_32bits(&m_tempbuffer[0]); + if (chunkid != get_chunkid_for_stream(stream)) + return error::INVALID_DATA; /* determine how many samples to copy */ samples_this_chunk = chunkend - firstsample; - samples_this_chunk = MIN(samples_this_chunk, numsamples); + samples_this_chunk = (std::min)(samples_this_chunk, numsamples); /* extract 16-bit samples from the chunk */ - if (stream->samplebits == 16) + if (stream->samplebits() == 16) { - const INT16 *base = (const INT16 *)(file->tempbuffer + 8); - base += stream->channels * (firstsample - chunkbase) + offset; + const std::int16_t *base = reinterpret_cast<const std::int16_t *>(&m_tempbuffer[8]); + base += stream->channels() * (firstsample - chunkbase) + offset; for (sampnum = 0; sampnum < samples_this_chunk; sampnum++) { *output++ = LITTLE_ENDIANIZE_INT16(*base); - base += stream->channels; + base += stream->channels(); } } /* extract 8-bit samples from the chunk */ - else if (stream->samplebits == 8) + else if (stream->samplebits() == 8) { - const UINT8 *base = (const UINT8 *)(file->tempbuffer + 8); - base += stream->channels * (firstsample - chunkbase) + offset; + const std::uint8_t *base = &m_tempbuffer[8]; + base += stream->channels() * (firstsample - chunkbase) + offset; for (sampnum = 0; sampnum < samples_this_chunk; sampnum++) { *output++ = (*base << 8) - 0x8000; - base += stream->channels; + base += stream->channels(); } } @@ -1388,7 +1764,7 @@ avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample numsamples -= samples_this_chunk; } - return avierr; + return error::NONE; } @@ -1408,45 +1784,45 @@ avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample * @return An avi_error. */ -avi_error avi_append_video_frame(avi_file *file, bitmap_yuy16 &bitmap) +avi_file::error avi_file_impl::append_video_frame(bitmap_yuy16 &bitmap) { - avi_stream *stream = get_video_stream(file); - avi_error avierr; - UINT32 maxlength; + avi_stream *const stream = get_video_stream(); + error avierr; + std::uint32_t maxlength; /* validate our ability to handle the data */ - if (stream->format != FORMAT_UYVY && stream->format != FORMAT_VYUY && stream->format != FORMAT_YUY2 && stream->format != FORMAT_HFYU) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU) + return error::UNSUPPORTED_VIDEO_FORMAT; /* write out any sound data first */ - avierr = soundbuf_write_chunk(file, stream->chunks); - if (avierr != AVIERR_NONE) + avierr = soundbuf_write_chunk(stream->chunks()); + if (avierr != error::NONE) return avierr; /* make sure we have enough room */ - maxlength = 2 * stream->width * stream->height; - avierr = expand_tempbuffer(file, maxlength); - if (avierr != AVIERR_NONE) + maxlength = 2 * stream->width() * stream->height(); + avierr = expand_tempbuffer(maxlength); + if (avierr != error::NONE) return avierr; /* now compress the data */ - avierr = yuy16_compress_to_yuy(stream, bitmap, file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = stream->yuy16_compress_to_yuy(bitmap, &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* write the data */ - avierr = chunk_write(file, get_chunkid_for_stream(file, stream), file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* set the info for this new chunk */ - avierr = set_stream_chunk_info(stream, stream->chunks, file->writeoffs - maxlength - 8, maxlength + 8); - if (avierr != AVIERR_NONE) + avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8); + if (avierr != error::NONE) return avierr; - stream->samples = file->info.video_numsamples = stream->chunks; + stream->set_samples(m_info.video_numsamples = stream->chunks()); - return AVIERR_NONE; + return error::NONE; } @@ -1466,49 +1842,49 @@ avi_error avi_append_video_frame(avi_file *file, bitmap_yuy16 &bitmap) * @return An avi_error. */ -avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap) +avi_file::error avi_file_impl::append_video_frame(bitmap_rgb32 &bitmap) { - avi_stream *stream = get_video_stream(file); - avi_error avierr; - UINT32 maxlength; + avi_stream *const stream = get_video_stream(); + error avierr; + std::uint32_t maxlength; /* validate our ability to handle the data */ - if (stream->format != 0) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->format() != 0) + return error::UNSUPPORTED_VIDEO_FORMAT; /* depth must be 24 */ - if (stream->depth != 24) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->depth() != 24) + return error::UNSUPPORTED_VIDEO_FORMAT; /* write out any sound data first */ - avierr = soundbuf_write_chunk(file, stream->chunks); - if (avierr != AVIERR_NONE) + avierr = soundbuf_write_chunk(stream->chunks()); + if (avierr != error::NONE) return avierr; /* make sure we have enough room */ - maxlength = 3 * stream->width * stream->height; - avierr = expand_tempbuffer(file, maxlength); - if (avierr != AVIERR_NONE) + maxlength = 3 * stream->width() * stream->height(); + avierr = expand_tempbuffer(maxlength); + if (avierr != error::NONE) return avierr; /* copy the RGB data to the destination */ - avierr = rgb32_compress_to_rgb(stream, bitmap, file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = stream->rgb32_compress_to_rgb(bitmap, &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* write the data */ - avierr = chunk_write(file, get_chunkid_for_stream(file, stream), file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* set the info for this new chunk */ - avierr = set_stream_chunk_info(stream, stream->chunks, file->writeoffs - maxlength - 8, maxlength + 8); - if (avierr != AVIERR_NONE) + avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8); + if (avierr != error::NONE) return avierr; - stream->samples = file->info.video_numsamples = stream->chunks; + stream->set_samples(m_info.video_numsamples = stream->chunks()); - return AVIERR_NONE; + return error::NONE; } @@ -1518,7 +1894,7 @@ avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap) -------------------------------------------------*/ /** - * @fn avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples, UINT32 sampleskip) + * @fn avi_error avi_append_sound_samples(avi_file *file, int channel, const std::int16_t *samples, std::uint32_t numsamples, std::uint32_t sampleskip) * * @brief Avi append sound samples. * @@ -1531,27 +1907,27 @@ avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap) * @return An avi_error. */ -avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples, UINT32 sampleskip) +avi_file::error avi_file_impl::append_sound_samples(int channel, const std::int16_t *samples, std::uint32_t numsamples, std::uint32_t sampleskip) { - UINT32 sampoffset = file->soundbuf_chansamples[channel]; - UINT32 sampnum; + std::uint32_t sampoffset = m_soundbuf_chansamples[channel]; + std::uint32_t sampnum; /* see if we have enough room in the buffer */ - if (sampoffset + numsamples > file->soundbuf_samples) - return AVIERR_EXCEEDED_SOUND_BUFFER; + if (sampoffset + numsamples > m_soundbuf_samples) + return error::EXCEEDED_SOUND_BUFFER; /* append samples to the buffer in little-endian format */ for (sampnum = 0; sampnum < numsamples; sampnum++) { - INT16 data = *samples++; + std::int16_t data = *samples++; samples += sampleskip; data = LITTLE_ENDIANIZE_INT16(data); - file->soundbuf[sampoffset++ * file->info.audio_channels + channel] = data; + m_soundbuf[sampoffset++ * m_info.audio_channels + channel] = data; } - file->soundbuf_chansamples[channel] = sampoffset; + m_soundbuf_chansamples[channel] = sampoffset; /* flush any full sound chunks to disk */ - return soundbuf_flush(file, TRUE); + return soundbuf_flush(TRUE); } @@ -1561,7 +1937,7 @@ avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *sam -------------------------------------------------*/ /** - * @fn static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer) + * @fn static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, std::uint8_t **buffer) * * @brief Reads chunk data. * @@ -1572,26 +1948,22 @@ avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *sam * @return The chunk data. */ -static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer) +avi_file::error avi_file_impl::read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer) { - file_error filerr; - UINT32 bytes_read; - /* allocate memory for the data */ - *buffer = (UINT8 *)malloc(chunk->size); - if (*buffer == nullptr) - return AVIERR_NO_MEMORY; + try { buffer.reset(new std::uint8_t[chunk.size]); } + catch (...) { return error::NO_MEMORY; } /* read from the file */ - filerr = osd_read(file->file, *buffer, chunk->offset + 8, chunk->size, &bytes_read); - if (filerr != FILERR_NONE || bytes_read != chunk->size) + std::uint32_t bytes_read; + osd_file::error const filerr = m_file->read(&buffer[0], chunk.offset + 8, chunk.size, bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != chunk.size) { - free(*buffer); - *buffer = nullptr; - return AVIERR_READ_ERROR; + buffer.reset(); + return error::READ_ERROR; } - return AVIERR_NONE; + return error::NONE; } @@ -1612,12 +1984,12 @@ static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 * * @return The first chunk. */ -static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk) +avi_file::error avi_file_impl::get_first_chunk(avi_chunk const *parent, avi_chunk &newchunk) { - UINT64 startoffset = (parent != nullptr && parent->type != 0) ? parent->offset + 12 : 0; + std::uint64_t startoffset = (parent != nullptr && parent->type != 0) ? parent->offset + 12 : 0; if (parent != nullptr && parent->type != CHUNKTYPE_LIST && parent->type != CHUNKTYPE_RIFF && parent->type != 0) - return AVIERR_INVALID_DATA; - return get_next_chunk_internal(file, parent, newchunk, startoffset); + return error::INVALID_DATA; + return get_next_chunk_internal(parent, newchunk, startoffset); } @@ -1638,10 +2010,10 @@ static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_ch * @return The next chunk. */ -static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk) +avi_file::error avi_file_impl::get_next_chunk(avi_chunk const *parent, avi_chunk &newchunk) { - UINT64 nextoffset = newchunk->offset + 8 + newchunk->size + (newchunk->size & 1); - return get_next_chunk_internal(file, parent, newchunk, nextoffset); + std::uint64_t nextoffset = newchunk.offset + 8 + newchunk.size + (newchunk.size & 1); + return get_next_chunk_internal(parent, newchunk, nextoffset); } @@ -1651,7 +2023,7 @@ static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chu -------------------------------------------------*/ /** - * @fn static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_first_chunk(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the first chunk. * @@ -1663,13 +2035,13 @@ static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chu * @return The found chunk. */ -static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_first_chunk(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = get_first_chunk(file, container, result); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, result)) - if (result->type == findme) - return AVIERR_NONE; + for (avierr = get_first_chunk(container, result); avierr == error::NONE; avierr = get_next_chunk(container, result)) + if (result.type == findme) + return error::NONE; return avierr; } @@ -1681,7 +2053,7 @@ static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk -------------------------------------------------*/ /** - * @fn static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_next_chunk(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the next chunk. * @@ -1693,13 +2065,13 @@ static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk * @return The found chunk. */ -static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_next_chunk(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = get_next_chunk(file, container, result); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, result)) - if (result->type == findme) - return AVIERR_NONE; + for (avierr = get_next_chunk(container, result); avierr == error::NONE; avierr = get_next_chunk(container, result)) + if (result.type == findme) + return error::NONE; return avierr; } @@ -1711,7 +2083,7 @@ static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk -------------------------------------------------*/ /** - * @fn static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_first_list(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the first list. * @@ -1723,13 +2095,13 @@ static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk * @return The found list. */ -static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_first_list(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = find_first_chunk(file, CHUNKTYPE_LIST, container, result); avierr == AVIERR_NONE; avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result)) - if (result->listtype == findme) - return AVIERR_NONE; + for (avierr = find_first_chunk(CHUNKTYPE_LIST, container, result); avierr == error::NONE; avierr = find_next_chunk(CHUNKTYPE_LIST, container, result)) + if (result.listtype == findme) + return error::NONE; return avierr; } @@ -1741,7 +2113,7 @@ static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk -------------------------------------------------*/ /** - * @fn static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_next_list(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the next list. * @@ -1753,13 +2125,13 @@ static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk * @return The found list. */ -static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_next_list(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result); avierr == AVIERR_NONE; avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result)) - if (result->listtype == findme) - return AVIERR_NONE; + for (avierr = find_next_chunk(CHUNKTYPE_LIST, container, result); avierr == error::NONE; avierr = find_next_chunk(CHUNKTYPE_LIST, container, result)) + if (result.listtype == findme) + return error::NONE; return avierr; } @@ -1771,7 +2143,7 @@ static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk * -------------------------------------------------*/ /** - * @fn static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset) + * @fn static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, std::uint64_t offset) * * @brief Gets the next chunk internal. * @@ -1783,42 +2155,42 @@ static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk * * @return The next chunk internal. */ -static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset) +avi_file::error avi_file_impl::get_next_chunk_internal(const avi_chunk *parent, avi_chunk &newchunk, std::uint64_t offset) { - file_error filerr; - UINT8 buffer[12]; - UINT32 bytesread; + osd_file::error filerr; + std::uint8_t buffer[12]; + std::uint32_t bytesread; /* NULL parent implies the root */ if (parent == nullptr) - parent = &file->rootchunk; + parent = &m_rootchunk; /* start at the current offset */ - newchunk->offset = offset; + newchunk.offset = offset; /* if we're past the bounds of the parent, bail */ - if (newchunk->offset + 8 >= parent->offset + 8 + parent->size) - return AVIERR_END; + if (newchunk.offset + 8 >= parent->offset + 8 + parent->size) + return error::END; /* read the header */ - filerr = osd_read(file->file, buffer, newchunk->offset, 8, &bytesread); - if (filerr != FILERR_NONE || bytesread != 8) - return AVIERR_INVALID_DATA; + filerr = m_file->read(buffer, newchunk.offset, 8, bytesread); + if (filerr != osd_file::error::NONE || bytesread != 8) + return error::INVALID_DATA; /* fill in the new chunk */ - newchunk->type = fetch_32bits(&buffer[0]); - newchunk->size = fetch_32bits(&buffer[4]); + newchunk.type = fetch_32bits(&buffer[0]); + newchunk.size = fetch_32bits(&buffer[4]); /* if we are a list, fetch the list type */ - if (newchunk->type == CHUNKTYPE_LIST || newchunk->type == CHUNKTYPE_RIFF) + if (newchunk.type == CHUNKTYPE_LIST || newchunk.type == CHUNKTYPE_RIFF) { - filerr = osd_read(file->file, &buffer[8], newchunk->offset + 8, 4, &bytesread); - if (filerr != FILERR_NONE || bytesread != 4) - return AVIERR_INVALID_DATA; - newchunk->listtype = fetch_32bits(&buffer[8]); + filerr = m_file->read(&buffer[8], newchunk.offset + 8, 4, bytesread); + if (filerr != osd_file::error::NONE || bytesread != 4) + return error::INVALID_DATA; + newchunk.listtype = fetch_32bits(&buffer[8]); } - return AVIERR_NONE; + return error::NONE; } @@ -1836,98 +2208,95 @@ static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent * @return The movie data. */ -static avi_error read_movie_data(avi_file *file) +avi_file::error avi_file_impl::read_movie_data() { avi_chunk riff, hdrl, avih, strl, strh, strf, indx, movi, idx1; - avi_error avierr; - int strindex; + error avierr; /* find the RIFF chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_RIFF, nullptr, &riff); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_RIFF, nullptr, riff); + if (avierr != error::NONE) + return avierr; /* verify that the RIFF type is AVI */ if (riff.listtype != LISTTYPE_AVI) { - avierr = AVIERR_INVALID_DATA; - goto error; + avierr = error::INVALID_DATA; + return avierr; } /* find the hdrl LIST chunk within the RIFF */ - avierr = find_first_list(file, LISTTYPE_HDRL, &riff, &hdrl); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_list(LISTTYPE_HDRL, &riff, hdrl); + if (avierr != error::NONE) + return avierr; /* find the avih chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_AVIH, &hdrl, &avih); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_AVIH, &hdrl, avih); + if (avierr != error::NONE) + return avierr; /* parse the avih chunk */ - avierr = parse_avih_chunk(file, &avih); - if (avierr != AVIERR_NONE) - goto error; + avierr = parse_avih_chunk(avih); + if (avierr != error::NONE) + return avierr; /* loop over strl LIST chunks */ - strindex = 0; - for (avierr = find_first_list(file, LISTTYPE_STRL, &hdrl, &strl); avierr == AVIERR_NONE; avierr = find_next_list(file, LISTTYPE_STRL, &hdrl, &strl)) + int strindex = 0; + for (avierr = find_first_list(LISTTYPE_STRL, &hdrl, strl); avierr == error::NONE; avierr = find_next_list(LISTTYPE_STRL, &hdrl, strl)) { /* if we have too many, it's a bad file */ - if (strindex >= file->streams) - goto error; + if (strindex >= m_streams.size()) + return avierr; /* find the strh chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_STRH, &strl, &strh); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_STRH, &strl, strh); + if (avierr != error::NONE) + return avierr; /* parse the data */ - avierr = parse_strh_chunk(file, &file->stream[strindex], &strh); - if (avierr != AVIERR_NONE) - goto error; + avierr = parse_strh_chunk(m_streams[strindex], strh); + if (avierr != error::NONE) + return avierr; /* find the strf chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_STRF, &strl, &strf); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_STRF, &strl, strf); + if (avierr != error::NONE) + return avierr; /* parse the data */ - avierr = parse_strf_chunk(file, &file->stream[strindex], &strf); - if (avierr != AVIERR_NONE) - goto error; + avierr = parse_strf_chunk(m_streams[strindex], strf); + if (avierr != error::NONE) + return avierr; /* find the indx chunk, if present */ - avierr = find_first_chunk(file, CHUNKTYPE_INDX, &strl, &indx); - if (avierr == AVIERR_NONE) - avierr = parse_indx_chunk(file, &file->stream[strindex], &indx); + avierr = find_first_chunk(CHUNKTYPE_INDX, &strl, indx); + if (avierr == error::NONE) + avierr = parse_indx_chunk(m_streams[strindex], indx); /* next stream */ strindex++; } /* normalize the error after parsing the stream headers */ - if (avierr == AVIERR_END) - avierr = AVIERR_NONE; - if (avierr != AVIERR_NONE) - goto error; + if (avierr == error::END) + avierr = error::NONE; + if (avierr != error::NONE) + return avierr; /* find the base of the movi data */ - avierr = find_first_list(file, LISTTYPE_MOVI, &riff, &movi); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_list(LISTTYPE_MOVI, &riff, movi); + if (avierr != error::NONE) + return avierr; /* find and parse the idx1 chunk within the RIFF (if present) */ - avierr = find_first_chunk(file, CHUNKTYPE_IDX1, &riff, &idx1); - if (avierr == AVIERR_NONE) - avierr = parse_idx1_chunk(file, movi.offset + 8, &idx1); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_IDX1, &riff, idx1); + if (avierr == error::NONE) + avierr = parse_idx1_chunk(movi.offset + 8, idx1); + if (avierr != error::NONE) + return avierr; /* now extract the movie info */ - avierr = extract_movie_info(file); - -error: + avierr = extract_movie_info(); return avierr; } @@ -1947,59 +2316,55 @@ error: * @return The extracted movie information. */ -static avi_error extract_movie_info(avi_file *file) +avi_file::error avi_file_impl::extract_movie_info() { - //avi_stream *audiostream; avi_stream *stream; + int offset; /* get the video stream */ - stream = get_video_stream(file); + stream = get_video_stream(); if (stream != nullptr) { /* fill in the info */ - file->info.video_format = stream->format; - file->info.video_timescale = stream->rate; - file->info.video_sampletime = stream->scale; - file->info.video_numsamples = stream->samples; - file->info.video_width = stream->width; - file->info.video_height = stream->height; + m_info.video_format = stream->format(); + m_info.video_timescale = stream->rate(); + m_info.video_sampletime = stream->scale(); + m_info.video_numsamples = stream->samples(); + m_info.video_width = stream->width(); + m_info.video_height = stream->height(); } /* get the first audio stream */ - stream = get_audio_stream(file, 0, nullptr); + stream = get_audio_stream(0, offset); if (stream != nullptr) { /* fill in the info */ - file->info.audio_format = stream->format; - file->info.audio_timescale = stream->rate; - file->info.audio_sampletime = stream->scale; - file->info.audio_numsamples = stream->samples; - file->info.audio_channels = 1; - file->info.audio_samplebits = stream->samplebits; - file->info.audio_samplerate = stream->samplerate; + m_info.audio_format = stream->format(); + m_info.audio_timescale = stream->rate(); + m_info.audio_sampletime = stream->scale(); + m_info.audio_numsamples = stream->samples(); + m_info.audio_channels = 1; + m_info.audio_samplebits = stream->samplebits(); + m_info.audio_samplerate = stream->samplerate(); } /* now make sure all other audio streams are valid */ - //audiostream = stream; - while (1) + while (nullptr != (stream = get_audio_stream(m_info.audio_channels, offset))) { /* get the stream info */ - stream = get_audio_stream(file, file->info.audio_channels, nullptr); - if (stream == nullptr) - break; - file->info.audio_channels++; + m_info.audio_channels++; /* verify compatibility */ - if (file->info.audio_format != stream->format || - file->info.audio_timescale != stream->rate || - file->info.audio_sampletime != stream->scale || - file->info.audio_numsamples != stream->samples || - file->info.audio_samplebits != stream->samplebits || - file->info.audio_samplerate != stream->samplerate) - return AVIERR_INCOMPATIBLE_AUDIO_STREAMS; + if (m_info.audio_format != stream->format() || + m_info.audio_timescale != stream->rate() || + m_info.audio_sampletime != stream->scale() || + m_info.audio_numsamples != stream->samples() || + m_info.audio_samplebits != stream->samplebits() || + m_info.audio_samplerate != stream->samplerate()) + return error::INCOMPATIBLE_AUDIO_STREAMS; } - return AVIERR_NONE; + return error::NONE; } @@ -2019,28 +2384,21 @@ static avi_error extract_movie_info(avi_file *file) * @return An avi_error. */ -static avi_error parse_avih_chunk(avi_file *file, avi_chunk *avih) +avi_file::error avi_file_impl::parse_avih_chunk(avi_chunk const &avih) { - UINT8 *chunkdata = nullptr; - avi_error avierr; - /* read the data */ - avierr = read_chunk_data(file, avih, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + error avierr = read_chunk_data(avih, chunkdata); + if (avierr != error::NONE) + return avierr; /* extract the data */ - file->streams = fetch_32bits(&chunkdata[24]); + std::uint32_t const streams = fetch_32bits(&chunkdata[24]); /* allocate memory for the streams */ - file->stream = (avi_stream *)malloc(sizeof(*file->stream) * file->streams); - if (file->stream == nullptr) - goto error; - memset(file->stream, 0, sizeof(*file->stream) * file->streams); + m_streams.clear(); + m_streams.resize(streams); -error: - if (chunkdata != nullptr) - free(chunkdata); return avierr; } @@ -2062,26 +2420,16 @@ error: * @return An avi_error. */ -static avi_error parse_strh_chunk(avi_file *file, avi_stream *stream, avi_chunk *strh) +avi_file::error avi_file_impl::parse_strh_chunk(avi_stream &stream, avi_chunk const &strh) { - UINT8 *chunkdata = nullptr; - avi_error avierr; - /* read the data */ - avierr = read_chunk_data(file, strh, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + error const avierr = read_chunk_data(strh, chunkdata); + if (avierr != error::NONE) + return avierr; /* extract the data */ - stream->type = fetch_32bits(&chunkdata[0]); - stream->scale = fetch_32bits(&chunkdata[20]); - stream->rate = fetch_32bits(&chunkdata[24]); - stream->samples = fetch_32bits(&chunkdata[32]); - -error: - if (chunkdata != nullptr) - free(chunkdata); - return avierr; + return stream.set_strh_data(&chunkdata[0], strh.size); } @@ -2102,43 +2450,17 @@ error: * @return An avi_error. */ -static avi_error parse_strf_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf) +avi_file::error avi_file_impl::parse_strf_chunk(avi_stream &stream, avi_chunk const &strf) { - UINT8 *chunkdata = nullptr; - avi_error avierr; + error avierr; /* read the data */ - avierr = read_chunk_data(file, strf, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; - - /* audio and video streams have differing headers */ - if (stream->type == STREAMTYPE_VIDS) - { - stream->width = fetch_32bits(&chunkdata[4]); - stream->height = fetch_32bits(&chunkdata[8]); - stream->depth = fetch_16bits(&chunkdata[14]); - stream->format = fetch_32bits(&chunkdata[16]); - - /* extra extraction for HuffYUV data */ - if (stream->format == FORMAT_HFYU && strf->size >= 56) - { - avierr = huffyuv_extract_tables(stream, chunkdata, strf->size); - if (avierr != AVIERR_NONE) - goto error; - } - } - else if (stream->type == STREAMTYPE_AUDS) - { - stream->channels = fetch_16bits(&chunkdata[2]); - stream->samplebits = fetch_16bits(&chunkdata[14]); - stream->samplerate = fetch_32bits(&chunkdata[4]); - } + std::unique_ptr<std::uint8_t []> chunkdata; + avierr = read_chunk_data(strf, chunkdata); + if (avierr != error::NONE) + return avierr; -error: - if (chunkdata != nullptr) - free(chunkdata); - return avierr; + return stream.set_strf_data(&chunkdata[0], strf.size); } @@ -2158,50 +2480,46 @@ error: * @return An avi_error. */ -static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf) +avi_file::error avi_file_impl::parse_indx_chunk(avi_stream &stream, avi_chunk const &strf) { - UINT32 entries, entry; - UINT8 *chunkdata = nullptr; - UINT16 longs_per_entry; - UINT8 type; - UINT64 baseoffset; - avi_error avierr; + error avierr; /* read the data */ - avierr = read_chunk_data(file, strf, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + avierr = read_chunk_data(strf, chunkdata); + if (avierr != error::NONE) + return avierr; /* extract the data */ - longs_per_entry = fetch_16bits(&chunkdata[0]); + std::uint16_t const longs_per_entry = fetch_16bits(&chunkdata[0]); //subtype = chunkdata[2]; - type = chunkdata[3]; - entries = fetch_32bits(&chunkdata[4]); + std::uint8_t const type = chunkdata[3]; + std::uint32_t const entries = fetch_32bits(&chunkdata[4]); //id = fetch_32bits(&chunkdata[8]); - baseoffset = fetch_64bits(&chunkdata[12]); + std::uint64_t const baseoffset = fetch_64bits(&chunkdata[12]); /* if this is a superindex, loop over entries and call ourselves recursively */ if (type == AVI_INDEX_OF_INDEXES) { /* validate the size of each entry */ if (longs_per_entry != 4) - return AVIERR_INVALID_DATA; + return error::INVALID_DATA; /* loop over entries and create subchunks for each */ - for (entry = 0; entry < entries; entry++) + for (std::uint32_t entry = 0; entry < entries; entry++) { - const UINT8 *base = &chunkdata[24 + entry * 16]; - file_error filerr; + const std::uint8_t *base = &chunkdata[24 + entry * 16]; + osd_file::error filerr; avi_chunk subchunk; - UINT32 bytes_read; - UINT8 buffer[8]; + std::uint32_t bytes_read; + std::uint8_t buffer[8]; /* go read the subchunk */ subchunk.offset = fetch_64bits(&base[0]); - filerr = osd_read(file->file, buffer, subchunk.offset, sizeof(buffer), &bytes_read); - if (filerr != FILERR_NONE || bytes_read != sizeof(buffer)) + filerr = m_file->read(buffer, subchunk.offset, sizeof(buffer), bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != sizeof(buffer)) { - avierr = AVIERR_READ_ERROR; + avierr = error::READ_ERROR; break; } @@ -2210,8 +2528,8 @@ static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk subchunk.size = fetch_32bits(&buffer[4]); /* recursively parse each referenced chunk; stop if we hit an error */ - avierr = parse_indx_chunk(file, stream, &subchunk); - if (avierr != AVIERR_NONE) + avierr = parse_indx_chunk(stream, subchunk); + if (avierr != error::NONE) break; } } @@ -2221,25 +2539,22 @@ static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk { /* validate the size of each entry */ if (longs_per_entry != 2 && longs_per_entry != 3) - return AVIERR_INVALID_DATA; + return error::INVALID_DATA; /* loop over entries and parse out the data */ - for (entry = 0; entry < entries; entry++) + for (std::uint32_t entry = 0; entry < entries; entry++) { - const UINT8 *base = &chunkdata[24 + entry * 4 * longs_per_entry]; - UINT32 offset = fetch_32bits(&base[0]); - UINT32 size = fetch_32bits(&base[4]) & 0x7fffffff; // bit 31 == NOT a keyframe + const std::uint8_t *base = &chunkdata[24 + entry * 4 * longs_per_entry]; + std::uint32_t offset = fetch_32bits(&base[0]); + std::uint32_t size = fetch_32bits(&base[4]) & 0x7fffffff; // bit 31 == NOT a keyframe /* set the info for this chunk and advance */ - avierr = set_stream_chunk_info(stream, stream->chunks++, baseoffset + offset - 8, size + 8); - if (avierr != AVIERR_NONE) + avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset - 8, size + 8); + if (avierr != error::NONE) break; } } -error: - if (chunkdata != nullptr) - free(chunkdata); return avierr; } @@ -2249,7 +2564,7 @@ error: -------------------------------------------------*/ /** - * @fn static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1) + * @fn static avi_error parse_idx1_chunk(avi_file *file, std::uint64_t baseoffset, avi_chunk *idx1) * * @brief Parse index 1 chunk. * @@ -2260,48 +2575,39 @@ error: * @return An avi_error. */ -static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1) +avi_file::error avi_file_impl::parse_idx1_chunk(std::uint64_t baseoffset, avi_chunk const &idx1) { - UINT8 *chunkdata = nullptr; - avi_error avierr; - UINT32 entries; - UINT32 entry; + error avierr; /* read the data */ - avierr = read_chunk_data(file, idx1, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + avierr = read_chunk_data(idx1, chunkdata); + if (avierr != error::NONE) + return avierr; /* loop over entries */ - entries = idx1->size / 16; - for (entry = 0; entry < entries; entry++) + std::uint32_t const entries = idx1.size / 16; + for (std::uint32_t entry = 0; entry < entries; entry++) { - const UINT8 *base = &chunkdata[entry * 16]; - UINT32 chunkid = fetch_32bits(&base[0]); - UINT32 offset = fetch_32bits(&base[8]); - UINT32 size = fetch_32bits(&base[12]); - avi_stream *stream; + std::uint8_t const *const base = &chunkdata[entry * 16]; + std::uint32_t const chunkid = fetch_32bits(&base[0]); + std::uint32_t const offset = fetch_32bits(&base[8]); + std::uint32_t const size = fetch_32bits(&base[12]); int streamnum; /* determine the stream index */ streamnum = ((chunkid >> 8) & 0xff) - '0'; streamnum += 10 * ((chunkid & 0xff) - '0'); - if (streamnum >= file->streams) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - stream = &file->stream[streamnum]; + if (streamnum >= m_streams.size()) + return error::INVALID_DATA; + avi_stream &stream = m_streams[streamnum]; /* set the appropriate entry */ - avierr = set_stream_chunk_info(stream, stream->chunks++, baseoffset + offset, size + 8); - if (avierr != AVIERR_NONE) - goto error; + avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset, size + 8); + if (avierr != error::NONE) + return avierr; } -error: - if (chunkdata != nullptr) - free(chunkdata); return avierr; } @@ -2311,7 +2617,7 @@ error: -------------------------------------------------*/ /** - * @fn static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength) + * @fn static avi_error chunk_open(avi_file *file, std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength) * * @brief Queries if a given chunk open. * @@ -2323,57 +2629,55 @@ error: * @return An avi_error. */ -static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength) +avi_file::error avi_file_impl::chunk_open(std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength) { - file_error filerr; - avi_chunk *chunk; - UINT32 written; - /* if we're out of stack entries, bail */ - if (file->chunksp >= ARRAY_LENGTH(file->chunkstack)) - return AVIERR_STACK_TOO_DEEP; - chunk = &file->chunkstack[file->chunksp++]; + if (m_chunksp >= m_chunkstack.size()) + return error::STACK_TOO_DEEP; + avi_chunk &chunk = m_chunkstack[m_chunksp++]; /* set up the chunk information */ - chunk->offset = file->writeoffs; - chunk->size = estlength; - chunk->type = type; - chunk->listtype = listtype; + chunk.offset = m_writeoffs; + chunk.size = estlength; + chunk.type = type; + chunk.listtype = listtype; /* non-list types */ if (type != CHUNKTYPE_RIFF && type != CHUNKTYPE_LIST) { - UINT8 buffer[8]; + std::uint8_t buffer[8]; /* populate the header */ - put_32bits(&buffer[0], chunk->type); - put_32bits(&buffer[4], chunk->size); + put_32bits(&buffer[0], chunk.type); + put_32bits(&buffer[4], chunk.size); /* write the header */ - filerr = osd_write(file->file, buffer, file->writeoffs, sizeof(buffer), &written); - if (filerr != FILERR_NONE || written != sizeof(buffer)) - return AVIERR_WRITE_ERROR; - file->writeoffs += written; + std::uint32_t written; + osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); + if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + return error::WRITE_ERROR; + m_writeoffs += written; } /* list types */ else { - UINT8 buffer[12]; + std::uint8_t buffer[12]; /* populate the header */ - put_32bits(&buffer[0], chunk->type); - put_32bits(&buffer[4], chunk->size); - put_32bits(&buffer[8], chunk->listtype); + put_32bits(&buffer[0], chunk.type); + put_32bits(&buffer[4], chunk.size); + put_32bits(&buffer[8], chunk.listtype); /* write the header */ - filerr = osd_write(file->file, buffer, file->writeoffs, sizeof(buffer), &written); - if (filerr != FILERR_NONE || written != sizeof(buffer)) - return AVIERR_WRITE_ERROR; - file->writeoffs += written; + std::uint32_t written; + osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); + if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + return error::WRITE_ERROR; + m_writeoffs += written; } - return AVIERR_NONE; + return error::NONE; } @@ -2391,32 +2695,31 @@ static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 * @return An avi_error. */ -static avi_error chunk_close(avi_file *file) +avi_file::error avi_file_impl::chunk_close() { - avi_chunk *chunk = &file->chunkstack[--file->chunksp]; - UINT64 chunksize = file->writeoffs - (chunk->offset + 8); - UINT32 written; + avi_chunk const &chunk = m_chunkstack[--m_chunksp]; + std::uint64_t const chunksize = m_writeoffs - (chunk.offset + 8); /* error if we don't fit into 32 bits */ - if (chunksize != (UINT32)chunksize) - return AVIERR_INVALID_DATA; + if (chunksize != std::uint32_t(chunksize)) + return error::INVALID_DATA; /* write the final size if it is different from the guess */ - if (chunk->size != chunksize) + if (chunk.size != chunksize) { - file_error filerr; - UINT8 buffer[4]; + std::uint8_t buffer[4]; - put_32bits(&buffer[0], (UINT32)chunksize); - filerr = osd_write(file->file, buffer, chunk->offset + 4, sizeof(buffer), &written); - if (filerr != FILERR_NONE || written != sizeof(buffer)) - return AVIERR_WRITE_ERROR; + put_32bits(&buffer[0], std::uint32_t(chunksize)); + std::uint32_t written; + osd_file::error const filerr = m_file->write(buffer, chunk.offset + 4, sizeof(buffer), written); + if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + return error::WRITE_ERROR; } /* round up to the next word */ - file->writeoffs += chunksize & 1; + m_writeoffs += chunksize & 1; - return AVIERR_NONE; + return error::NONE; } @@ -2425,7 +2728,7 @@ static avi_error chunk_close(avi_file *file) -------------------------------------------------*/ /** - * @fn static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length) + * @fn static avi_error chunk_write(avi_file *file, std::uint32_t type, const void *data, std::uint32_t length) * * @brief Chunk write. * @@ -2437,67 +2740,63 @@ static avi_error chunk_close(avi_file *file) * @return An avi_error. */ -static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length) +avi_file::error avi_file_impl::chunk_write(std::uint32_t type, const void *data, std::uint32_t length) { - file_error filerr; - avi_error avierr; - UINT32 idxreserve; - UINT32 written; + error avierr; /* if we are the first RIFF, we must reserve enough space for the IDX chunk */ - idxreserve = 0; - if (file->riffbase == 0 && type != CHUNKTYPE_IDX1) - idxreserve = compute_idx1_size(file); + std::uint32_t const idxreserve = (m_riffbase == 0 && type != CHUNKTYPE_IDX1) ? compute_idx1_size() : 0; /* if we are getting too big, split the RIFF */ /* note that we ignore writes before the current RIFF base, as those are assumed to be overwrites of a chunk from the previous RIFF */ - if (file->writeoffs >= file->riffbase && file->writeoffs + length + idxreserve - file->riffbase >= MAX_RIFF_SIZE) + if ((m_writeoffs >= m_riffbase) && ((m_writeoffs + length + idxreserve - m_riffbase) >= MAX_RIFF_SIZE)) { /* close the movi list */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; /* write the idx1 chunk if this is the first */ - if (file->riffbase == 0) + if (m_riffbase == 0) { - avierr = write_idx1_chunk(file); - if (avierr != AVIERR_NONE) + avierr = write_idx1_chunk(); + if (avierr != error::NONE) return avierr; } /* close the RIFF */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; /* open a new RIFF */ - file->riffbase = file->writeoffs; - avierr = chunk_open(file, CHUNKTYPE_RIFF, LISTTYPE_AVIX, 0); - if (avierr != AVIERR_NONE) + m_riffbase = m_writeoffs; + avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVIX, 0); + if (avierr != error::NONE) return avierr; /* open a nested movi list */ - file->saved_movi_offset = file->writeoffs; - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); - if (avierr != AVIERR_NONE) + m_saved_movi_offset = m_writeoffs; + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); + if (avierr != error::NONE) return avierr; } /* open the chunk */ - avierr = chunk_open(file, type, 0, length); - if (avierr != AVIERR_NONE) + avierr = chunk_open(type, 0, length); + if (avierr != error::NONE) return avierr; /* write the data */ - filerr = osd_write(file->file, data, file->writeoffs, length, &written); - if (filerr != FILERR_NONE || written != length) - return AVIERR_WRITE_ERROR; - file->writeoffs += written; + std::uint32_t written; + osd_file::error const filerr = m_file->write(data, m_writeoffs, length, written); + if (filerr != osd_file::error::NONE || written != length) + return error::WRITE_ERROR; + m_writeoffs += written; /* close the chunk */ - return chunk_close(file); + return chunk_close(); } @@ -2509,7 +2808,7 @@ static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT -------------------------------------------------*/ /** - * @fn static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write) + * @fn static avi_error chunk_overwrite(avi_file *file, std::uint32_t type, const void *data, std::uint32_t length, std::uint64_t *offset, int initial_write) * * @brief Chunk overwrite. * @@ -2523,28 +2822,27 @@ static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT * @return An avi_error. */ -static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write) +avi_file::error avi_file_impl::chunk_overwrite(std::uint32_t type, const void *data, std::uint32_t length, std::uint64_t &offset, bool initial_write) { - UINT64 savedoffset = 0; - avi_error avierr; + std::uint64_t savedoffset = 0; /* if this is our initial write, save the offset */ if (initial_write) - *offset = file->writeoffs; + offset = m_writeoffs; /* otherwise, remember the current write offset and replace it with the original */ else { - savedoffset = file->writeoffs; - file->writeoffs = *offset; + savedoffset = m_writeoffs; + m_writeoffs = offset; } /* write the chunk */ - avierr = chunk_write(file, type, data, length); + error const avierr = chunk_write(type, data, length); /* if this isn't the initial write, restore the previous offset */ if (!initial_write) - file->writeoffs = savedoffset; + m_writeoffs = savedoffset; return avierr; } @@ -2565,67 +2863,66 @@ static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, * @return An avi_error. */ -static avi_error write_initial_headers(avi_file *file) +avi_file::error avi_file_impl::write_initial_headers() { - avi_error avierr; - int strnum; + error avierr; /* reset the write pointer */ - file->writeoffs = 0; + m_writeoffs = 0; /* open a RIFF chunk */ - avierr = chunk_open(file, CHUNKTYPE_RIFF, LISTTYPE_AVI, 0); - if (avierr != AVIERR_NONE) + avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVI, 0); + if (avierr != error::NONE) return avierr; /* open a hdlr LIST */ - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_HDRL, 0); - if (avierr != AVIERR_NONE) + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_HDRL, 0); + if (avierr != error::NONE) return avierr; /* write an avih chunk */ - avierr = write_avih_chunk(file, TRUE); - if (avierr != AVIERR_NONE) + avierr = write_avih_chunk(true); + if (avierr != error::NONE) return avierr; /* for each stream, write a strl LIST */ - for (strnum = 0; strnum < file->streams; strnum++) + for (int strnum = 0; strnum < m_streams.size(); strnum++) { /* open a strl LIST */ - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_STRL, 0); - if (avierr != AVIERR_NONE) + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_STRL, 0); + if (avierr != error::NONE) return avierr; /* write the strh chunk */ - avierr = write_strh_chunk(file, &file->stream[strnum], TRUE); - if (avierr != AVIERR_NONE) + avierr = write_strh_chunk(m_streams[strnum], true); + if (avierr != error::NONE) return avierr; /* write the strf chunk */ - avierr = write_strf_chunk(file, &file->stream[strnum]); - if (avierr != AVIERR_NONE) + avierr = write_strf_chunk(m_streams[strnum]); + if (avierr != error::NONE) return avierr; /* write the indx chunk */ - avierr = write_indx_chunk(file, &file->stream[strnum], TRUE); - if (avierr != AVIERR_NONE) + avierr = write_indx_chunk(m_streams[strnum], true); + if (avierr != error::NONE) return avierr; /* close the strl LIST */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; } /* close the hdlr LIST */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; /* open a movi LIST */ - file->saved_movi_offset = file->writeoffs; - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); - if (avierr != AVIERR_NONE) + m_saved_movi_offset = m_writeoffs; + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); + if (avierr != error::NONE) return avierr; return avierr; @@ -2648,23 +2945,23 @@ static avi_error write_initial_headers(avi_file *file) * @return An avi_error. */ -static avi_error write_avih_chunk(avi_file *file, int initial_write) +avi_file::error avi_file_impl::write_avih_chunk(bool initial_write) { - avi_stream *video = get_video_stream(file); - UINT8 buffer[56]; + avi_stream *video = get_video_stream(); + std::uint8_t buffer[56]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); - put_32bits(&buffer[0], 1000000 * (INT64)video->scale / video->rate); /* dwMicroSecPerFrame */ + put_32bits(&buffer[0], 1000000 * std::int64_t(video->scale()) / video->rate()); /* dwMicroSecPerFrame */ put_32bits(&buffer[12], AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* dwFlags */ - put_32bits(&buffer[16], video->samples); /* dwTotalFrames */ - put_32bits(&buffer[24], file->streams); /* dwStreams */ - put_32bits(&buffer[32], video->width); /* dwWidth */ - put_32bits(&buffer[36], video->height); /* dwHeight */ + put_32bits(&buffer[16], video->samples()); /* dwTotalFrames */ + put_32bits(&buffer[24], m_streams.size()); /* dwStreams */ + put_32bits(&buffer[32], video->width()); /* dwWidth */ + put_32bits(&buffer[36], video->height()); /* dwHeight */ /* (over)write the chunk */ - return chunk_overwrite(file, CHUNKTYPE_AVIH, buffer, sizeof(buffer), &file->saved_avih_offset, initial_write); + return chunk_overwrite(CHUNKTYPE_AVIH, buffer, sizeof(buffer), m_saved_avih_offset, initial_write); } @@ -2685,41 +2982,41 @@ static avi_error write_avih_chunk(avi_file *file, int initial_write) * @return An avi_error. */ -static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initial_write) +avi_file::error avi_file_impl::write_strh_chunk(avi_stream &stream, bool initial_write) { - UINT8 buffer[56]; + std::uint8_t buffer[56]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); - put_32bits(&buffer[0], stream->type); /* fccType */ - put_32bits(&buffer[20], stream->scale); /* dwScale */ - put_32bits(&buffer[24], stream->rate); /* dwRate */ - put_32bits(&buffer[32], stream->samples); /* dwLength */ + put_32bits(&buffer[0], stream.type()); /* fccType */ + put_32bits(&buffer[20], stream.scale()); /* dwScale */ + put_32bits(&buffer[24], stream.rate()); /* dwRate */ + put_32bits(&buffer[32], stream.samples()); /* dwLength */ put_32bits(&buffer[40], 10000); /* dwQuality */ /* video-stream specific data */ - if (stream->type == STREAMTYPE_VIDS) + if (stream.type() == STREAMTYPE_VIDS) { put_32bits(&buffer[4], /* fccHandler */ - (stream->format == FORMAT_HFYU) ? HANDLER_HFYU : HANDLER_DIB); + (stream.format() == FORMAT_HFYU) ? HANDLER_HFYU : HANDLER_DIB); put_32bits(&buffer[36], /* dwSuggestedBufferSize */ - stream->width * stream->height * 4); - put_16bits(&buffer[52], stream->width); /* rcFrame.right */ - put_16bits(&buffer[54], stream->height); /* rcFrame.bottom */ + stream.width() * stream.height() * 4); + put_16bits(&buffer[52], stream.width()); /* rcFrame.right */ + put_16bits(&buffer[54], stream.height()); /* rcFrame.bottom */ } /* audio-stream specific data */ - if (stream->type == STREAMTYPE_AUDS) + if (stream.type() == STREAMTYPE_AUDS) { put_32bits(&buffer[36], /* dwSuggestedBufferSize */ - stream->samplerate * stream->channels * (stream->samplebits / 8)); + stream.samplerate() * stream.bytes_per_sample()); put_32bits(&buffer[44], /* dwSampleSize */ - stream->channels * (stream->samplebits / 8)); + stream.bytes_per_sample()); } /* write the chunk */ - return chunk_overwrite(file, CHUNKTYPE_STRH, buffer, sizeof(buffer), &stream->saved_strh_offset, initial_write); + return chunk_overwrite(CHUNKTYPE_STRH, buffer, sizeof(buffer), stream.saved_strh_offset(), initial_write); } @@ -2739,51 +3036,51 @@ static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initia * @return An avi_error. */ -static avi_error write_strf_chunk(avi_file *file, avi_stream *stream) +avi_file::error avi_file_impl::write_strf_chunk(avi_stream const &stream) { /* video stream */ - if (stream->type == STREAMTYPE_VIDS) + if (stream.type() == STREAMTYPE_VIDS) { - UINT8 buffer[40]; + std::uint8_t buffer[40]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); put_32bits(&buffer[0], sizeof(buffer)); /* biSize */ - put_32bits(&buffer[4], stream->width); /* biWidth */ - put_32bits(&buffer[8], stream->height); /* biHeight */ + put_32bits(&buffer[4], stream.width()); /* biWidth */ + put_32bits(&buffer[8], stream.height()); /* biHeight */ put_16bits(&buffer[12], 1); /* biPlanes */ - put_16bits(&buffer[14], stream->depth); /* biBitCount */ - put_32bits(&buffer[16], stream->format); /* biCompression */ + put_16bits(&buffer[14], stream.depth()); /* biBitCount */ + put_32bits(&buffer[16], stream.format()); /* biCompression */ put_32bits(&buffer[20], /* biSizeImage */ - stream->width * stream->height * (stream->depth + 7) / 8); + stream.width() * stream.height() * (stream.depth() + 7) / 8); /* write the chunk */ - return chunk_write(file, CHUNKTYPE_STRF, buffer, sizeof(buffer)); + return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer)); } /* audio stream */ - if (stream->type == STREAMTYPE_AUDS) + if (stream.type() == STREAMTYPE_AUDS) { - UINT8 buffer[16]; + std::uint8_t buffer[16]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); put_16bits(&buffer[0], 1); /* wFormatTag */ - put_16bits(&buffer[2], stream->channels); /* nChannels */ - put_32bits(&buffer[4], stream->samplerate); /* nSamplesPerSec */ + put_16bits(&buffer[2], stream.channels()); /* nChannels */ + put_32bits(&buffer[4], stream.samplerate()); /* nSamplesPerSec */ put_32bits(&buffer[8], /* nAvgBytesPerSec */ - stream->samplerate * stream->channels * (stream->samplebits / 8)); + stream.samplerate() * stream.bytes_per_sample()); put_16bits(&buffer[12], /* nBlockAlign */ - stream->channels * (stream->samplebits / 8)); - put_16bits(&buffer[14], stream->samplebits); /* wBitsPerSample */ + stream.bytes_per_sample()); + put_16bits(&buffer[14], stream.samplebits()); /* wBitsPerSample */ /* write the chunk */ - return chunk_write(file, CHUNKTYPE_STRF, buffer, sizeof(buffer)); + return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer)); } - return AVIERR_INVALID_DATA; + return error::INVALID_DATA; } @@ -2804,35 +3101,30 @@ static avi_error write_strf_chunk(avi_file *file, avi_stream *stream) * @return An avi_error. */ -static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initial_write) +avi_file::error avi_file_impl::write_indx_chunk(avi_stream &stream, bool initial_write) { - UINT8 buffer[24 + 16 * MAX_AVI_SIZE_IN_GB / 4]; - UINT32 chunkid, indexchunkid; - UINT32 master_entries = 0; + std::uint8_t buffer[24 + 16 * MAX_AVI_SIZE_IN_GB / 4]; + std::uint32_t master_entries = 0; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); /* construct the chunk ID and index chunk ID */ - chunkid = get_chunkid_for_stream(file, stream); - indexchunkid = AVI_FOURCC('i', 'x', '0' + (stream - file->stream) / 10, '0' + (stream - file->stream) % 10); + std::uint32_t const chunkid = get_chunkid_for_stream(&stream); + std::uint32_t const indexchunkid = AVI_FOURCC('i', 'x', '0' + (&stream - &m_streams[0]) / 10, '0' + (&stream - &m_streams[0]) % 10); /* loop over chunks of 4GB and write out indexes for them first */ - if (!initial_write && file->riffbase != 0) + if (!initial_write && m_riffbase != 0) { - UINT64 currentbase; - for (currentbase = 0; currentbase < file->writeoffs; currentbase += FOUR_GB) + for (std::uint64_t currentbase = 0; currentbase < m_writeoffs; currentbase += FOUR_GB) { - UINT64 currentend = currentbase + FOUR_GB; - UINT32 chunks_this_index = 0; - UINT32 bytes_this_index = 0; - avi_error avierr; - UINT32 chunknum; - UINT8 *tempbuf; + std::uint64_t const currentend = currentbase + FOUR_GB; + std::uint32_t chunks_this_index = 0; + std::uint32_t bytes_this_index = 0; /* count chunks that are in this region */ - for (chunknum = 0; chunknum < stream->chunks; chunknum++) - if (stream->chunk[chunknum].offset >= currentbase && stream->chunk[chunknum].offset < currentend) + for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++) + if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend) chunks_this_index++; /* if no chunks, skip */ @@ -2840,10 +3132,10 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia continue; /* allocate memory */ - tempbuf = (UINT8 *)malloc(24 + 8 * chunks_this_index); - if (tempbuf == nullptr) - return AVIERR_NO_MEMORY; - memset(tempbuf, 0, 24 + 8 * chunks_this_index); + std::unique_ptr<std::uint8_t []> tempbuf; + try { tempbuf.reset(new std::uint8_t[24 + 8 * chunks_this_index]); } + catch (...) { return error::NO_MEMORY; } + std::memset(&tempbuf[0], 0, 24 + 8 * chunks_this_index); /* make a regular index */ put_16bits(&tempbuf[0], 2); /* wLongsPerEntry */ @@ -2855,28 +3147,27 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia /* now fill in the indexes */ chunks_this_index = 0; - for (chunknum = 0; chunknum < stream->chunks; chunknum++) - if (stream->chunk[chunknum].offset >= currentbase && stream->chunk[chunknum].offset < currentend) + for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++) + if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend) { - put_32bits(&tempbuf[24 + 8 * chunks_this_index + 0], stream->chunk[chunknum].offset + 8 - currentbase); - put_32bits(&tempbuf[24 + 8 * chunks_this_index + 4], stream->chunk[chunknum].length - 8); - bytes_this_index += stream->chunk[chunknum].length; + put_32bits(&tempbuf[24 + 8 * chunks_this_index + 0], stream.chunk(chunknum).offset + 8 - currentbase); + put_32bits(&tempbuf[24 + 8 * chunks_this_index + 4], stream.chunk(chunknum).length - 8); + bytes_this_index += stream.chunk(chunknum).length; chunks_this_index++; } /* write the offset of this index to the master table */ - put_64bits(&buffer[24 + 16 * master_entries + 0], file->writeoffs); + put_64bits(&buffer[24 + 16 * master_entries + 0], m_writeoffs); put_32bits(&buffer[24 + 16 * master_entries + 8], 24 + 8 * chunks_this_index + 8); - if (stream->type == STREAMTYPE_VIDS) + if (stream.type() == STREAMTYPE_VIDS) put_32bits(&buffer[24 + 16 * master_entries + 12], chunks_this_index); - else if (stream->type == STREAMTYPE_AUDS) - put_32bits(&buffer[24 + 16 * master_entries + 12], bytes_this_index / ((stream->samplebits / 8) * stream->channels)); + else if (stream.type() == STREAMTYPE_AUDS) + put_32bits(&buffer[24 + 16 * master_entries + 12], bytes_this_index / stream.bytes_per_sample()); master_entries++; /* write the index */ - avierr = chunk_write(file, indexchunkid, tempbuf, 24 + 8 * chunks_this_index); - free(tempbuf); - if (avierr != AVIERR_NONE) + error const avierr = chunk_write(indexchunkid, &tempbuf[0], 24 + 8 * chunks_this_index); + if (avierr != error::NONE) return avierr; } } @@ -2892,7 +3183,7 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia } /* (over)write the chunk */ - return chunk_overwrite(file, (master_entries == 0) ? CHUNKTYPE_JUNK : CHUNKTYPE_INDX, buffer, sizeof(buffer), &stream->saved_indx_offset, initial_write); + return chunk_overwrite((master_entries == 0) ? CHUNKTYPE_JUNK : CHUNKTYPE_INDX, buffer, sizeof(buffer), stream.saved_indx_offset(), initial_write); } @@ -2910,47 +3201,42 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia * @return An avi_error. */ -static avi_error write_idx1_chunk(avi_file *file) +avi_file::error avi_file_impl::write_idx1_chunk() { - UINT32 tempbuflength = compute_idx1_size(file) - 8; - UINT32 curchunk[2] = { 0 }; - UINT32 curoffset; - avi_error avierr; - UINT8 *tempbuf; + std::uint32_t const tempbuflength = compute_idx1_size() - 8; + std::uint32_t curchunk[2] = { 0 }; /* allocate a temporary buffer */ - tempbuf = (UINT8 *)malloc(tempbuflength); - if (tempbuf == nullptr) - return AVIERR_NO_MEMORY; + std::unique_ptr<std::uint8_t []> tempbuf; + try { tempbuf.reset(new std::uint8_t[tempbuflength]); } + catch (...) { return error::NO_MEMORY; } /* fill it in */ - for (curoffset = 0; curoffset < tempbuflength; curoffset += 16) + for (std::uint32_t curoffset = 0; curoffset < tempbuflength; curoffset += 16) { - UINT64 minoffset = ~(UINT64)0; - int strnum, minstr = 0; + std::uint64_t minoffset = ~std::uint64_t(0); + int minstr = 0; /* determine which stream has the next chunk */ - for (strnum = 0; strnum < file->streams; strnum++) - if (curchunk[strnum] < file->stream[strnum].chunks && file->stream[strnum].chunk[curchunk[strnum]].offset < minoffset) + for (int strnum = 0; strnum < m_streams.size(); strnum++) + if (curchunk[strnum] < m_streams[strnum].chunks() && m_streams[strnum].chunk(curchunk[strnum]).offset < minoffset) { - minoffset = file->stream[strnum].chunk[curchunk[strnum]].offset; + minoffset = m_streams[strnum].chunk(curchunk[strnum]).offset; minstr = strnum; } /* make an entry for this index */ - put_32bits(&tempbuf[curoffset + 0], get_chunkid_for_stream(file, &file->stream[minstr])); + put_32bits(&tempbuf[curoffset + 0], get_chunkid_for_stream(&m_streams[minstr])); put_32bits(&tempbuf[curoffset + 4], 0x0010 /* AVIIF_KEYFRAME */); - put_32bits(&tempbuf[curoffset + 8], minoffset - (file->saved_movi_offset + 8)); - put_32bits(&tempbuf[curoffset + 12], file->stream[minstr].chunk[curchunk[minstr]].length - 8); + put_32bits(&tempbuf[curoffset + 8], minoffset - (m_saved_movi_offset + 8)); + put_32bits(&tempbuf[curoffset + 12], m_streams[minstr].chunk(curchunk[minstr]).length - 8); /* advance the chunk counter for this stream */ curchunk[minstr]++; } /* write the chunk */ - avierr = chunk_write(file, CHUNKTYPE_IDX1, tempbuf, tempbuflength); - free(tempbuf); - return avierr; + return chunk_write(CHUNKTYPE_IDX1, &tempbuf[0], tempbuflength); } @@ -2969,31 +3255,31 @@ static avi_error write_idx1_chunk(avi_file *file) * @return An avi_error. */ -static avi_error soundbuf_initialize(avi_file *file) +avi_file::error avi_file_impl::soundbuf_initialize() { - avi_stream *audio = get_audio_stream(file, 0, nullptr); - avi_stream *video = get_video_stream(file); + int offset; + avi_stream *const audio = get_audio_stream(0, offset); + avi_stream *const video = get_video_stream(); /* we require a video stream */ if (video == nullptr) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + return error::UNSUPPORTED_VIDEO_FORMAT; /* skip if no audio stream */ if (audio == nullptr) - return AVIERR_NONE; + return error::NONE; /* determine the number of samples we want in our buffer; 2 seconds should be enough */ - file->soundbuf_samples = file->info.audio_samplerate * SOUND_BUFFER_MSEC / 1000; + m_soundbuf_samples = m_info.audio_samplerate * SOUND_BUFFER_MSEC / 1000; /* allocate a buffer */ - file->soundbuf = (INT16 *)malloc(file->soundbuf_samples * file->info.audio_channels * sizeof(file->soundbuf[0])); - if (file->soundbuf == nullptr) - return AVIERR_NO_MEMORY; - memset(file->soundbuf, 0, file->soundbuf_samples * file->info.audio_channels * sizeof(file->soundbuf[0])); + m_soundbuf.clear(); + try { m_soundbuf.resize(m_soundbuf_samples * m_info.audio_channels, 0); } + catch (...) { return error::NO_MEMORY; } /* determine the number of frames to be ahead (0.75secs) */ - file->soundbuf_frames = ((UINT64)video->rate * 75) / ((UINT64)video->scale * 100) + 1; - return AVIERR_NONE; + m_soundbuf_frames = (std::uint64_t(video->rate()) * 75) / (std::uint64_t(video->scale()) * 100) + 1; + return error::NONE; } @@ -3003,7 +3289,7 @@ static avi_error soundbuf_initialize(avi_file *file) -------------------------------------------------*/ /** - * @fn static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum) + * @fn static avi_error soundbuf_write_chunk(avi_file *file, std::uint32_t framenum) * * @brief Soundbuf write chunk. * @@ -3013,30 +3299,30 @@ static avi_error soundbuf_initialize(avi_file *file) * @return An avi_error. */ -static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum) +avi_file::error avi_file_impl::soundbuf_write_chunk(std::uint32_t framenum) { - avi_stream *stream = get_audio_stream(file, 0, nullptr); - avi_error avierr; - UINT32 length; + int offset; + avi_stream *const stream = get_audio_stream(0, offset); + std::uint32_t length; /* skip if no audio stream */ if (stream == nullptr) - return AVIERR_NONE; + return error::NONE; /* determine the length of this chunk */ if (framenum == 0) - length = framenum_to_samplenum(file, file->soundbuf_frames); + length = framenum_to_samplenum(m_soundbuf_frames); else - length = framenum_to_samplenum(file, framenum + 1 + file->soundbuf_frames) - framenum_to_samplenum(file, framenum + file->soundbuf_frames); - length *= stream->channels * sizeof(INT16); + length = framenum_to_samplenum(framenum + 1 + m_soundbuf_frames) - framenum_to_samplenum(framenum + m_soundbuf_frames); + length *= stream->channels() * sizeof(std::int16_t); /* then do the initial write */ - avierr = chunk_write(file, get_chunkid_for_stream(file, stream), file->soundbuf, length); - if (avierr != AVIERR_NONE) + error const avierr = chunk_write(get_chunkid_for_stream(stream), &m_soundbuf[0], length); + if (avierr != error::NONE) return avierr; /* set the info for this new chunk */ - return set_stream_chunk_info(stream, stream->chunks, file->writeoffs - length - 8, length + 8); + return stream->set_chunk_info(stream->chunks(), m_writeoffs - length - 8, length + 8); } @@ -3056,36 +3342,35 @@ static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum) * @return An avi_error. */ -static avi_error soundbuf_flush(avi_file *file, int only_flush_full) +avi_file::error avi_file_impl::soundbuf_flush(bool only_flush_full) { - avi_stream *stream = get_audio_stream(file, 0, nullptr); - INT32 channelsamples = file->soundbuf_samples; - INT32 processedsamples = 0; - UINT32 bytes_per_sample; - UINT32 finalchunks; - avi_error avierr; - UINT32 chunknum; - UINT32 chunkid; - int channel; + int offset; + avi_stream *const stream = get_audio_stream(0, offset); + std::int32_t channelsamples = m_soundbuf_samples; + std::int32_t processedsamples = 0; + std::uint32_t finalchunks; + error avierr; + std::uint32_t chunknum; + std::uint32_t chunkid; /* skip if no stream */ if (stream == nullptr) - return AVIERR_NONE; + return error::NONE; /* get the chunk ID for this stream */ - chunkid = get_chunkid_for_stream(file, stream); - bytes_per_sample = stream->channels * sizeof(INT16); - finalchunks = stream->chunks; + chunkid = get_chunkid_for_stream(stream); + std::uint32_t const bytes_per_sample = stream->channels() * sizeof(std::int16_t); + finalchunks = stream->chunks(); /* find out how many samples we've accumulated */ - for (channel = 0; channel < stream->channels; channel++) - channelsamples = MIN(channelsamples, file->soundbuf_chansamples[channel]); + for (int channel = 0; channel < stream->channels(); channel++) + channelsamples = (std::min<std::int32_t>)(channelsamples, m_soundbuf_chansamples[channel]); /* loop over pending sound chunks */ - for (chunknum = file->soundbuf_chunks; chunknum < stream->chunks; chunknum++) + for (chunknum = m_soundbuf_chunks; chunknum < stream->chunks(); chunknum++) { - avi_chunk_list *chunk = &stream->chunk[chunknum]; - UINT32 chunksamples = (chunk->length - 8) / bytes_per_sample; + avi_chunk_list &chunk = stream->chunk(chunknum); + std::uint32_t const chunksamples = (chunk.length - 8) / bytes_per_sample; /* stop if we don't have enough to satisfy this chunk */ if (only_flush_full && channelsamples < chunksamples) @@ -3094,696 +3379,300 @@ static avi_error soundbuf_flush(avi_file *file, int only_flush_full) /* if we don't have all the samples we need, pad with 0's */ if (channelsamples > 0 && channelsamples < chunksamples) { - if (processedsamples + chunksamples > file->soundbuf_samples) - return AVIERR_EXCEEDED_SOUND_BUFFER; - memset(&file->soundbuf[(processedsamples + channelsamples) * stream->channels], 0, (chunksamples - channelsamples) * bytes_per_sample); + if (processedsamples + chunksamples > m_soundbuf_samples) + return error::EXCEEDED_SOUND_BUFFER; + std::memset(&m_soundbuf[(processedsamples + channelsamples) * stream->channels()], 0, (chunksamples - channelsamples) * bytes_per_sample); } /* if we're completely out of samples, clear the buffer entirely and use the end */ else if (channelsamples <= 0) { - processedsamples = file->soundbuf_samples - chunksamples; - memset(&file->soundbuf[processedsamples * stream->channels], 0, chunksamples * bytes_per_sample); + processedsamples = m_soundbuf_samples - chunksamples; + std::memset(&m_soundbuf[processedsamples * stream->channels()], 0, chunksamples * bytes_per_sample); chunkid = CHUNKTYPE_JUNK; finalchunks--; } /* copy the sample data in */ - avierr = chunk_overwrite(file, chunkid, &file->soundbuf[processedsamples * stream->channels], chunk->length - 8, &chunk->offset, FALSE); - if (avierr != AVIERR_NONE) + avierr = chunk_overwrite(chunkid, &m_soundbuf[processedsamples * stream->channels()], chunk.length - 8, chunk.offset, false); + if (avierr != error::NONE) return avierr; /* add up the samples */ if (channelsamples > chunksamples) - file->info.audio_numsamples = stream->samples += chunksamples; + m_info.audio_numsamples = stream->add_samples(chunksamples); else if (channelsamples > 0) - file->info.audio_numsamples = stream->samples += channelsamples; + m_info.audio_numsamples = stream->add_samples(channelsamples); /* advance past those */ processedsamples += chunksamples; channelsamples -= chunksamples; - channelsamples = MAX(0, channelsamples); + channelsamples = (std::max)(0, channelsamples); } /* if we have a non-zero offset, shift everything down */ if (processedsamples > 0) { /* first account for the samples we processed */ - memmove(&file->soundbuf[0], &file->soundbuf[processedsamples * stream->channels], (file->soundbuf_samples - processedsamples) * bytes_per_sample); - for (channel = 0; channel < stream->channels; channel++) - file->soundbuf_chansamples[channel] -= processedsamples; + std::memmove(&m_soundbuf[0], &m_soundbuf[processedsamples * stream->channels()], (m_soundbuf_samples - processedsamples) * bytes_per_sample); + for (int channel = 0; channel < stream->channels(); channel++) + m_soundbuf_chansamples[channel] -= processedsamples; } /* update the final chunk count */ if (!only_flush_full) - stream->chunks = finalchunks; + stream->set_chunks(finalchunks); /* account for flushed chunks */ - file->soundbuf_chunks = chunknum; - return AVIERR_NONE; + m_soundbuf_chunks = chunknum; + return error::NONE; } /*------------------------------------------------- - rgb32_compress_to_rgb - "compress" an RGB32 - bitmap to an RGB encoded frame + printf_chunk_recursive - print information + about a chunk recursively -------------------------------------------------*/ /** - * @fn static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, UINT8 *data, UINT32 numbytes) - * - * @brief RGB 32 compress to RGB. + * @fn static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent) * - * @param [in,out] stream If non-null, the stream. - * @param bitmap The bitmap. - * @param [in,out] data If non-null, the data. - * @param numbytes The numbytes. + * @brief Printf chunk recursive. * - * @return An avi_error. + * @param [in,out] file If non-null, the file. + * @param [in,out] container If non-null, the container. + * @param indent The indent. */ -static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, UINT8 *data, UINT32 numbytes) +void avi_file_impl::printf_chunk_recursive(avi_chunk const *container, int indent) { - int height = MIN(stream->height, bitmap.height()); - int width = MIN(stream->width, bitmap.width()); - UINT8 *dataend = data + numbytes; - int x, y; + char size[20], offset[20]; + avi_chunk curchunk; + error avierr; - /* compressed video */ - for (y = 0; y < height; y++) + /* iterate over chunks in this container */ + for (avierr = get_first_chunk(container, curchunk); avierr == error::NONE; avierr = get_next_chunk(container, curchunk)) { - const UINT32 *source = &bitmap.pix32(y); - UINT8 *dest = data + (stream->height - 1 - y) * stream->width * 3; - - for (x = 0; x < width && dest < dataend; x++) - { - rgb_t pix = *source++; - *dest++ = pix.b(); - *dest++ = pix.g(); - *dest++ = pix.r(); - } + std::uint32_t chunksize = curchunk.size; + bool recurse = false; - /* fill in any blank space on the right */ - for ( ; x < stream->width && dest < dataend; x++) - { - *dest++ = 0; - *dest++ = 0; - *dest++ = 0; - } - } + u64toa(curchunk.size, size); + u64toa(curchunk.offset, offset); + printf("%*schunk = %c%c%c%c, size=%s (%s)\n", indent, "", + std::uint8_t(curchunk.type >> 0), + std::uint8_t(curchunk.type >> 8), + std::uint8_t(curchunk.type >> 16), + std::uint8_t(curchunk.type >> 24), + size, offset); - /* fill in any blank space on the bottom */ - for ( ; y < stream->height; y++) - { - UINT8 *dest = data + (stream->height - 1 - y) * stream->width * 3; - for (x = 0; x < stream->width && dest < dataend; x++) + /* certain chunks are just containers; recurse into them */ + switch (curchunk.type) { - *dest++ = 0; - *dest++ = 0; - *dest++ = 0; + /* basic containers */ + case CHUNKTYPE_RIFF: + case CHUNKTYPE_LIST: + printf("%*stype = %c%c%c%c\n", indent, "", + std::uint8_t(curchunk.listtype >> 0), + std::uint8_t(curchunk.listtype >> 8), + std::uint8_t(curchunk.listtype >> 16), + std::uint8_t(curchunk.listtype >> 24)); + recurse = true; + chunksize = 0; + break; } - } - - return AVIERR_NONE; -} - - -/*------------------------------------------------- - yuv_decompress_to_yuy16 - decompress a YUV - encoded frame to a YUY16 bitmap --------------------------------------------------*/ - -/** - * @fn static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) - * - * @brief Yuv decompress to yuy 16. - * - * @param [in,out] stream If non-null, the stream. - * @param data The data. - * @param numbytes The numbytes. - * @param [in,out] bitmap The bitmap. - * - * @return An avi_error. - */ -static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) -{ - const UINT16 *dataend = (const UINT16 *)(data + numbytes); - int x, y; - - /* compressed video */ - for (y = 0; y < stream->height; y++) - { - const UINT16 *source = (const UINT16 *)data + y * stream->width; - UINT16 *dest = &bitmap.pix16(y); - - /* switch off the compression */ - switch (stream->format) + /* print data within the chunk */ + if (chunksize > 0 && curchunk.size < 1024 * 1024) { - case FORMAT_UYVY: - for (x = 0; x < stream->width && source < dataend; x++) - *dest++ = *source++; - break; + std::unique_ptr<std::uint8_t []> data; - case FORMAT_VYUY: - case FORMAT_YUY2: - for (x = 0; x < stream->width && source < dataend; x++) + /* read the data for a chunk */ + avierr = read_chunk_data(curchunk, data); + if (avierr == error::NONE) + { + std::uint32_t const bytes = (std::min)(std::uint32_t(512), chunksize); + for (std::uint32_t i = 0; i < bytes; i++) { - UINT16 pix = *source++; - *dest++ = (pix >> 8) | (pix << 8); + if (i % 16 == 0) printf("%*s ", indent, ""); + printf("%02X ", data[i]); + if (i % 16 == 15) printf("\n"); } - break; + if (chunksize % 16 != 0) printf("\n"); + } } + + /* if we're recursing, dive down */ + if (recurse) + printf_chunk_recursive(&curchunk, indent + 4); } - return AVIERR_NONE; + /* if we didn't get a legitimate error, indicate that */ + if (avierr != error::END) + printf("[chunk error %d]\n", int(avierr)); } +} // anonymous namespace + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ /*------------------------------------------------- - yuy16_compress_to_yuy - "compress" a YUY16 - bitmap to a YUV encoded frame + avi_open - open an AVI movie file for read -------------------------------------------------*/ /** - * @fn static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, UINT8 *data, UINT32 numbytes) + * @fn avi_error avi_open(const char *filename, avi_file **file) * - * @brief Yuy 16 compress to yuy. + * @brief Queries if a given avi open. * - * @param [in,out] stream If non-null, the stream. - * @param bitmap The bitmap. - * @param [in,out] data If non-null, the data. - * @param numbytes The numbytes. + * @param filename Filename of the file. + * @param [in,out] file If non-null, the file. * * @return An avi_error. */ -static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, UINT8 *data, UINT32 numbytes) +avi_file::error avi_file::open(std::string const &filename, ptr &file) { - const UINT16 *dataend = (const UINT16 *)(data + numbytes); - int x, y; - - /* compressed video */ - for (y = 0; y < stream->height; y++) - { - const UINT16 *source = &bitmap.pix16(y); - UINT16 *dest = (UINT16 *)data + y * stream->width; + /* open the file */ + osd_file::ptr f; + std::uint64_t length; + if (osd_file::open(filename, OPEN_FLAG_READ, f, length) != osd_file::error::NONE) + return error::CANT_OPEN_FILE; - /* switch off the compression */ - switch (stream->format) - { - case FORMAT_UYVY: - for (x = 0; x < stream->width && dest < dataend; x++) - *dest++ = *source++; - break; + /* allocate the file */ + std::unique_ptr<avi_file_impl> newfile; + try { newfile = std::make_unique<avi_file_impl>(std::move(f), length); } + catch (...) { return error::NO_MEMORY; } - case FORMAT_VYUY: - case FORMAT_YUY2: - for (x = 0; x < stream->width && dest < dataend; x++) - { - UINT16 pix = *source++; - *dest++ = (pix >> 8) | (pix << 8); - } - break; - } - } + /* parse the data */ + error const avierr = newfile->read_movie_data(); + if (avierr != error::NONE) + return avierr; - return AVIERR_NONE; + file = std::move(newfile); + return error::NONE; } /*------------------------------------------------- - huffyuv_extract_tables - extract HuffYUV - tables + avi_create - create a new AVI movie file -------------------------------------------------*/ /** - * @fn static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size) + * @fn avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file) * - * @brief Huffyuv extract tables. + * @brief Avi create. * - * @param [in,out] stream If non-null, the stream. - * @param chunkdata The chunkdata. - * @param size The size. + * @param filename Filename of the file. + * @param info The information. + * @param [in,out] file If non-null, the file. * * @return An avi_error. */ -static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size) +avi_file::error avi_file::create(std::string const &filename, movie_info const &info, ptr &file) { - const UINT8 *chunkend = chunkdata + size; - avi_error avierr = AVIERR_NONE; - int tabnum; - - /* allocate memory for the data */ - stream->huffyuv = (huffyuv_data *)malloc(sizeof(*stream->huffyuv)); - if (stream->huffyuv == nullptr) - { - avierr = AVIERR_NO_MEMORY; - goto error; - } - - /* extract predictor information */ - if (&chunkdata[40] >= chunkend) - return AVIERR_INVALID_DATA; - stream->huffyuv->predictor = chunkdata[40]; + /* validate video info */ + if ((info.video_format != 0 && info.video_format != FORMAT_UYVY && info.video_format != FORMAT_VYUY && info.video_format != FORMAT_YUY2) || + (info.video_width == 0) || + (info.video_height == 0) || + (info.video_depth == 0) || + (info.video_depth % 8 != 0)) + return error::UNSUPPORTED_VIDEO_FORMAT; - /* make sure it's the left predictor */ - if ((stream->huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) != HUFFYUV_PREDICT_LEFT) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + /* validate audio info */ + if (info.audio_format != 0 || + info.audio_channels > MAX_SOUND_CHANNELS || + info.audio_samplebits != 16) + return error::UNSUPPORTED_AUDIO_FORMAT; - /* make sure it's 16bpp YUV data */ - if (chunkdata[41] != 16) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; - chunkdata += 44; + /* open the file */ + osd_file::ptr f; + std::uint64_t length; + osd_file::error const filerr = osd_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, f, length); + if (filerr != osd_file::error::NONE) + return error::CANT_OPEN_FILE; - /* loop over tables */ - for (tabnum = 0; tabnum < 3; tabnum++) + /* allocate the file */ + error avierr; + std::unique_ptr<avi_file_impl> newfile; + try { newfile = std::make_unique<avi_file_impl>(std::move(f), info); } + catch (...) { - huffyuv_table *table = &stream->huffyuv->table[tabnum]; - UINT32 curbits, bitadd; - UINT16 bitsat16 = 0; - int offset = 0, bits; - - /* loop until we populate the whole table */ - while (offset < 256) - { - int data, shift, count, i; - - /* extract the next run */ - if (chunkdata >= chunkend) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - data = *chunkdata++; - shift = data & 0x1f; - count = data >> 5; - - /* zero count means next whole byte is a count */ - if (count == 0) - { - if (chunkdata >= chunkend) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - count = *chunkdata++; - } - for (i = 0; i < count; i++) - table->shift[offset++] = shift; - } - - /* now determine bit patterns and masks */ - curbits = 0; - for (bits = 31; bits >= 0; bits--) - { - bitadd = 1 << (32 - bits); - - /* make sure we've cleared out all the bits below */ - if ((curbits & (bitadd - 1)) != 0) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - - /* find all entries with this shift count and assign them */ - for (offset = 0; offset < 256; offset++) - if (table->shift[offset] == bits) - { - table->bits[offset] = curbits; - table->mask[offset] = ~(bitadd - 1); - curbits += bitadd; - } + avierr = error::NO_MEMORY; + goto error; + } - /* remember the bit pattern when we complete all the 17-bit codes */ - if (bits == 17) - bitsat16 = curbits >> 16; - } + /* initialize the sound buffering */ + avierr = newfile->soundbuf_initialize(); + if (avierr != error::NONE) + goto error; - /* allocate the number of extra lookup tables we need */ - if (bitsat16 > 0) - { - table->extralookup = (UINT16 *)malloc(bitsat16 * 65536 * sizeof(table->extralookup[0])); - if (table->extralookup == nullptr) - { - avierr = AVIERR_NO_MEMORY; - goto error; - } - for (offset = 0; offset < bitsat16; offset++) - table->baselookup[offset] = (offset << 8) | 0; - } + /* write the initial headers */ + avierr = newfile->write_initial_headers(); + if (avierr != error::NONE) + goto error; - /* then create lookup tables */ - for (offset = 0; offset < 256; offset++) - if (table->shift[offset] > 16) - { - UINT16 *tablebase = table->extralookup + (table->bits[offset] >> 16) * 65536; - UINT32 start = table->bits[offset] & 0xffff; - UINT32 end = start + ((1 << (32 - table->shift[offset])) - 1); - while (start <= end) - tablebase[start++] = (offset << 8) | (table->shift[offset] - 16); - } - else if (table->shift[offset] > 0) - { - UINT32 start = table->bits[offset] >> 16; - UINT32 end = start + ((1 << (16 - table->shift[offset])) - 1); - while (start <= end) - table->baselookup[start++] = (offset << 8) | table->shift[offset]; - } - } + file = std::move(newfile); + return error::NONE; error: - if (avierr != AVIERR_NONE && stream->huffyuv != nullptr) - { - free(stream->huffyuv); - stream->huffyuv = nullptr; - } + f.reset(); + newfile.reset(); + osd_file::remove(filename); return avierr; } /*------------------------------------------------- - huffyuv_decompress_to_yuy16 - decompress a - HuffYUV-encoded frame to a YUY16 bitmap + avi_error_string - get the error string for + an avi_error -------------------------------------------------*/ /** - * @fn static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) + * @fn const char *avi_error_string(avi_error err) * - * @brief Huffyuv decompress to yuy 16. + * @brief Avi error string. * - * @param [in,out] stream If non-null, the stream. - * @param data The data. - * @param numbytes The numbytes. - * @param [in,out] bitmap The bitmap. + * @param err The error. * - * @return An avi_error. + * @return null if it fails, else a char*. */ -static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) +const char *avi_file::error_string(error err) { - huffyuv_data *huffyuv = stream->huffyuv; - int prevlines = (stream->height > 288) ? 2 : 1; - UINT8 lastprevy = 0, lastprevcb = 0, lastprevcr = 0; - UINT8 lasty = 0, lastcb = 0, lastcr = 0; - UINT8 bitsinbuffer = 0; - UINT32 bitbuffer = 0; - UINT32 dataoffs = 0; - int x, y; - - /* compressed video */ - for (y = 0; y < stream->height; y++) - { - UINT16 *dest = &bitmap.pix16(y); - - /* handle the first four bytes independently */ - x = 0; - if (y == 0) - { - /* first DWORD is stored as YUY2 */ - lasty = data[dataoffs++]; - lastcb = data[dataoffs++]; - *dest++ = (lasty << 8) | lastcb; - lasty = data[dataoffs++]; - lastcr = data[dataoffs++]; - *dest++ = (lasty << 8) | lastcr; - x = 2; - } - - /* loop over pixels */ - for ( ; x < stream->width; x++) - { - huffyuv_table *ytable = &huffyuv->table[0]; - huffyuv_table *ctable = &huffyuv->table[1 + (x & 1)]; - UINT16 pixel, huffdata; - int shift; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - /* look up the Y component */ - huffdata = ytable->baselookup[bitbuffer >> 16]; - shift = huffdata & 0xff; - if (shift == 0) - { - bitsinbuffer -= 16; - bitbuffer <<= 16; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - huffdata = ytable->extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; - shift = huffdata & 0xff; - } - bitsinbuffer -= shift; - bitbuffer <<= shift; - pixel = huffdata & 0xff00; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - /* look up the Cb/Cr component */ - huffdata = ctable->baselookup[bitbuffer >> 16]; - shift = huffdata & 0xff; - if (shift == 0) - { - bitsinbuffer -= 16; - bitbuffer <<= 16; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - huffdata = ctable->extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; - shift = huffdata & 0xff; - } - bitsinbuffer -= shift; - bitbuffer <<= shift; - *dest++ = pixel | (huffdata >> 8); - } - } - - /* apply deltas */ - lastprevy = lastprevcb = lastprevcr = 0; - for (y = 0; y < stream->height; y++) + switch (err) { - UINT16 *prevrow = &bitmap.pix16(y - prevlines); - UINT16 *dest = &bitmap.pix16(y); - - /* handle the first four bytes independently */ - x = 0; - if (y == 0) - { - /* lasty, lastcr, lastcb are set up previously */ - x = 2; - } - - /* left predict or gradient predict */ - if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_LEFT || - ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT)) - { - /* first do left deltas */ - for ( ; x < stream->width; x += 2) - { - UINT16 pixel0 = dest[x + 0]; - UINT16 pixel1 = dest[x + 1]; - - lasty += pixel0 >> 8; - lastcb += pixel0; - dest[x + 0] = (lasty << 8) | lastcb; - - lasty += pixel1 >> 8; - lastcr += pixel1; - dest[x + 1] = (lasty << 8) | lastcr; - } - - /* for gradient, we then add in the previous row */ - if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT && y >= prevlines) - for (x = 0; x < stream->width; x++) - { - UINT16 curpix = dest[x]; - UINT16 prevpix = prevrow[x]; - UINT8 ysum = (curpix >> 8) + (prevpix >> 8); - UINT8 csum = curpix + prevpix; - dest[x] = (ysum << 8) | csum; - } - } - - /* median predict on rows > 0 */ - else if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_MEDIAN && y >= prevlines) - { - for ( ; x < stream->width; x += 2) - { - UINT16 prevpixel0 = prevrow[x + 0]; - UINT16 prevpixel1 = prevrow[x + 1]; - UINT16 pixel0 = dest[x + 0]; - UINT16 pixel1 = dest[x + 1]; - UINT8 a, b, c; - - /* compute previous, above, and (prev + above - above-left) */ - a = lasty; - b = prevpixel0 >> 8; - c = lastprevy; - lastprevy = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lasty = (pixel0 >> 8) + b; - - /* compute previous, above, and (prev + above - above-left) */ - a = lastcb; - b = prevpixel0 & 0xff; - c = lastprevcb; - lastprevcb = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lastcb = (pixel0 & 0xff) + b; - dest[x + 0] = (lasty << 8) | lastcb; - - /* compute previous, above, and (prev + above - above-left) */ - a = lasty; - b = prevpixel1 >> 8; - c = lastprevy; - lastprevy = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lasty = (pixel1 >> 8) + b; - - /* compute previous, above, and (prev + above - above-left) */ - a = lastcr; - b = prevpixel1 & 0xff; - c = lastprevcr; - lastprevcr = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lastcr = (pixel1 & 0xff) + b; - dest[x + 1] = (lasty << 8) | lastcr; - } - } + case error::NONE: return "success"; + case error::END: return "hit end of file"; + case error::INVALID_DATA: return "invalid data"; + case error::NO_MEMORY: return "out of memory"; + case error::READ_ERROR: return "read error"; + case error::WRITE_ERROR: return "write error"; + case error::STACK_TOO_DEEP: return "stack overflow"; + case error::UNSUPPORTED_FEATURE: return "unsupported feature"; + case error::CANT_OPEN_FILE: return "unable to open file"; + case error::INCOMPATIBLE_AUDIO_STREAMS: return "found incompatible audio streams"; + case error::INVALID_SAMPLERATE: return "found invalid sample rate"; + case error::INVALID_STREAM: return "invalid stream"; + case error::INVALID_FRAME: return "invalid frame index"; + case error::INVALID_BITMAP: return "invalid bitmap"; + case error::UNSUPPORTED_VIDEO_FORMAT: return "unsupported video format"; + case error::UNSUPPORTED_AUDIO_FORMAT: return "unsupported audio format"; + case error::EXCEEDED_SOUND_BUFFER: return "sound buffer overflow"; + default: return "undocumented error"; } - - return AVIERR_NONE; } -/** - * @fn static void u64toa(UINT64 val, char *output) - * - * @brief 64toas. - * - * @param val The value. - * @param [in,out] output If non-null, the output. - */ -static void u64toa(UINT64 val, char *output) +avi_file::avi_file() { - UINT32 lo = (UINT32)(val & 0xffffffff); - UINT32 hi = (UINT32)(val >> 32); - if (hi != 0) - sprintf(output, "%X%08X", hi, lo); - else - sprintf(output, "%X", lo); } -/*------------------------------------------------- - printf_chunk_recursive - print information - about a chunk recursively --------------------------------------------------*/ - -/** - * @fn static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent) - * - * @brief Printf chunk recursive. - * - * @param [in,out] file If non-null, the file. - * @param [in,out] container If non-null, the container. - * @param indent The indent. - */ - -static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent) +avi_file::~avi_file() { - char size[20], offset[20]; - avi_chunk curchunk; - int avierr; - - /* iterate over chunks in this container */ - for (avierr = get_first_chunk(file, container, &curchunk); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, &curchunk)) - { - UINT32 chunksize = curchunk.size; - int recurse = FALSE; - - u64toa(curchunk.size, size); - u64toa(curchunk.offset, offset); - printf("%*schunk = %c%c%c%c, size=%s (%s)\n", indent, "", - (UINT8)(curchunk.type >> 0), - (UINT8)(curchunk.type >> 8), - (UINT8)(curchunk.type >> 16), - (UINT8)(curchunk.type >> 24), - size, offset); - - /* certain chunks are just containers; recurse into them */ - switch (curchunk.type) - { - /* basic containers */ - case CHUNKTYPE_RIFF: - case CHUNKTYPE_LIST: - printf("%*stype = %c%c%c%c\n", indent, "", - (UINT8)(curchunk.listtype >> 0), - (UINT8)(curchunk.listtype >> 8), - (UINT8)(curchunk.listtype >> 16), - (UINT8)(curchunk.listtype >> 24)); - recurse = TRUE; - chunksize = 0; - break; - } - - /* print data within the chunk */ - if (chunksize > 0 && curchunk.size < 1024 * 1024) - { - UINT8 *data = nullptr; - int i; - - /* read the data for a chunk */ - avierr = read_chunk_data(file, &curchunk, &data); - if (avierr == AVIERR_NONE) - { - int bytes = MIN(512, chunksize); - for (i = 0; i < bytes; i++) - { - if (i % 16 == 0) printf("%*s ", indent, ""); - printf("%02X ", data[i]); - if (i % 16 == 15) printf("\n"); - } - if (chunksize % 16 != 0) printf("\n"); - free(data); - } - } - - /* if we're recursing, dive down */ - if (recurse) - printf_chunk_recursive(file, &curchunk, indent + 4); - } - - /* if we didn't get a legitimate error, indicate that */ - if (avierr != AVIERR_END) - printf("[chunk error %d]\n", avierr); } |