Skip to content

Commit

Permalink
🔀 Merge branch 'yann/feature/videokit/play-video' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
ladislas committed Apr 21, 2022
2 parents 78ce767 + f6eed16 commit bf6351b
Show file tree
Hide file tree
Showing 16 changed files with 238 additions and 20 deletions.
3 changes: 2 additions & 1 deletion drivers/CoreVideo/include/CoreJPEG.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class CoreJPEG : public interface::JPEGBase

auto getHandle() -> JPEG_HandleTypeDef & final;

void decodeImage(interface::File &file) final;
auto decodeImage(interface::File &file) -> size_t final;
auto findSOIMarker(interface::File &file, size_t start_index) -> size_t final;

auto getImageProperties() -> JPEGImageProperties final;

Expand Down
4 changes: 2 additions & 2 deletions drivers/CoreVideo/include/CoreJPEGModeDMA.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct CoreJPEGModeDMA : interface::JPEGMode {
void onDataAvailableCallback(JPEG_HandleTypeDef *hjpeg, uint32_t decoded_datasize) final;
void onDecodeCompleteCallback(JPEG_HandleTypeDef *hjpeg) final;

auto decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> HAL_StatusTypeDef final;
auto decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> size_t final;

private:
static std::array<uint8_t, jpeg::input_chunk_size * jpeg::input_buffers_nb> BIG_CHUNGUS_OF_MEMORY_IN;
Expand Down Expand Up @@ -66,7 +66,7 @@ struct CoreJPEGModeDMA : interface::JPEGMode {
// color conversion function pointer, set by onInfoReadyCallback
JPEG_YCbCrToRGB_Convert_Function convertMCUBlocks;

uint32_t _image_size = 0;
size_t _image_size = 0;
uint32_t _mcu_number = 0;
uint32_t _mcu_block_index = 0;
bool _hw_decode_ended = false;
Expand Down
3 changes: 2 additions & 1 deletion drivers/CoreVideo/include/CoreJPEGModePolling.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CoreJPEGModePolling : public interface::JPEGMode
void onDataAvailableCallback(JPEG_HandleTypeDef *hjpeg, uint32_t decoded_datasize) final;
void onDecodeCompleteCallback(JPEG_HandleTypeDef *hjpeg) final;

auto decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> HAL_StatusTypeDef final;
auto decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> size_t final;

private:
struct JPEGDataBuffer {
Expand All @@ -35,6 +35,7 @@ class CoreJPEGModePolling : public interface::JPEGMode
interface::STM32Hal &_hal;
interface::File *_file {};

size_t _image_size = 0;
uint32_t _mcu_number = 0;
uint32_t _mcu_block_index = 0;
uint32_t _input_file_offset = 0;
Expand Down
1 change: 1 addition & 0 deletions drivers/CoreVideo/include/CoreVideo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class CoreVideo
void clearScreen(CGColor color = CGColor::white);
void displayRectangle(interface::Graphics::FilledRectangle rectangle, CGColor color);
void displayImage(interface::File &file);
void playVideo(interface::File &file);
void displayText(const char *text, uint32_t size, uint32_t starting_line, CGColor foreground = CGColor::black,
CGColor background = CGColor::white);

Expand Down
3 changes: 2 additions & 1 deletion drivers/CoreVideo/include/interface/JPEG.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class JPEGBase

virtual auto getHandle() -> JPEG_HandleTypeDef & = 0;

virtual void decodeImage(interface::File &) = 0;
virtual auto decodeImage(interface::File &) -> size_t = 0;
virtual auto findSOIMarker(interface::File &file, size_t start_index) -> size_t = 0;

virtual auto getImageProperties() -> JPEGImageProperties = 0;
};
Expand Down
3 changes: 1 addition & 2 deletions drivers/CoreVideo/include/interface/JPEGMode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ struct JPEGMode {
virtual void onDataAvailableCallback(JPEG_HandleTypeDef *hjpeg, uint32_t decoded_datasize) = 0;
virtual void onDecodeCompleteCallback(JPEG_HandleTypeDef *hjpeg) = 0;

// TODO(@yann): Update Return type with something else than HAL status
virtual auto decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> HAL_StatusTypeDef = 0;
virtual auto decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> size_t = 0;
};

} // namespace leka::interface
2 changes: 2 additions & 0 deletions drivers/CoreVideo/include/internal/corevideo_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ namespace jpeg {
inline constexpr uint8_t JPEG_RGB_FORMAT = JPEG_ARGB8888; // Select RGB format: ARGB8888, RGB888, RBG565
inline constexpr uint8_t JPEG_SWAP_RB = 0; // Change color order to BGR

inline constexpr uint16_t JPEG_SOI_MARKER = 0xFFD8; // JPEG Start of Image marker

} // namespace jpeg

namespace graphics {
Expand Down
30 changes: 28 additions & 2 deletions drivers/CoreVideo/source/CoreJPEG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,33 @@ void CoreJPEG::registerProcessCallbacks()
[](JPEG_HandleTypeDef *hjpeg) { self._mode.onErrorCallback(hjpeg); });
}

