From 7dc59ce975234e55720150334e524605acbb7732 Mon Sep 17 00:00:00 2001 From: mooglyguy Date: Sun, 21 Jul 2019 09:15:31 +0200 Subject: -aviio: Added functionality to read RGB24 and YUV420p uncompressed video frames. [Ryan Holtz] --- src/lib/util/aviio.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++++----- src/lib/util/aviio.h | 7 ++- 2 files changed, 137 insertions(+), 15 deletions(-) diff --git a/src/lib/util/aviio.cpp b/src/lib/util/aviio.cpp index 47c8023d3a0..b7849ee36cc 100644 --- a/src/lib/util/aviio.cpp +++ b/src/lib/util/aviio.cpp @@ -365,6 +365,10 @@ public: // HuffYUV helpers avi_file::error huffyuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const; + // Uncompressed helpers + avi_file::error uncompressed_rgb24_to_rgb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_rgb32 &bitmap) const; + avi_file::error uncompressed_yuv420p_to_rgb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_rgb32 &bitmap) const; + private: struct huffyuv_table { @@ -456,6 +460,7 @@ public: 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_uncompressed_video_frame(std::uint32_t framenum, bitmap_rgb32 &bitmap) 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; @@ -1483,6 +1488,73 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data } +/*------------------------------------------------- + uncompressed_rgb24_to_rgb32 - convert a raw + RGB24-encoded frame to an RGB32 bitmap +-------------------------------------------------*/ + +avi_file::error avi_stream::uncompressed_rgb24_to_rgb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_rgb32 &bitmap) const +{ + std::uint32_t dataoffs = 0; + + /* uncompressed video */ + for (int y = 0; y < m_height; y++) + { + std::uint32_t *dest = &bitmap.pix32(y); + + /* loop over pixels */ + for (int x = 0; x < m_width; x++) + { + const uint8_t b = data[dataoffs++]; + const uint8_t g = data[dataoffs++]; + const uint8_t r = data[dataoffs++]; + *dest++ = 0xff << 24 | r << 16 | g << 8 | b; + } + } + + return avi_file::error::NONE; +} + + +/*------------------------------------------------- + uncompressed_yuv420p_to_rgb32 - convert a + YUV420p-encoded frame to an RGB32 bitmap +-------------------------------------------------*/ + +avi_file::error avi_stream::uncompressed_yuv420p_to_rgb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_rgb32 &bitmap) const +{ + const int width = bitmap.width(); + const int height = bitmap.height(); + const int size_total = width * height; + + /* uncompressed video */ + for (int y = 0; y < m_height; y++) + { + std::uint32_t *dest = &bitmap.pix32(y); + + /* loop over pixels */ + for (int x = 0; x < m_width; x++) + { + const uint8_t luma = data[y * width + x]; + const uint8_t u = data[(y / 2) * (width / 2) + x / 2 + size_total]; + const uint8_t v = data[(y / 2) * (width / 2) + x / 2 + size_total + size_total / 4]; + + int r = luma + (1.370705f * (v - 0x80)); + int g = luma - (0.698001f * (v - 0x80)) - (0.337633f * (u - 0x80)); + int b = luma + (1.732446f * (u - 0x80)); + + r = (r < 0) ? 0 : ((r > 255) ? 255 : r); + g = (g < 0) ? 0 : ((g > 255) ? 255 : g); + b = (b < 0) ? 0 : ((b > 255) ? 255 : b); + + *dest++ = 0xff << 24 | r << 16 | g << 8 | b; + } + } + + return avi_file::error::NONE; +} + + /*------------------------------------------------- avi_close - close an AVI movie file -------------------------------------------------*/ @@ -1602,22 +1674,67 @@ std::uint32_t avi_file_impl::first_sample_in_frame(std::uint32_t framenum) const /*------------------------------------------------- - avi_read_video_frame - read video data - for a particular frame from the AVI file, - converting to YUY16 format + read_uncompressed_video_frame - read raw video + data for a particular frame from the AVI file, + converting to RGB32 format -------------------------------------------------*/ -/** - * @fn avi_error avi_read_video_frame(avi_file *file, std::uint32_t framenum, bitmap_yuy16 &bitmap) - * - * @brief Avi read video frame. - * - * @param [in,out] file If non-null, the file. - * @param framenum The framenum. - * @param [in,out] bitmap The bitmap. - * - * @return An avi_error. - */ +avi_file::error avi_file_impl::read_uncompressed_video_frame(std::uint32_t framenum, bitmap_rgb32 &bitmap) +{ + /* get the video stream */ + avi_stream *const stream = get_video_stream(); + if (!stream) + return error::INVALID_STREAM; + + if (stream->format() != FORMAT_UNCOMPRESSED && stream->format() != FORMAT_DIB && stream->format() != FORMAT_RGB && stream->format() != FORMAT_RAW && stream->format() != FORMAT_I420) + return error::UNSUPPORTED_VIDEO_FORMAT; + + if (bitmap.width() < stream->width() || bitmap.height() < stream->height()) + bitmap.resize(stream->width(), stream->height()); + + /* assume one chunk == one 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 error::INVALID_BITMAP; + + /* expand the tempbuffer to hold the data if necessary */ + error avierr = error::NONE; + avierr = expand_tempbuffer(stream->chunk(framenum).length); + if (avierr != error::NONE) + return avierr; + + /* read in the data */ + 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 */ + std::uint32_t const chunkid = fetch_32bits(&m_tempbuffer[0]); + if (chunkid == get_chunkid_for_stream(stream)) + { + /* uncompressed YUV420p */ + if (stream->format() == FORMAT_I420) + avierr = stream->uncompressed_yuv420p_to_rgb32(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap); + else + avierr = stream->uncompressed_rgb24_to_rgb32(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap); + } + else + { + avierr = error::INVALID_DATA; + } + + return avierr; +} + +/*------------------------------------------------- + read_video_frame - read video data for a + particular frame from the AVI file, converting + to YUY16 format +-------------------------------------------------*/ avi_file::error avi_file_impl::read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) { diff --git a/src/lib/util/aviio.h b/src/lib/util/aviio.h index c6d547903fe..d35cf20a2d1 100644 --- a/src/lib/util/aviio.h +++ b/src/lib/util/aviio.h @@ -29,7 +29,11 @@ #define FORMAT_VYUY AVI_FOURCC('V','Y','U','Y') #define FORMAT_YUY2 AVI_FOURCC('Y','U','Y','2') #define FORMAT_HFYU AVI_FOURCC('H','F','Y','U') - +#define FORMAT_I420 AVI_FOURCC('I','4','2','0') +#define FORMAT_DIB AVI_FOURCC('D','I','B',' ') +#define FORMAT_RGB AVI_FOURCC('R','G','B',' ') +#define FORMAT_RAW AVI_FOURCC('R','A','W',' ') +#define FORMAT_UNCOMPRESSED 0x00000000 class avi_file @@ -115,6 +119,7 @@ public: virtual movie_info const &get_movie_info() const = 0; virtual std::uint32_t first_sample_in_frame(std::uint32_t framenum) const = 0; + virtual error read_uncompressed_video_frame(std::uint32_t framenum, bitmap_rgb32 &bitmap) = 0; virtual error read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) = 0; virtual error read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) = 0; -- cgit v1.2.3