added keyframe for speed increase when decoding hopefully
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#ifndef VIDEO_HPP
|
#ifndef VIDEO_HPP
|
||||||
#define VIDEO_HPP
|
#define VIDEO_HPP
|
||||||
|
|
||||||
|
|
||||||
#include "frame.hpp"
|
#include "frame.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -8,16 +9,19 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <unordered_map>
|
||||||
#include "../timing_decorator.hpp"
|
#include "../timing_decorator.hpp"
|
||||||
|
|
||||||
class video {
|
class video {
|
||||||
private:
|
private:
|
||||||
std::vector<std::vector<std::pair<uint8_t, uint32_t>>> compressed_frames_;
|
std::vector<std::vector<std::pair<uint8_t, uint32_t>>> compressed_frames_;
|
||||||
|
std::unordered_map<size_t, size_t> keyframe_indices_; // Maps frame index to keyframe index
|
||||||
size_t width_;
|
size_t width_;
|
||||||
size_t height_;
|
size_t height_;
|
||||||
std::vector<char> channels_;
|
std::vector<char> channels_;
|
||||||
double fps_;
|
double fps_;
|
||||||
bool use_differential_encoding_;
|
bool use_differential_encoding_;
|
||||||
|
size_t keyframe_interval_;
|
||||||
|
|
||||||
// Compress frame using differential encoding
|
// Compress frame using differential encoding
|
||||||
std::vector<std::pair<uint8_t, uint32_t>> compress_with_differential(
|
std::vector<std::pair<uint8_t, uint32_t>> compress_with_differential(
|
||||||
@@ -25,7 +29,7 @@ private:
|
|||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
|
|
||||||
if (previous_frame == nullptr) {
|
if (previous_frame == nullptr) {
|
||||||
// First frame - compress normally
|
// First frame or keyframe - compress normally
|
||||||
return current_frame.compress_rle();
|
return current_frame.compress_rle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,15 +73,78 @@ private:
|
|||||||
return frame(reconstructed_data, width_, height_, channels_);
|
return frame(reconstructed_data, width_, height_, channels_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the nearest keyframe index for a given frame index
|
||||||
|
size_t find_nearest_keyframe(size_t frame_index) const {
|
||||||
|
if (keyframe_indices_.empty()) return 0;
|
||||||
|
|
||||||
|
// Keyframes are stored at intervals, so we can calculate the nearest one
|
||||||
|
size_t keyframe_idx = (frame_index / keyframe_interval_) * keyframe_interval_;
|
||||||
|
|
||||||
|
// Make sure the keyframe exists
|
||||||
|
if (keyframe_idx >= compressed_frames_.size()) {
|
||||||
|
// Find the last available keyframe
|
||||||
|
for (size_t i = frame_index; i > 0; --i) {
|
||||||
|
if (keyframe_indices_.count(i)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyframe_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build keyframe indices (call this when frames change)
|
||||||
|
void rebuild_keyframe_indices() {
|
||||||
|
keyframe_indices_.clear();
|
||||||
|
for (size_t i = 0; i < compressed_frames_.size(); i += keyframe_interval_) {
|
||||||
|
if (i < compressed_frames_.size()) {
|
||||||
|
keyframe_indices_[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Always ensure frame 0 is a keyframe
|
||||||
|
if (!compressed_frames_.empty() && !keyframe_indices_.count(0)) {
|
||||||
|
keyframe_indices_[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get frame with keyframe optimization - much faster for random access
|
||||||
|
frame get_frame_optimized(size_t index) const {
|
||||||
|
if (index >= compressed_frames_.size()) {
|
||||||
|
throw std::out_of_range("Frame index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a keyframe or we're not using differential encoding, decompress directly
|
||||||
|
if (keyframe_indices_.count(index) || !use_differential_encoding_) {
|
||||||
|
frame result;
|
||||||
|
result.decompress_rle(compressed_frames_[index]);
|
||||||
|
result.resize(width_, height_, channels_);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the nearest keyframe
|
||||||
|
size_t keyframe_idx = find_nearest_keyframe(index);
|
||||||
|
|
||||||
|
// Decompress the keyframe first
|
||||||
|
frame current_frame = get_frame_optimized(keyframe_idx);
|
||||||
|
|
||||||
|
// Then decompress all frames from keyframe to target frame
|
||||||
|
for (size_t i = keyframe_idx + 1; i <= index; ++i) {
|
||||||
|
current_frame = decompress_differential(compressed_frames_[i], current_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_frame;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Default constructor
|
// Default constructor
|
||||||
video() : width_(0), height_(0), fps_(30.0), use_differential_encoding_(true) {}
|
video() : width_(0), height_(0), fps_(30.0), use_differential_encoding_(true), keyframe_interval_(50) {}
|
||||||
|
|
||||||
// Constructor with dimensions and settings
|
// Constructor with dimensions and settings
|
||||||
video(size_t width, size_t height, const std::vector<char>& channels = {'\0'},
|
video(size_t width, size_t height, const std::vector<char>& channels = {'\0'},
|
||||||
double fps = 30.0, bool use_differential = true)
|
double fps = 30.0, bool use_differential = true, size_t keyframe_interval = 50)
|
||||||
: width_(width), height_(height), channels_(channels), fps_(fps),
|
: width_(width), height_(height), channels_(channels), fps_(fps),
|
||||||
use_differential_encoding_(use_differential) {
|
use_differential_encoding_(use_differential), keyframe_interval_(keyframe_interval) {
|
||||||
|
|
||||||
if (width == 0 || height == 0) {
|
if (width == 0 || height == 0) {
|
||||||
throw std::invalid_argument("Dimensions must be positive");
|
throw std::invalid_argument("Dimensions must be positive");
|
||||||
@@ -88,12 +155,15 @@ public:
|
|||||||
if (fps <= 0) {
|
if (fps <= 0) {
|
||||||
throw std::invalid_argument("FPS must be positive");
|
throw std::invalid_argument("FPS must be positive");
|
||||||
}
|
}
|
||||||
|
if (keyframe_interval == 0) {
|
||||||
|
throw std::invalid_argument("Keyframe interval must be positive");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constructor with initializer list for channels
|
// Constructor with initializer list for channels
|
||||||
video(size_t width, size_t height, std::initializer_list<char> channels,
|
video(size_t width, size_t height, std::initializer_list<char> channels,
|
||||||
double fps = 30.0, bool use_differential = true)
|
double fps = 30.0, bool use_differential = true, size_t keyframe_interval = 50)
|
||||||
: video(width, height, std::vector<char>(channels), fps, use_differential) {}
|
: video(width, height, std::vector<char>(channels), fps, use_differential, keyframe_interval) {}
|
||||||
|
|
||||||
// Accessors
|
// Accessors
|
||||||
size_t width() const noexcept { return width_; }
|
size_t width() const noexcept { return width_; }
|
||||||
@@ -103,6 +173,8 @@ public:
|
|||||||
bool use_differential_encoding() const noexcept { return use_differential_encoding_; }
|
bool use_differential_encoding() const noexcept { return use_differential_encoding_; }
|
||||||
size_t frame_count() const noexcept { return compressed_frames_.size(); }
|
size_t frame_count() const noexcept { return compressed_frames_.size(); }
|
||||||
size_t channels_count() const noexcept { return channels_.size(); }
|
size_t channels_count() const noexcept { return channels_.size(); }
|
||||||
|
size_t keyframe_interval() const noexcept { return keyframe_interval_; }
|
||||||
|
const std::unordered_map<size_t, size_t>& keyframe_indices() const noexcept { return keyframe_indices_; }
|
||||||
|
|
||||||
// Check if video is empty
|
// Check if video is empty
|
||||||
bool empty() const noexcept {
|
bool empty() const noexcept {
|
||||||
@@ -120,13 +192,29 @@ public:
|
|||||||
throw std::invalid_argument("Frame channels must match video channels");
|
throw std::invalid_argument("Frame channels must match video channels");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t new_index = compressed_frames_.size();
|
||||||
|
|
||||||
if (compressed_frames_.empty() || !use_differential_encoding_) {
|
if (compressed_frames_.empty() || !use_differential_encoding_) {
|
||||||
// First frame or differential encoding disabled - compress normally
|
// First frame or differential encoding disabled - compress normally
|
||||||
compressed_frames_.push_back(new_frame.compress_rle());
|
compressed_frames_.push_back(new_frame.compress_rle());
|
||||||
} else {
|
} else {
|
||||||
// Get the previous frame for differential encoding
|
// Check if this should be a keyframe
|
||||||
frame prev_frame = get_frame(frame_count() - 1);
|
bool is_keyframe = (new_index % keyframe_interval_ == 0);
|
||||||
compressed_frames_.push_back(compress_with_differential(new_frame, &prev_frame));
|
|
||||||
|
if (is_keyframe) {
|
||||||
|
// Keyframe - compress normally
|
||||||
|
compressed_frames_.push_back(new_frame.compress_rle());
|
||||||
|
keyframe_indices_[new_index] = new_index;
|
||||||
|
} else {
|
||||||
|
// Regular frame - use differential encoding from previous frame
|
||||||
|
frame prev_frame = get_frame_optimized(new_index - 1);
|
||||||
|
compressed_frames_.push_back(compress_with_differential(new_frame, &prev_frame));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we have keyframe at index 0
|
||||||
|
if (compressed_frames_.size() == 1) {
|
||||||
|
keyframe_indices_[0] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,28 +223,30 @@ public:
|
|||||||
add_frame(new_frame); // Just call the const version
|
add_frame(new_frame); // Just call the const version
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a specific frame
|
// Get a specific frame (uses optimized version with keyframes)
|
||||||
frame get_frame(size_t index) const {
|
frame get_frame(size_t index) const {
|
||||||
if (index >= compressed_frames_.size()) {
|
TIME_FUNCTION;
|
||||||
throw std::out_of_range("Frame index out of range");
|
if (!use_differential_encoding_ || keyframe_indices_.empty()) {
|
||||||
|
// Fallback to original method if no optimization possible
|
||||||
|
if (index >= compressed_frames_.size()) {
|
||||||
|
throw std::out_of_range("Frame index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == 0 || !use_differential_encoding_) {
|
||||||
|
frame result;
|
||||||
|
result.decompress_rle(compressed_frames_[index]);
|
||||||
|
result.resize(width_, height_, channels_);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
frame prev_frame = get_frame(index - 1);
|
||||||
|
return decompress_differential(compressed_frames_[index], prev_frame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index == 0 || !use_differential_encoding_) {
|
return get_frame_optimized(index);
|
||||||
// First frame or no differential encoding - decompress normally
|
|
||||||
frame result;
|
|
||||||
result.decompress_rle(compressed_frames_[index]);
|
|
||||||
|
|
||||||
// Set dimensions and channels
|
|
||||||
result.resize(width_, height_, channels_);
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
// Differential encoded frame - need previous frame to reconstruct
|
|
||||||
frame prev_frame = get_frame(index - 1);
|
|
||||||
return decompress_differential(compressed_frames_[index], prev_frame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get multiple frames as a sequence
|
// Get multiple frames as a sequence (optimized for sequential access)
|
||||||
std::vector<frame> get_frames(size_t start_index, size_t count) const {
|
std::vector<frame> get_frames(size_t start_index, size_t count) const {
|
||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
if (start_index >= compressed_frames_.size()) {
|
if (start_index >= compressed_frames_.size()) {
|
||||||
@@ -167,8 +257,38 @@ public:
|
|||||||
std::vector<frame> frames;
|
std::vector<frame> frames;
|
||||||
frames.reserve(count);
|
frames.reserve(count);
|
||||||
|
|
||||||
for (size_t i = start_index; i < start_index + count; ++i) {
|
if (!use_differential_encoding_ || keyframe_indices_.empty()) {
|
||||||
frames.push_back(get_frame(i));
|
// Original sequential method
|
||||||
|
for (size_t i = start_index; i < start_index + count; ++i) {
|
||||||
|
frames.push_back(get_frame(i));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Optimized method: start from nearest keyframe
|
||||||
|
size_t current_index = start_index;
|
||||||
|
size_t keyframe_idx = find_nearest_keyframe(start_index);
|
||||||
|
|
||||||
|
// Get the keyframe
|
||||||
|
frame current_frame = get_frame_optimized(keyframe_idx);
|
||||||
|
|
||||||
|
// If we started before the keyframe (shouldn't happen), handle it
|
||||||
|
if (keyframe_idx > start_index) {
|
||||||
|
// This is a fallback - should not normally occur
|
||||||
|
current_frame = get_frame_optimized(start_index);
|
||||||
|
current_index = start_index + 1;
|
||||||
|
} else if (keyframe_idx < start_index) {
|
||||||
|
// Decode frames from keyframe to start_index
|
||||||
|
for (size_t i = keyframe_idx + 1; i < start_index; ++i) {
|
||||||
|
current_frame = decompress_differential(compressed_frames_[i], current_frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now add the requested frames
|
||||||
|
for (size_t i = start_index; i < start_index + count; ++i) {
|
||||||
|
if (i > keyframe_idx) {
|
||||||
|
current_frame = decompress_differential(compressed_frames_[i], current_frame);
|
||||||
|
}
|
||||||
|
frames.push_back(current_frame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return frames;
|
return frames;
|
||||||
@@ -185,11 +305,13 @@ public:
|
|||||||
throw std::out_of_range("Frame index out of range");
|
throw std::out_of_range("Frame index out of range");
|
||||||
}
|
}
|
||||||
compressed_frames_.erase(compressed_frames_.begin() + index);
|
compressed_frames_.erase(compressed_frames_.begin() + index);
|
||||||
|
rebuild_keyframe_indices();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear all frames
|
// Clear all frames
|
||||||
void clear_frames() noexcept {
|
void clear_frames() noexcept {
|
||||||
compressed_frames_.clear();
|
compressed_frames_.clear();
|
||||||
|
keyframe_indices_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace a frame
|
// Replace a frame
|
||||||
@@ -207,19 +329,37 @@ public:
|
|||||||
throw std::invalid_argument("Frame channels must match video channels");
|
throw std::invalid_argument("Frame channels must match video channels");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index == 0 || !use_differential_encoding_) {
|
bool was_keyframe = keyframe_indices_.count(index);
|
||||||
|
bool should_be_keyframe = (index % keyframe_interval_ == 0);
|
||||||
|
|
||||||
|
if (index == 0 || !use_differential_encoding_ || should_be_keyframe) {
|
||||||
|
// Keyframe or no differential encoding - compress normally
|
||||||
compressed_frames_[index] = new_frame.compress_rle();
|
compressed_frames_[index] = new_frame.compress_rle();
|
||||||
|
if (should_be_keyframe) {
|
||||||
|
keyframe_indices_[index] = index;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
frame prev_frame = get_frame(index - 1);
|
// Differential frame
|
||||||
|
frame prev_frame = get_frame_optimized(index - 1);
|
||||||
compressed_frames_[index] = compress_with_differential(new_frame, &prev_frame);
|
compressed_frames_[index] = compress_with_differential(new_frame, &prev_frame);
|
||||||
|
|
||||||
|
// Remove from keyframes if it was one but shouldn't be
|
||||||
|
if (was_keyframe && !should_be_keyframe) {
|
||||||
|
keyframe_indices_.erase(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this isn't the last frame, we need to update the next frame's differential encoding
|
// If this isn't the last frame, we need to update the next frame's differential encoding
|
||||||
if (use_differential_encoding_ && index + 1 < compressed_frames_.size()) {
|
if (use_differential_encoding_ && index + 1 < compressed_frames_.size()) {
|
||||||
frame current_frame = get_frame(index);
|
frame current_frame = get_frame_optimized(index);
|
||||||
frame next_frame_original = get_frame(index + 1);
|
frame next_frame_original = get_frame_optimized(index + 1);
|
||||||
compressed_frames_[index + 1] = compress_with_differential(next_frame_original, ¤t_frame);
|
compressed_frames_[index + 1] = compress_with_differential(next_frame_original, ¤t_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rebuild keyframe indices if we changed keyframe status
|
||||||
|
if (was_keyframe != should_be_keyframe) {
|
||||||
|
rebuild_keyframe_indices();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set FPS
|
// Set FPS
|
||||||
@@ -251,6 +391,45 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set keyframe interval and rebuild indices
|
||||||
|
void set_keyframe_interval(size_t interval) {
|
||||||
|
if (interval == 0) {
|
||||||
|
throw std::invalid_argument("Keyframe interval must be positive");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval != keyframe_interval_) {
|
||||||
|
keyframe_interval_ = interval;
|
||||||
|
if (!compressed_frames_.empty()) {
|
||||||
|
// Rebuild keyframe indices with new interval
|
||||||
|
rebuild_keyframe_indices();
|
||||||
|
|
||||||
|
// If we have frames, we may need to recompress some as keyframes
|
||||||
|
if (use_differential_encoding_) {
|
||||||
|
auto original_frames = get_all_frames();
|
||||||
|
clear_frames();
|
||||||
|
|
||||||
|
for (const auto& f : original_frames) {
|
||||||
|
add_frame(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a specific frame to be a keyframe
|
||||||
|
void make_keyframe(size_t index) {
|
||||||
|
if (index >= compressed_frames_.size()) {
|
||||||
|
throw std::out_of_range("Frame index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyframe_indices_.count(index)) {
|
||||||
|
// Recompress this frame as a keyframe
|
||||||
|
frame original_frame = get_frame_optimized(index);
|
||||||
|
compressed_frames_[index] = original_frame.compress_rle();
|
||||||
|
keyframe_indices_[index] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get video duration in seconds
|
// Get video duration in seconds
|
||||||
double duration() const noexcept {
|
double duration() const noexcept {
|
||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
@@ -310,6 +489,8 @@ public:
|
|||||||
double overall_ratio;
|
double overall_ratio;
|
||||||
double average_frame_ratio;
|
double average_frame_ratio;
|
||||||
double video_duration;
|
double video_duration;
|
||||||
|
size_t keyframe_count;
|
||||||
|
size_t keyframe_interval;
|
||||||
};
|
};
|
||||||
|
|
||||||
compression_stats get_compression_stats() const {
|
compression_stats get_compression_stats() const {
|
||||||
@@ -321,6 +502,8 @@ public:
|
|||||||
stats.overall_ratio = overall_compression_ratio();
|
stats.overall_ratio = overall_compression_ratio();
|
||||||
stats.average_frame_ratio = average_frame_compression_ratio();
|
stats.average_frame_ratio = average_frame_compression_ratio();
|
||||||
stats.video_duration = duration();
|
stats.video_duration = duration();
|
||||||
|
stats.keyframe_count = keyframe_indices_.size();
|
||||||
|
stats.keyframe_interval = keyframe_interval_;
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +515,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame_count = std::min(frame_count, compressed_frames_.size() - start_frame);
|
frame_count = std::min(frame_count, compressed_frames_.size() - start_frame);
|
||||||
video result(width_, height_, channels_, fps_, use_differential_encoding_);
|
video result(width_, height_, channels_, fps_, use_differential_encoding_, keyframe_interval_);
|
||||||
|
|
||||||
|
// Add frames one by one to maintain proper keyframe structure
|
||||||
for (size_t i = start_frame; i < start_frame + frame_count; ++i) {
|
for (size_t i = start_frame; i < start_frame + frame_count; ++i) {
|
||||||
result.compressed_frames_.push_back(compressed_frames_[i]);
|
result.add_frame(get_frame(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -348,30 +532,23 @@ public:
|
|||||||
throw std::invalid_argument("Videos must have same dimensions and channels");
|
throw std::invalid_argument("Videos must have same dimensions and channels");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If both use differential encoding, we can directly append compressed frames
|
// Add frames one by one to maintain proper keyframe structure
|
||||||
if (use_differential_encoding_ && other.use_differential_encoding_) {
|
auto other_frames = other.get_all_frames();
|
||||||
compressed_frames_.insert(compressed_frames_.end(),
|
for (const auto& frame : other_frames) {
|
||||||
other.compressed_frames_.begin(),
|
add_frame(frame);
|
||||||
other.compressed_frames_.end());
|
|
||||||
} else {
|
|
||||||
// Otherwise, we need to decompress and recompress
|
|
||||||
auto other_frames = other.get_all_frames();
|
|
||||||
for (const auto& frame : other_frames) {
|
|
||||||
add_frame(frame);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save/Load functionality (basic serialization)
|
// Save/Load functionality (basic serialization) - updated for keyframes
|
||||||
std::vector<uint8_t> serialize() const {
|
std::vector<uint8_t> serialize() const {
|
||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
// Simple serialization format:
|
// Simple serialization format:
|
||||||
// [header][compressed_frame_data...]
|
// [header][compressed_frame_data...]
|
||||||
// Header: width(4), height(4), channels_count(1), channels_data(n), fps(8), frame_count(4)
|
// Header: width(4), height(4), channels_count(1), channels_data(n), fps(8),
|
||||||
|
// use_diff(1), keyframe_interval(4), frame_count(4), keyframe_count(4), keyframe_indices...
|
||||||
|
|
||||||
std::vector<uint8_t> result;
|
std::vector<uint8_t> result;
|
||||||
|
|
||||||
// Header
|
|
||||||
auto add_uint32 = [&result](uint32_t value) {
|
auto add_uint32 = [&result](uint32_t value) {
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
result.push_back((value >> (i * 8)) & 0xFF);
|
result.push_back((value >> (i * 8)) & 0xFF);
|
||||||
@@ -394,8 +571,15 @@ public:
|
|||||||
}
|
}
|
||||||
add_double(fps_);
|
add_double(fps_);
|
||||||
result.push_back(use_differential_encoding_ ? 1 : 0);
|
result.push_back(use_differential_encoding_ ? 1 : 0);
|
||||||
|
add_uint32(static_cast<uint32_t>(keyframe_interval_));
|
||||||
add_uint32(static_cast<uint32_t>(compressed_frames_.size()));
|
add_uint32(static_cast<uint32_t>(compressed_frames_.size()));
|
||||||
|
|
||||||
|
// Write keyframe indices
|
||||||
|
add_uint32(static_cast<uint32_t>(keyframe_indices_.size()));
|
||||||
|
for (const auto& kv : keyframe_indices_) {
|
||||||
|
add_uint32(static_cast<uint32_t>(kv.first));
|
||||||
|
}
|
||||||
|
|
||||||
// Write compressed frames
|
// Write compressed frames
|
||||||
for (const auto& compressed_frame : compressed_frames_) {
|
for (const auto& compressed_frame : compressed_frames_) {
|
||||||
add_uint32(static_cast<uint32_t>(compressed_frame.size()));
|
add_uint32(static_cast<uint32_t>(compressed_frame.size()));
|
||||||
@@ -411,7 +595,7 @@ public:
|
|||||||
// Deserialize from byte data
|
// Deserialize from byte data
|
||||||
static video deserialize(const std::vector<uint8_t>& data) {
|
static video deserialize(const std::vector<uint8_t>& data) {
|
||||||
TIME_FUNCTION;
|
TIME_FUNCTION;
|
||||||
if (data.size() < 4 + 4 + 1 + 8 + 1 + 4) { // Minimum header size
|
if (data.size() < 4 + 4 + 1 + 8 + 1 + 4 + 4 + 4) { // Minimum header size
|
||||||
throw std::invalid_argument("Invalid video data: too short");
|
throw std::invalid_argument("Invalid video data: too short");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,9 +632,17 @@ public:
|
|||||||
|
|
||||||
double fps = read_double();
|
double fps = read_double();
|
||||||
bool use_diff = data[pos++] != 0;
|
bool use_diff = data[pos++] != 0;
|
||||||
|
uint32_t keyframe_interval = read_uint32();
|
||||||
uint32_t frame_count = read_uint32();
|
uint32_t frame_count = read_uint32();
|
||||||
|
|
||||||
video result(width, height, channels, fps, use_diff);
|
video result(width, height, channels, fps, use_diff, keyframe_interval);
|
||||||
|
|
||||||
|
// Read keyframe indices
|
||||||
|
uint32_t keyframe_count = read_uint32();
|
||||||
|
for (uint32_t i = 0; i < keyframe_count; ++i) {
|
||||||
|
uint32_t keyframe_index = read_uint32();
|
||||||
|
result.keyframe_indices_[keyframe_index] = keyframe_index;
|
||||||
|
}
|
||||||
|
|
||||||
// Read compressed frames
|
// Read compressed frames
|
||||||
for (uint32_t i = 0; i < frame_count; ++i) {
|
for (uint32_t i = 0; i < frame_count; ++i) {
|
||||||
|
|||||||
Reference in New Issue
Block a user