void CoreJPEG::decodeImage(interface::File &file)
auto CoreJPEG::decodeImage(interface::File &file) -> size_t
{
_mode.decode(&_hjpeg, file);
return _mode.decode(&_hjpeg, file);
}

auto CoreJPEG::findSOIMarker(interface::File &file, size_t start_index) -> size_t
{
auto buffer = std::array<uint8_t, 512> {};

auto is_jpeg_soi_marker_predicate = [](uint8_t val1, uint8_t val2) {
return (static_cast<uint16_t>((val1 << 8) + val2)) == jpeg::JPEG_SOI_MARKER;
};

while (file.size() > start_index) {
file.seek(start_index, SEEK_SET);
auto bytes_read = file.read(buffer.data(), buffer.size());

auto *buffer_end_index = std::begin(buffer) + bytes_read;
auto *find_index = std::adjacent_find(std::begin(buffer), buffer_end_index, is_jpeg_soi_marker_predicate);

if (auto marker_found = (find_index != buffer_end_index)) {
auto marker_index = start_index + std::distance(std::begin(buffer), find_index);
return marker_index;
}

start_index += bytes_read;
}

return 0;
}
9 changes: 2 additions & 7 deletions drivers/CoreVideo/source/CoreJPEGModeDMA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ void CoreJPEGModeDMA::onDecodeCompleteCallback(JPEG_HandleTypeDef *hjpeg)
_hw_decode_ended = true;
}

auto CoreJPEGModeDMA::decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> HAL_StatusTypeDef
auto CoreJPEGModeDMA::decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> size_t
{
reset();

Expand All @@ -171,12 +171,7 @@ auto CoreJPEGModeDMA::decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -
process_ended = decoderOutputHandler(hjpeg);
} while (!process_ended);

auto ret = HAL_OK;
if (_image_size == 0) {
ret = HAL_ERROR;
}

return ret;
return _image_size;
}

std::array<uint8_t, jpeg::input_chunk_size * jpeg::input_buffers_nb> CoreJPEGModeDMA::BIG_CHUNGUS_OF_MEMORY_IN;
Expand Down
7 changes: 5 additions & 2 deletions drivers/CoreVideo/source/CoreJPEGModePolling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,21 @@ void CoreJPEGModePolling::onDataAvailableCallback(JPEG_HandleTypeDef *hjpeg, uin
} else {
// TODO(@yann): handle error
}

_image_size += _jpeg_input_buffer.size;
}

void CoreJPEGModePolling::onDecodeCompleteCallback(JPEG_HandleTypeDef *hjpeg)
{
// TODO(@yann): implement flag
}

auto CoreJPEGModePolling::decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> HAL_StatusTypeDef
auto CoreJPEGModePolling::decode(JPEG_HandleTypeDef *hjpeg, interface::File &file) -> size_t
{
_file = &file;

// WARNING: DO NOT REMOVE
_image_size = 0;
_mcu_block_index = 0;

if (_jpeg_input_buffer.size = _file->read(_jpeg_input_buffer.data, leka::jpeg::input_chunk_size);
Expand All @@ -99,5 +102,5 @@ auto CoreJPEGModePolling::decode(JPEG_HandleTypeDef *hjpeg, interface::File &fil
_hal.HAL_JPEG_Decode(hjpeg, _jpeg_input_buffer.data, _jpeg_input_buffer.size, _mcu_data_output_buffer.data(),
leka::jpeg::output_chunk_size, HAL_MAX_DELAY);

return HAL_OK;
return _image_size;
}
17 changes: 17 additions & 0 deletions drivers/CoreVideo/source/CoreVideo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ void CoreVideo::displayImage(interface::File &file)
image_properties.getWidthOffset());
}

