implement embedding
This commit is contained in:
parent
c9f19b960f
commit
05e9af696d
@ -17,3 +17,7 @@ ffmpeg -i in.mp4 -vframes 300 "%d.png"
|
|||||||
```
|
```
|
||||||
ffmpeg -r 30 -i "%d.png" -vcodec libx264 -pix_fmt yuv420p out.mp4
|
ffmpeg -r 30 -i "%d.png" -vcodec libx264 -pix_fmt yuv420p out.mp4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
ffmpeg -i src.mp4 -t 10s cut.mp4
|
||||||
|
```
|
||||||
|
107
blocky/embed.hh
Normal file
107
blocky/embed.hh
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "video_decoder.hh"
|
||||||
|
#include "video_encoder.hh"
|
||||||
|
|
||||||
|
|
||||||
|
void Embed(const std::vector<uint32_t>& features,
|
||||||
|
const std::string& dst_path,
|
||||||
|
const std::string& video_path,
|
||||||
|
const std::tuple<uint32_t, uint32_t>& div,
|
||||||
|
uint32_t utime) {
|
||||||
|
assert(features.size());
|
||||||
|
|
||||||
|
const uint32_t div_x = std::get<0>(div);
|
||||||
|
const uint32_t div_y = std::get<1>(div);
|
||||||
|
|
||||||
|
VideoDecoder dec {video_path};
|
||||||
|
std::optional<VideoEncoder> enc;
|
||||||
|
|
||||||
|
std::vector<uint8_t> Y;
|
||||||
|
std::vector<uint8_t> U;
|
||||||
|
std::vector<uint8_t> V;
|
||||||
|
|
||||||
|
uint32_t last_feat = UINT32_MAX;
|
||||||
|
std::vector<uint8_t> feat_pix;
|
||||||
|
for (uint32_t time = 0; dec.Decode();) {
|
||||||
|
const auto& src = dec.frame();
|
||||||
|
if (src.iBufferStatus != 1) continue;
|
||||||
|
++time;
|
||||||
|
|
||||||
|
const uint32_t w = static_cast<uint32_t>(src.UsrData.sSystemBuffer.iWidth);
|
||||||
|
const uint32_t h = static_cast<uint32_t>(src.UsrData.sSystemBuffer.iHeight);
|
||||||
|
|
||||||
|
const uint32_t stride_y = static_cast<uint32_t>(src.UsrData.sSystemBuffer.iStride[0]);
|
||||||
|
const uint32_t stride_uv = static_cast<uint32_t>(src.UsrData.sSystemBuffer.iStride[1]);
|
||||||
|
|
||||||
|
const uint8_t* const* srcp = src.pDst;
|
||||||
|
|
||||||
|
// copy buffer to modify
|
||||||
|
Y.resize(w*h);
|
||||||
|
U.resize(w*h/2/2);
|
||||||
|
V.resize(w*h/2/2);
|
||||||
|
for (uint32_t y = 0; y < h; ++y) {
|
||||||
|
std::memcpy(Y.data()+y*w, srcp[0]+stride_y*y, w);
|
||||||
|
}
|
||||||
|
for (uint32_t y = 0; y < h/2; ++y) {
|
||||||
|
std::memcpy(U.data()+y*(w/2), srcp[1]+stride_uv*y, w/2);
|
||||||
|
std::memcpy(V.data()+y*(w/2), srcp[2]+stride_uv*y, w/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// embed a feature to the buffer
|
||||||
|
const uint32_t feat = features[(time/utime)%features.size()];
|
||||||
|
|
||||||
|
const uint32_t feat_x = feat%div_x;
|
||||||
|
const uint32_t feat_y = feat/div_x;
|
||||||
|
|
||||||
|
const uint32_t feat_size_x = w/div_x;
|
||||||
|
const uint32_t feat_size_y = h/div_y;
|
||||||
|
|
||||||
|
const uint32_t feat_offset_x = feat_x*feat_size_x;
|
||||||
|
const uint32_t feat_offset_y = feat_y*feat_size_y;
|
||||||
|
|
||||||
|
if (feat != last_feat) {
|
||||||
|
feat_pix.resize(feat_size_x*feat_size_y);
|
||||||
|
for (uint32_t y = 0; y < feat_size_y; ++y) {
|
||||||
|
const uint32_t ay = y+feat_offset_y;
|
||||||
|
std::memcpy(feat_pix.data()+(y/2)*(feat_size_x/2), U.data()+(ay/2)*(w/2)+feat_offset_x/2, feat_size_x/2);
|
||||||
|
std::memcpy(feat_pix.data()+(y/2)*(feat_size_x/2)+feat_pix.size()/2, V.data()+(ay/2)*(w/2)+feat_offset_x/2, feat_size_x/2);
|
||||||
|
}
|
||||||
|
last_feat = feat;
|
||||||
|
}
|
||||||
|
for (uint32_t y = 0; y < feat_size_y; ++y) {
|
||||||
|
const uint32_t ay = y+feat_offset_y;
|
||||||
|
std::memcpy(U.data()+(ay/2)*(w/2)+feat_offset_x/2, feat_pix.data()+(y/2)*(feat_size_x/2), feat_size_x/2);
|
||||||
|
std::memcpy(V.data()+(ay/2)*(w/2)+feat_offset_x/2, feat_pix.data()+(y/2)*(feat_size_x/2)+feat_pix.size()/2, feat_size_x/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an encoder if not yet
|
||||||
|
if (!enc) {
|
||||||
|
SEncParamBase param = {};
|
||||||
|
param.iUsageType = SCREEN_CONTENT_REAL_TIME;
|
||||||
|
param.iPicWidth = static_cast<int>(w);
|
||||||
|
param.iPicHeight = static_cast<int>(h);
|
||||||
|
param.fMaxFrameRate = 30;
|
||||||
|
param.iTargetBitrate = 5000000;
|
||||||
|
enc.emplace(dst_path, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode
|
||||||
|
SSourcePicture dst = {};
|
||||||
|
dst.iColorFormat = videoFormatI420;
|
||||||
|
dst.pData[0] = Y.data();
|
||||||
|
dst.pData[1] = U.data();
|
||||||
|
dst.pData[2] = V.data();
|
||||||
|
dst.iStride[0] = static_cast<int>(w);
|
||||||
|
dst.iStride[1] = static_cast<int>(w/2);
|
||||||
|
dst.iStride[2] = static_cast<int>(w/2);
|
||||||
|
dst.iPicWidth = static_cast<int>(w);
|
||||||
|
dst.iPicHeight = static_cast<int>(h);
|
||||||
|
dst.uiTimeStamp = static_cast<int64_t>(src.uiOutYuvTimeStamp);
|
||||||
|
enc->Encode(dst);
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "common.hh"
|
#include "common.hh"
|
||||||
#include "bytes.hh"
|
#include "bytes.hh"
|
||||||
|
#include "embed.hh"
|
||||||
#include "features.hh"
|
#include "features.hh"
|
||||||
|
|
||||||
#include <args.hxx>
|
#include <args.hxx>
|
||||||
@ -71,6 +72,19 @@ args::ValueFlag<uint8_t> param_seed {
|
|||||||
{"seed"},
|
{"seed"},
|
||||||
123
|
123
|
||||||
};
|
};
|
||||||
|
args::ValueFlag<std::string> param_video {
|
||||||
|
param_group,
|
||||||
|
"path",
|
||||||
|
"a video file where information is embed",
|
||||||
|
{"path"},
|
||||||
|
};
|
||||||
|
args::ValueFlag<uint32_t> param_utime {
|
||||||
|
param_group,
|
||||||
|
"int>0",
|
||||||
|
"a duration (milliseconds) of features",
|
||||||
|
{"utime"},
|
||||||
|
10
|
||||||
|
};
|
||||||
|
|
||||||
args::Group probgen_group {
|
args::Group probgen_group {
|
||||||
parser, "params for feature probability generator", args::Group::Validators::DontCare
|
parser, "params for feature probability generator", args::Group::Validators::DontCare
|
||||||
@ -125,7 +139,7 @@ try {
|
|||||||
char buf[2];
|
char buf[2];
|
||||||
std::cin >> buf[0] >> buf[1];
|
std::cin >> buf[0] >> buf[1];
|
||||||
if (std::cin.eof()) break;
|
if (std::cin.eof()) break;
|
||||||
bytes.push_back(ToHex(buf[0]) << 4 | ToHex(buf[1]));
|
bytes.push_back(static_cast<uint8_t>((ToHex(buf[0]) << 4) | ToHex(buf[1])));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error {"invalid source format for bytes"};
|
throw std::runtime_error {"invalid source format for bytes"};
|
||||||
@ -181,8 +195,13 @@ try {
|
|||||||
probgen_normalize);
|
probgen_normalize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// TODO embed into video
|
assert(dst_video);
|
||||||
assert(false);
|
Embed(
|
||||||
|
features,
|
||||||
|
args::get(dst_video),
|
||||||
|
args::get(param_video),
|
||||||
|
args::get(param_block_num),
|
||||||
|
args::get(param_utime));
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
|
|
||||||
case kVideo:
|
case kVideo:
|
||||||
|
@ -97,8 +97,12 @@ class VideoDecoder final {
|
|||||||
VideoDecoder& operator=(const VideoDecoder&) = delete;
|
VideoDecoder& operator=(const VideoDecoder&) = delete;
|
||||||
VideoDecoder& operator=(VideoDecoder&&) = delete;
|
VideoDecoder& operator=(VideoDecoder&&) = delete;
|
||||||
|
|
||||||
void Decode() {
|
bool Decode() {
|
||||||
if (temp_consumed_ >= temp_.size()) {
|
if (temp_consumed_ >= temp_.size()) {
|
||||||
|
if (count_ >= demuxer_.track[track_].sample_count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned size, time, dur;
|
unsigned size, time, dur;
|
||||||
const auto off = MP4D_frame_offset(
|
const auto off = MP4D_frame_offset(
|
||||||
&demuxer_,
|
&demuxer_,
|
||||||
@ -116,6 +120,8 @@ class VideoDecoder final {
|
|||||||
assert(file_);
|
assert(file_);
|
||||||
|
|
||||||
Decode();
|
Decode();
|
||||||
|
++count_;
|
||||||
|
return true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
auto& i = temp_consumed_;
|
auto& i = temp_consumed_;
|
||||||
@ -129,13 +135,17 @@ class VideoDecoder final {
|
|||||||
temp_[i+1] = 0;
|
temp_[i+1] = 0;
|
||||||
temp_[i+2] = 0;
|
temp_[i+2] = 0;
|
||||||
temp_[i+3] = 1;
|
temp_[i+3] = 1;
|
||||||
if (decoder_->DecodeFrameNoDelay(temp_.data(), static_cast<int>(nal_size), yuv_, &frame_)) {
|
if (decoder_->DecodeFrameNoDelay(temp_.data()+i, static_cast<int>(nal_size), yuv_, &frame_)) {
|
||||||
throw std::runtime_error {"failed to decode a frame"};
|
throw std::runtime_error {"failed to decode a frame"};
|
||||||
}
|
}
|
||||||
i += nal_size;
|
i += nal_size;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SBufferInfo& frame() const noexcept { return frame_; }
|
||||||
|
const uint8_t* const* yuv() const noexcept { return yuv_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::ifstream file_;
|
std::ifstream file_;
|
||||||
size_t size_;
|
size_t size_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user