void CoreVideo::playVideo(interface::File &file)
{
auto config = _corejpeg.getImageProperties();

auto frame_index = _corejpeg.findSOIMarker(file, 0);
auto frame_size = size_t {0};

while (frame_index != 0) {
file.seek(frame_index, SEEK_SET);

frame_size = _corejpeg.decodeImage(file);
_coredma2d.transferImage(config.ImageWidth, config.ImageHeight, config.getWidthOffset());

frame_index = _corejpeg.findSOIMarker(file, frame_index + frame_size);
}
}

void CoreVideo::displayText(const char *text, uint32_t size, uint32_t starting_line, CGColor foreground,
CGColor background)
{
Expand Down
135 changes: 135 additions & 0 deletions drivers/CoreVideo/tests/CoreJPEG_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
using namespace leka;

using ::testing::_;
using ::testing::AnyNumber;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Matcher;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SetArrayArgument;

class CoreJPEGTest : public ::testing::Test
{
Expand Down Expand Up @@ -91,3 +94,135 @@ TEST_F(CoreJPEGTest, decodeImage)

corejpeg.decodeImage(filemock);
}

TEST_F(CoreJPEGTest, findSOIMarkerFileIsEmpty)
{
EXPECT_CALL(filemock, size).WillOnce(Return(0));

corejpeg.findSOIMarker(filemock, 0);
}

TEST_F(CoreJPEGTest, findSOIMarkerBase)
{
auto start_index = 0;
auto expected_frame_index = 0;

EXPECT_CALL(filemock, size).WillRepeatedly(Return(1024));
// EXPECT_CALL(filemock, seek(start_index, _)).Times(AnyNumber());
EXPECT_CALL(filemock, seek).Times(AnyNumber());

EXPECT_CALL(filemock, read(Matcher<uint8_t *>(_), _)).WillRepeatedly(Return(512));

auto actual_frame_index = corejpeg.findSOIMarker(filemock, start_index);
EXPECT_EQ(actual_frame_index, expected_frame_index);
}

TEST_F(CoreJPEGTest, findSOIMarkerPresent)
{
auto start_index = 0;

std::array<uint8_t, 1024> data {};
std::fill(std::begin(data), std::end(data), 0);

const auto expected_frame_index = size_t {218};
data.at(expected_frame_index) = (jpeg::JPEG_SOI_MARKER >> 8) & 0xFF;
data.at(expected_frame_index + 1) = jpeg::JPEG_SOI_MARKER & 0xFF;

EXPECT_CALL(filemock, size).Times(1).WillRepeatedly(Return(1024));
EXPECT_CALL(filemock, seek(start_index, _)).Times(1);

auto read_bytes = 512;
EXPECT_CALL(filemock, read(Matcher<uint8_t *>(_), _))
.Times(1)
.WillRepeatedly(
DoAll(SetArrayArgument<0>(std::begin(data), std::begin(data) + read_bytes), Return(read_bytes)));

auto actual_frame_index = corejpeg.findSOIMarker(filemock, start_index);
EXPECT_EQ(actual_frame_index, expected_frame_index);
}

TEST_F(CoreJPEGTest, findSOIMarkerAbsent)
{
auto start_index = 0;

std::array<uint8_t, 1024> data {};
std::fill(std::begin(data), std::end(data), 0);

const auto expected_frame_index = size_t {0};

EXPECT_CALL(filemock, size).Times(AnyNumber()).WillRepeatedly(Return(1024));
EXPECT_CALL(filemock, seek).Times(AnyNumber());

auto read_bytes = 256;
EXPECT_CALL(filemock, read(Matcher<uint8_t *>(_), _))
.Times(4)
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 0 * read_bytes, std::begin(data) + 1 * read_bytes),
Return(read_bytes)))
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 1 * read_bytes, std::begin(data) + 2 * read_bytes),
Return(read_bytes)))
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 2 * read_bytes, std::begin(data) + 3 * read_bytes),
Return(read_bytes)))
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 3 * read_bytes, std::begin(data) + 4 * read_bytes),
Return(read_bytes)));

auto actual_frame_index = corejpeg.findSOIMarker(filemock, start_index);
EXPECT_EQ(actual_frame_index, expected_frame_index);
}

TEST_F(CoreJPEGTest, findSOIMarkerPresentInLastPacket)
{
auto start_index = 0;

std::array<uint8_t, 1024> data {};
std::fill(std::begin(data), std::end(data), 0);

const auto expected_frame_index = size_t {1001};
data.at(expected_frame_index) = (jpeg::JPEG_SOI_MARKER >> 8) & 0xFF;
data.at(expected_frame_index + 1) = jpeg::JPEG_SOI_MARKER & 0xFF;

EXPECT_CALL(filemock, size).Times(AnyNumber()).WillRepeatedly(Return(1024));
EXPECT_CALL(filemock, seek).Times(AnyNumber());

auto read_bytes = 256;
EXPECT_CALL(filemock, read(Matcher<uint8_t *>(_), _))
.Times(4)
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 0 * read_bytes, std::begin(data) + 1 * read_bytes),
Return(read_bytes)))
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 1 * read_bytes, std::begin(data) + 2 * read_bytes),
Return(read_bytes)))
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 2 * read_bytes, std::begin(data) + 3 * read_bytes),
Return(read_bytes)))
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + 3 * read_bytes, std::begin(data) + 4 * read_bytes),
Return(read_bytes)));

auto actual_frame_index = corejpeg.findSOIMarker(filemock, start_index);
EXPECT_EQ(actual_frame_index, expected_frame_index);
}

TEST_F(CoreJPEGTest, findSOIMarkerPresentInLastPacketStartingIndexWithOffset)
{
auto start_index = 512;

std::array<uint8_t, 1024> data {};
std::fill(std::begin(data), std::end(data), 0);

const auto expected_frame_index = size_t {1001};
data.at(expected_frame_index) = (jpeg::JPEG_SOI_MARKER >> 8) & 0xFF;
data.at(expected_frame_index + 1) = jpeg::JPEG_SOI_MARKER & 0xFF;

EXPECT_CALL(filemock, size).Times(AnyNumber()).WillRepeatedly(Return(1024));
EXPECT_CALL(filemock, seek).Times(AnyNumber());

auto read_bytes = 256;
EXPECT_CALL(filemock, read(Matcher<uint8_t *>(_), _))
.Times(2)
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + start_index + 0 * read_bytes,
std::begin(data) + start_index + 1 * read_bytes),
Return(read_bytes)))
.WillOnce(DoAll(SetArrayArgument<0>(std::begin(data) + start_index + 1 * read_bytes,
std::begin(data) + start_index + 2 * read_bytes),
Return(read_bytes)));

auto actual_frame_index = corejpeg.findSOIMarker(filemock, start_index);
EXPECT_EQ(actual_frame_index, expected_frame_index);
}
23 changes: 23 additions & 0 deletions drivers/CoreVideo/tests/CoreVideo_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using namespace leka;
using ::testing::_;
using ::testing::InSequence;
using ::testing::Return;

class CoreVideoTest : public ::testing::Test
{
Expand Down Expand Up @@ -198,3 +199,25 @@ TEST_F(CoreVideoTest, displayTextWithColor)

corevideo.displayText(buff, text_length, starting_line, foreground_color, background_color);
}

TEST_F(CoreVideoTest, playVideo)
{
const auto any_frame_index = 218;
const auto any_frame_size = 27;

{
InSequence seq;

EXPECT_CALL(jpegmock, getImageProperties);
EXPECT_CALL(jpegmock, findSOIMarker).WillOnce(Return(any_frame_index));

EXPECT_CALL(filemock, seek(any_frame_index, SEEK_SET));

EXPECT_CALL(jpegmock, decodeImage).WillOnce(Return(any_frame_size));
EXPECT_CALL(dma2dmock, transferImage);

EXPECT_CALL(jpegmock, findSOIMarker(_, any_frame_index + any_frame_size)).WillOnce(Return(0));
}

corevideo.playVideo(filemock);
}
Loading

0 comments on commit bf6351b

Please sign in to comment.