temp
This commit is contained in:
parent
dda8cd928e
commit
fe6315e468
@ -3,14 +3,11 @@ target_compile_options(blocky PRIVATE ${BLOCKY_C_FLAGS})
|
|||||||
|
|
||||||
target_sources(blocky
|
target_sources(blocky
|
||||||
PUBLIC
|
PUBLIC
|
||||||
main.cc
|
args.hh
|
||||||
|
|
||||||
bytes.hh
|
|
||||||
common.hh
|
common.hh
|
||||||
embed.hh
|
embed.cc
|
||||||
extract.hh
|
extract.cc
|
||||||
video_encoder.hh
|
main.cc
|
||||||
video_decoder.hh
|
|
||||||
)
|
)
|
||||||
target_link_libraries(blocky
|
target_link_libraries(blocky
|
||||||
PUBLIC
|
PUBLIC
|
||||||
|
92
blocky/args.hh
Normal file
92
blocky/args.hh
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <args.hxx>
|
||||||
|
|
||||||
|
#include "common.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace blky::args {
|
||||||
|
|
||||||
|
using namespace ::args;
|
||||||
|
|
||||||
|
|
||||||
|
struct uvec2_Reader final {
|
||||||
|
void operator()(const std::string&, const std::string& value, uvec2& dst) {
|
||||||
|
char _;
|
||||||
|
if (3 != sscanf(value.c_str(), "%" PRIu32 "%c%" PRIu32, &dst.x, &_, &dst.y)) {
|
||||||
|
throw std::runtime_error {"invalid uvec2 specification: "+value};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- root parser ---- */
|
||||||
|
static inline ArgumentParser parser {
|
||||||
|
"blocky is a command line tool for data hiding of video"
|
||||||
|
};
|
||||||
|
static inline Group commands { parser, "COMMAND:" };
|
||||||
|
static inline HelpFlag help { parser, "help", "display this help menu", {'h', "help"}, Options::Global };
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- group: key ---- */
|
||||||
|
static inline Group key { "key information:" };
|
||||||
|
static inline ValueFlag<uvec2, uvec2_Reader> block_num {
|
||||||
|
key, "8x8", "number of blocks", {"block-num"}, {8, 8}
|
||||||
|
};
|
||||||
|
static inline ValueFlag<uint32_t> feat_bits {
|
||||||
|
key, "1", "bits per each step", {"feat-bits"}, 1
|
||||||
|
};
|
||||||
|
static inline ValueFlag<uint32_t> seed {
|
||||||
|
key, "123", "random seed for hopping", {"seed"}, 123
|
||||||
|
};
|
||||||
|
static inline ValueFlag<uint32_t> utime {
|
||||||
|
key, "10", "duration of each step (frames)", {"utime"}, 10
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- group: stream source ---- */
|
||||||
|
static inline Group stream { "stream source (choose one):", Group::Validators::AtMostOne };
|
||||||
|
static inline Flag stream_stdin {
|
||||||
|
stream, "stdin", "load from stdin (default)", {"stdin"}, true
|
||||||
|
};
|
||||||
|
static inline Flag stream_stdin_hex {
|
||||||
|
stream, "stdin-hex", "load from stdin as HEX (e.g 'FF00'", {"stdin-hex"}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- command: embed ---- */
|
||||||
|
static inline Group embed {};
|
||||||
|
static inline Positional<std::string> embed_dst {
|
||||||
|
embed, "dst", "destination video path", Options::Required
|
||||||
|
};
|
||||||
|
static inline Positional<std::string> embed_src {
|
||||||
|
embed, "src", "source video path", Options::Required
|
||||||
|
};
|
||||||
|
static inline Command command_embed { commands, "embed", "embed stream to video", [](auto& p) {
|
||||||
|
p.Add(embed);
|
||||||
|
p.Add(stream);
|
||||||
|
p.Add(key);
|
||||||
|
p.Parse();
|
||||||
|
Cmd_Embed();
|
||||||
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
/* ---- command: extract ---- */
|
||||||
|
static inline Group extract {};
|
||||||
|
static inline Positional<std::string> extract_src {
|
||||||
|
extract, "src", "source video path", Options::Required
|
||||||
|
};
|
||||||
|
static inline Command command_extract { commands, "extract", "extract stream from video", [](auto& p) {
|
||||||
|
p.Add(extract);
|
||||||
|
p.Add(key);
|
||||||
|
p.Parse();
|
||||||
|
Cmd_Extract();
|
||||||
|
}};
|
||||||
|
|
||||||
|
} // namespace blky::args
|
@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace blky {
|
|
||||||
|
|
||||||
std::vector<uint32_t> BytesEncoder(
|
|
||||||
const std::vector<uint8_t>& bytes,
|
|
||||||
std::tuple<uint32_t, uint32_t>& block_num,
|
|
||||||
uint8_t feat_bits,
|
|
||||||
uint32_t block_idx,
|
|
||||||
uint64_t seed) {
|
|
||||||
const uint32_t block_num_all = std::get<0>(block_num)*std::get<1>(block_num);
|
|
||||||
if (block_num_all == 0) throw std::runtime_error {"block num is zero"};
|
|
||||||
|
|
||||||
blky_encoder_t enc = {};
|
|
||||||
enc.block_num = block_num_all;
|
|
||||||
enc.feat_bits = feat_bits;
|
|
||||||
enc.block_index = block_idx;
|
|
||||||
enc.seed = seed;
|
|
||||||
|
|
||||||
std::vector<uint32_t> ret;
|
|
||||||
for (auto c : bytes) {
|
|
||||||
blky_encoder_feed(&enc, c);
|
|
||||||
|
|
||||||
uint32_t feat;
|
|
||||||
while (blky_encoder_pop(&enc, &feat, false)) {
|
|
||||||
ret.push_back(feat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t feat;
|
|
||||||
if (blky_encoder_pop(&enc, &feat, true)) {
|
|
||||||
ret.push_back(feat);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> BytesDecoder(
|
|
||||||
const std::vector<uint32_t>& features,
|
|
||||||
std::tuple<uint32_t, uint32_t>& block_num,
|
|
||||||
uint8_t feat_bits,
|
|
||||||
uint64_t seed) {
|
|
||||||
const uint32_t block_num_all = std::get<0>(block_num)*std::get<1>(block_num);
|
|
||||||
if (block_num_all == 0) throw std::runtime_error {"block num is zero"};
|
|
||||||
|
|
||||||
blky_decoder_t de = {};
|
|
||||||
de.block_num = block_num_all;
|
|
||||||
de.feat_bits = feat_bits;
|
|
||||||
de.seed = seed;
|
|
||||||
|
|
||||||
std::vector<uint8_t> ret;
|
|
||||||
for (auto c : features) {
|
|
||||||
if (!blky_decoder_feed(&de, c)) {
|
|
||||||
throw std::runtime_error {"path corruption"};
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t feat;
|
|
||||||
while (blky_decoder_pop(&de, &feat, false)) {
|
|
||||||
ret.push_back(feat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint8_t feat;
|
|
||||||
if (blky_decoder_pop(&de, &feat, true)) {
|
|
||||||
ret.push_back(feat);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace blky
|
|
@ -1,59 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
# include <liblocky.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <tuple>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
|
|
||||||
namespace blky {
|
namespace blky {
|
||||||
|
|
||||||
enum DataFlow {
|
struct uvec2 final { uint32_t x, y; };
|
||||||
kBytes,
|
|
||||||
kFeatures,
|
|
||||||
kFeatureProbs,
|
|
||||||
kVideo,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline const std::string kDataFlowList = "bytes/features/video";
|
|
||||||
static inline const std::unordered_map<std::string, DataFlow> kDataFlowMap = {
|
|
||||||
{"bytes", kBytes},
|
|
||||||
{"features", kFeatures},
|
|
||||||
{"feature-probs", kFeatureProbs},
|
|
||||||
{"video", kVideo},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
/* ---- global state accessors ---- */
|
||||||
std::vector<T> ReadAll(auto& ist) noexcept {
|
std::istream& GetStream();
|
||||||
std::vector<T> ret;
|
|
||||||
for (;;) {
|
|
||||||
T v;
|
|
||||||
ist >> v;
|
|
||||||
if (ist.eof()) return ret;
|
|
||||||
ret.push_back(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t ToHex(char c) {
|
|
||||||
if (!std::isxdigit(c)) throw std::runtime_error {"not xdigit"};
|
/* ---- command handlers ---- */
|
||||||
return static_cast<uint8_t>(std::isalpha(c)? std::tolower(c)-'a'+0xA: c-'0');
|
void Cmd_Embed();
|
||||||
}
|
void Cmd_Extract();
|
||||||
|
|
||||||
} // namespace blky
|
} // namespace blky
|
||||||
|
|
||||||
|
|
||||||
namespace args {
|
|
||||||
|
|
||||||
auto& operator>>(auto& ist, std::tuple<uint32_t, uint32_t>& v) {
|
|
||||||
ist >> std::get<0>(v) >> std::get<1>(v);
|
|
||||||
return ist;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace args
|
|
||||||
|
10
blocky/embed.cc
Normal file
10
blocky/embed.cc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "common.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace blky {
|
||||||
|
|
||||||
|
void Cmd_Embed() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blky
|
114
blocky/embed.hh
114
blocky/embed.hh
@ -1,114 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "video_decoder.hh"
|
|
||||||
#include "video_encoder.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace blky {
|
|
||||||
|
|
||||||
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*feat_size_x, Y.data()+ay*w+feat_offset_x, feat_size_x);
|
|
||||||
//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;
|
|
||||||
if (enc) enc->ForceIntraFrame();
|
|
||||||
}
|
|
||||||
for (uint32_t y = 0; y < feat_size_y; ++y) {
|
|
||||||
const uint32_t ay = y+feat_offset_y;
|
|
||||||
std::memcpy(Y.data()+ay*w+feat_offset_x, feat_pix.data()+y*feat_size_x, feat_size_x);
|
|
||||||
//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 = CAMERA_VIDEO_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace blky
|
|
10
blocky/extract.cc
Normal file
10
blocky/extract.cc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "common.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace blky {
|
||||||
|
|
||||||
|
void Cmd_Extract() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace blky
|
@ -1,65 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "common.hh"
|
|
||||||
#include "video_decoder.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace blky {
|
|
||||||
|
|
||||||
std::vector<double> Extract(
|
|
||||||
VideoDecoder& dec,
|
|
||||||
std::tuple<uint32_t, uint32_t> block_num,
|
|
||||||
std::tuple<uint32_t, uint32_t> sensor_num,
|
|
||||||
uint32_t utime) {
|
|
||||||
std::vector<double> ret;
|
|
||||||
|
|
||||||
blky_extractor_t ex = {};
|
|
||||||
ex.block_num_x = std::get<0>(block_num);
|
|
||||||
ex.block_num_y = std::get<1>(block_num);
|
|
||||||
ex.sensor_num_block_x = std::get<0>(sensor_num);
|
|
||||||
ex.sensor_num_block_y = std::get<1>(sensor_num);
|
|
||||||
ex.samples_per_pix = 1;
|
|
||||||
ex.pix_stride = 1;
|
|
||||||
ex.utime = utime;
|
|
||||||
ex.correl_max_var = 0.3;
|
|
||||||
ex.correl_min_avg = 0.8;
|
|
||||||
|
|
||||||
blky_extractor_init(&ex);
|
|
||||||
while (dec.Decode()) {
|
|
||||||
const auto& frame = dec.frame();
|
|
||||||
if (frame.iBufferStatus != 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t w = static_cast<uint32_t>(frame.UsrData.sSystemBuffer.iWidth);
|
|
||||||
const uint32_t h = static_cast<uint32_t>(frame.UsrData.sSystemBuffer.iHeight);
|
|
||||||
|
|
||||||
const uint32_t stride_y = static_cast<uint32_t>(frame.UsrData.sSystemBuffer.iStride[0]);
|
|
||||||
//const uint32_t stride_uv = static_cast<uint32_t>(frame.UsrData.sSystemBuffer.iStride[1]);
|
|
||||||
|
|
||||||
const uint8_t* const* srcp = frame.pDst;
|
|
||||||
|
|
||||||
const double wf = static_cast<double>(w) / static_cast<double>(stride_y);
|
|
||||||
const double verts[8] = {
|
|
||||||
0, 0,
|
|
||||||
0, 1,
|
|
||||||
wf, 1,
|
|
||||||
wf, 0,
|
|
||||||
};
|
|
||||||
if (blky_extractor_feed(&ex, srcp[0], stride_y, h, verts)) {
|
|
||||||
const size_t psize = ret.size();
|
|
||||||
ret.resize(ret.size() + ex.block_num);
|
|
||||||
std::memcpy(ret.data() + psize, ex.probs, ex.block_num * sizeof(ret[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
blky_extractor_deinit(&ex);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace blky
|
|
@ -1,105 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include "common.hh"
|
|
||||||
|
|
||||||
|
|
||||||
namespace blky {
|
|
||||||
|
|
||||||
std::vector<uint32_t> PathfindFeatures(
|
|
||||||
const std::vector<double>& probs,
|
|
||||||
std::tuple<uint32_t, uint32_t>& block_num,
|
|
||||||
uint8_t feat_bits,
|
|
||||||
uint64_t seed) {
|
|
||||||
const uint32_t num = std::get<0>(block_num) * std::get<1>(block_num);
|
|
||||||
if (num == 0) throw std::runtime_error {"number of blocks is zero"};
|
|
||||||
|
|
||||||
const uint64_t dur = probs.size()/num;
|
|
||||||
if (probs.size() == 0) {
|
|
||||||
throw std::runtime_error {"size of probability matrix is empty"};
|
|
||||||
}
|
|
||||||
if (dur*num != probs.size()) {
|
|
||||||
throw std::runtime_error {"size of probability matrix is mismatch"};
|
|
||||||
}
|
|
||||||
|
|
||||||
blky_pathfinder_t pf = {};
|
|
||||||
pf.block_num = num;
|
|
||||||
pf.feat_bits = feat_bits;
|
|
||||||
pf.seed = seed;
|
|
||||||
|
|
||||||
blky_pathfinder_init(&pf);
|
|
||||||
|
|
||||||
for (uint64_t t = 0; t < dur; ++t) {
|
|
||||||
blky_pathfinder_feed(&pf, probs.data()+t*num);
|
|
||||||
}
|
|
||||||
assert(pf.step_last);
|
|
||||||
|
|
||||||
uint32_t max_idx = 0;
|
|
||||||
double max_prob = -1;
|
|
||||||
for (uint32_t i = 0; i < num; ++i) {
|
|
||||||
if (max_prob < pf.probs[i]) {
|
|
||||||
max_prob = pf.probs[i];
|
|
||||||
max_idx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint32_t> ret;
|
|
||||||
blky_pathfinder_step_t* step = pf.step_last;
|
|
||||||
uint32_t idx = max_idx;
|
|
||||||
for (;;) {
|
|
||||||
ret.push_back(idx);
|
|
||||||
if (!step) break;
|
|
||||||
idx = step->indices[idx];
|
|
||||||
step = step->prev;
|
|
||||||
}
|
|
||||||
std::reverse(ret.begin(), ret.end());
|
|
||||||
|
|
||||||
blky_pathfinder_deinit(&pf);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> GenerateFeatureProbs(
|
|
||||||
const std::vector<uint32_t>& features,
|
|
||||||
std::tuple<uint32_t, uint32_t>& block_num,
|
|
||||||
double false_positive,
|
|
||||||
double false_negative,
|
|
||||||
uint64_t seed,
|
|
||||||
bool normalize) {
|
|
||||||
const uint32_t num = std::get<0>(block_num) * std::get<1>(block_num);
|
|
||||||
if (num == 0) throw std::runtime_error {"number of blocks is zero"};
|
|
||||||
|
|
||||||
std::vector<double> ret;
|
|
||||||
for (auto c : features) {
|
|
||||||
const auto begin = ret.size();
|
|
||||||
|
|
||||||
double sum = 0;
|
|
||||||
for (uint32_t bi = 0; bi < num; ++bi) {
|
|
||||||
seed = blky_numeric_xorshift64(seed);
|
|
||||||
const double prob1 = static_cast<double>(seed%1000)/1000.;
|
|
||||||
|
|
||||||
seed = blky_numeric_xorshift64(seed);
|
|
||||||
const double prob2 = static_cast<double>(seed%1000)/1000.;
|
|
||||||
|
|
||||||
seed = blky_numeric_xorshift64(seed);
|
|
||||||
double fprob;
|
|
||||||
if ((c == bi && prob1 >= false_negative) || prob2 < false_positive) {
|
|
||||||
fprob = static_cast<double>(seed%200)/1000. + .8;
|
|
||||||
} else {
|
|
||||||
fprob = static_cast<double>(seed%800)/1000.;
|
|
||||||
}
|
|
||||||
ret.push_back(fprob);
|
|
||||||
sum += fprob;
|
|
||||||
}
|
|
||||||
if (normalize) {
|
|
||||||
for (size_t i = begin; i < ret.size(); ++i) {
|
|
||||||
ret[i] /= sum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace blky
|
|
304
blocky/main.cc
304
blocky/main.cc
@ -1,319 +1,25 @@
|
|||||||
#include <cassert>
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iomanip>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <optional>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include "common.hh"
|
#include "args.hh"
|
||||||
#include "bytes.hh"
|
|
||||||
#include "embed.hh"
|
|
||||||
#include "extract.hh"
|
|
||||||
#include "features.hh"
|
|
||||||
|
|
||||||
#include <args.hxx>
|
|
||||||
|
|
||||||
#include "video_decoder.hh"
|
|
||||||
#include "video_encoder.hh"
|
|
||||||
|
|
||||||
|
|
||||||
using namespace blky;
|
using namespace ::blky::args;
|
||||||
|
|
||||||
|
|
||||||
args::ArgumentParser parser(
|
|
||||||
"liblocky command line tool",
|
|
||||||
"liblocky allow you to embed a bit array into video data secretly");
|
|
||||||
|
|
||||||
args::MapFlag<std::string, DataFlow> from {
|
|
||||||
parser, kDataFlowList, "input layer specifier", {"from"}, kDataFlowMap, args::Options::Required};
|
|
||||||
|
|
||||||
args::MapFlag<std::string, DataFlow> to {
|
|
||||||
parser, kDataFlowList, "output layer specifier", {"to"}, kDataFlowMap, args::Options::Required};
|
|
||||||
|
|
||||||
args::Group src_group {
|
|
||||||
parser, "source specifier", args::Group::Validators::Xor, args::Options::Required};
|
|
||||||
args::Flag src_stdin {src_group, "src-stdin", "read from stdin", {"src-stdin", "stdin"}};
|
|
||||||
args::Flag src_stdin_hex {src_group, "src-stdin-hex", "read hex text from stdin", {"src-stdin-hex", "stdin-hex"}};
|
|
||||||
args::ValueFlag<std::string> src_video {src_group, "path", "video input", {"src-video"}};
|
|
||||||
|
|
||||||
args::Group dst_group {
|
|
||||||
parser, "destination specifier", args::Group::Validators::Xor, args::Options::Required};
|
|
||||||
args::Flag dst_stdout {dst_group, "dst-stdout", "write to stdout", {"dst-stdout", "stdout"}};
|
|
||||||
args::Flag dst_stdout_hex {dst_group, "dst-stdout-hex", "write to stdout as hex text", {"dst-stdout-hex", "stdout-hex"}};
|
|
||||||
args::ValueFlag<std::string> dst_video {dst_group, "path", "video output", {"dst-video"}};
|
|
||||||
|
|
||||||
args::Group param_group {
|
|
||||||
parser, "general parameters", args::Group::Validators::DontCare
|
|
||||||
};
|
|
||||||
args::ValueFlag<std::tuple<uint32_t, uint32_t>> param_block_num {
|
|
||||||
param_group,
|
|
||||||
"int>0,int>0",
|
|
||||||
"number of features",
|
|
||||||
{"feature-num"},
|
|
||||||
{8, 8}
|
|
||||||
};
|
|
||||||
args::ValueFlag<uint32_t> param_block_first {
|
|
||||||
param_group,
|
|
||||||
"int>=0",
|
|
||||||
"an index of first block where feature will be embedded. used when encoding",
|
|
||||||
{"feature-first-index"},
|
|
||||||
0
|
|
||||||
};
|
|
||||||
args::ValueFlag<uint8_t> param_feat_bits {
|
|
||||||
param_group,
|
|
||||||
"int>0",
|
|
||||||
"number of bits that can be represented by a single feature",
|
|
||||||
{"feature-bits"},
|
|
||||||
1
|
|
||||||
};
|
|
||||||
args::ValueFlag<uint8_t> param_seed {
|
|
||||||
param_group,
|
|
||||||
"int>0",
|
|
||||||
"seed number for hopping randomization",
|
|
||||||
{"seed"},
|
|
||||||
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::ValueFlag<std::tuple<uint32_t, uint32_t>> param_sensor_num {
|
|
||||||
param_group,
|
|
||||||
"int>0,int>0",
|
|
||||||
"number of sensors (for extraction)",
|
|
||||||
{"sensor-num"},
|
|
||||||
{16, 16}
|
|
||||||
};
|
|
||||||
|
|
||||||
args::Group probgen_group {
|
|
||||||
parser, "params for feature probability generator", args::Group::Validators::DontCare
|
|
||||||
};
|
|
||||||
args::ValueFlag<double> probgen_false_positive {
|
|
||||||
probgen_group,
|
|
||||||
"0<=double<=1",
|
|
||||||
"false positive ratio in feature probability generation",
|
|
||||||
{"probgen-false-positive"},
|
|
||||||
0
|
|
||||||
};
|
|
||||||
args::ValueFlag<double> probgen_false_negative {
|
|
||||||
probgen_group,
|
|
||||||
"0<=double<=1",
|
|
||||||
"false negative ratio in feature probability generation",
|
|
||||||
{"probgen-false-negative"},
|
|
||||||
0
|
|
||||||
};
|
|
||||||
args::ValueFlag<uint64_t> probgen_seed {
|
|
||||||
probgen_group,
|
|
||||||
"int>0",
|
|
||||||
"random seed",
|
|
||||||
{"probgen-seed"},
|
|
||||||
1
|
|
||||||
};
|
|
||||||
args::Flag probgen_normalize {
|
|
||||||
probgen_group,
|
|
||||||
"probgen-normalize",
|
|
||||||
"normalize probabilities",
|
|
||||||
{"probgen-normalize"},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<uint8_t> bytes;
|
|
||||||
std::vector<uint32_t> features;
|
|
||||||
std::vector<double> feature_probs;
|
|
||||||
std::optional<VideoDecoder> decoder;
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
try {
|
try {
|
||||||
parser.ParseCLI(argc, argv);
|
parser.ParseCLI(argc, argv);
|
||||||
|
|
||||||
// read input
|
} catch (const Help&) {
|
||||||
switch (args::get(from)) {
|
|
||||||
case kBytes:
|
|
||||||
if (src_stdin) {
|
|
||||||
std::string temp;
|
|
||||||
std::cin >> temp;
|
|
||||||
bytes = {temp.begin(), temp.end()};
|
|
||||||
} else if (src_stdin_hex) {
|
|
||||||
for (;;) {
|
|
||||||
char buf[2];
|
|
||||||
std::cin >> buf[0] >> buf[1];
|
|
||||||
if (std::cin.eof()) break;
|
|
||||||
bytes.push_back(static_cast<uint8_t>((ToHex(buf[0]) << 4) | ToHex(buf[1])));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error {"invalid source format for bytes"};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kFeatures:
|
|
||||||
if (src_stdin) {
|
|
||||||
features = ReadAll<uint32_t>(std::cin);
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error {"invalid source format for features"};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kFeatureProbs:
|
|
||||||
if (src_stdin) {
|
|
||||||
feature_probs = ReadAll<double>(std::cin);
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error {"invalid source format for feature probs"};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kVideo:
|
|
||||||
if (src_video) {
|
|
||||||
decoder.emplace(args::get(src_video));
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error {"invalid source format for video"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args::get(from) < args::get(to)) {
|
|
||||||
// execute encoding
|
|
||||||
switch (args::get(from)) {
|
|
||||||
case kBytes:
|
|
||||||
if (args::get(to) == kBytes) break;
|
|
||||||
features = BytesEncoder(
|
|
||||||
bytes,
|
|
||||||
args::get(param_block_num),
|
|
||||||
args::get(param_feat_bits),
|
|
||||||
args::get(param_block_first),
|
|
||||||
args::get(param_seed));
|
|
||||||
/* fallthrough */
|
|
||||||
|
|
||||||
case kFeatures:
|
|
||||||
if (args::get(to) == kFeatures) break;
|
|
||||||
if (args::get(to) == kFeatureProbs) {
|
|
||||||
feature_probs = GenerateFeatureProbs(
|
|
||||||
features,
|
|
||||||
args::get(param_block_num),
|
|
||||||
args::get(probgen_false_positive),
|
|
||||||
args::get(probgen_false_negative),
|
|
||||||
args::get(probgen_seed),
|
|
||||||
probgen_normalize);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(dst_video);
|
|
||||||
Embed(
|
|
||||||
features,
|
|
||||||
args::get(dst_video),
|
|
||||||
args::get(param_video),
|
|
||||||
args::get(param_block_num),
|
|
||||||
args::get(param_utime));
|
|
||||||
/* fallthrough */
|
|
||||||
|
|
||||||
case kVideo:
|
|
||||||
if (args::get(to) == kVideo) break;
|
|
||||||
assert(false);
|
|
||||||
|
|
||||||
case kFeatureProbs:
|
|
||||||
throw std::runtime_error("couldn't start flow from the data");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (args::get(from) > args::get(to)) {
|
|
||||||
// execute decoding
|
|
||||||
switch (args::get(from)) {
|
|
||||||
case kVideo:
|
|
||||||
if (args::get(to) == kVideo) break;
|
|
||||||
if (!decoder) {
|
|
||||||
throw std::runtime_error {"decoder is empty"};
|
|
||||||
}
|
|
||||||
feature_probs = Extract(
|
|
||||||
*decoder,
|
|
||||||
args::get(param_block_num),
|
|
||||||
args::get(param_sensor_num),
|
|
||||||
args::get(param_utime));
|
|
||||||
/* fallthrough */
|
|
||||||
|
|
||||||
case kFeatureProbs:
|
|
||||||
if (args::get(to) == kFeatureProbs) break;
|
|
||||||
features = PathfindFeatures(
|
|
||||||
feature_probs,
|
|
||||||
args::get(param_block_num),
|
|
||||||
args::get(param_feat_bits),
|
|
||||||
args::get(param_seed));
|
|
||||||
/* fallthrough */
|
|
||||||
|
|
||||||
case kFeatures:
|
|
||||||
if (args::get(to) == kFeatures) break;
|
|
||||||
bytes = BytesDecoder(
|
|
||||||
features,
|
|
||||||
args::get(param_block_num),
|
|
||||||
args::get(param_feat_bits),
|
|
||||||
args::get(param_seed));
|
|
||||||
/* fallthrough */
|
|
||||||
|
|
||||||
case kBytes:
|
|
||||||
if (args::get(to) == kBytes) break;
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// output
|
|
||||||
switch (args::get(to)) {
|
|
||||||
case kBytes:
|
|
||||||
if (dst_stdout) {
|
|
||||||
std::cout << std::string {bytes.begin(), bytes.end()} << std::endl;
|
|
||||||
} else if (dst_stdout_hex) {
|
|
||||||
for (auto c : bytes) {
|
|
||||||
std::cout << std::hex << (int) c;
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error {"invalid destination format for bytes"};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kFeatures:
|
|
||||||
if (dst_stdout) {
|
|
||||||
for (auto& f : features) std::cout << f << "\n";
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error {"invalid destination format for features"};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kFeatureProbs:
|
|
||||||
if (dst_stdout) {
|
|
||||||
const auto size = args::get(param_block_num);
|
|
||||||
const auto cols = std::get<0>(size) * std::get<1>(size);
|
|
||||||
for (size_t i = 0; i < feature_probs.size();) {
|
|
||||||
for (size_t j = 0; i < feature_probs.size() && j < cols; ++i, ++j) {
|
|
||||||
std::cout << feature_probs[i] << " ";
|
|
||||||
}
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error {"invalid destination format for feature probs"};
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kVideo:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
} catch (const args::Help&) {
|
|
||||||
std::cout << parser << std::endl;
|
std::cout << parser << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
} catch (const args::ParseError& e) {
|
} catch (const ParseError& e) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
std::cerr << parser << std::endl;
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} catch (const args::ValidationError& e) {
|
} catch (const ValidationError& e) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
std::cerr << parser << std::endl;
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
} catch (const std::runtime_error& e) {
|
} catch (const std::runtime_error& e) {
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <exception>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <minimp4.h>
|
|
||||||
|
|
||||||
#include <wels/codec_api.h>
|
|
||||||
|
|
||||||
|
|
||||||
class VideoDecoder final {
|
|
||||||
public:
|
|
||||||
VideoDecoder() = delete;
|
|
||||||
VideoDecoder(const std::string& path) :
|
|
||||||
file_(path, std::ifstream::binary | std::ifstream::ate),
|
|
||||||
size_(static_cast<size_t>(file_.tellg())) {
|
|
||||||
// init objects
|
|
||||||
SDecodingParam dparam = {};
|
|
||||||
dparam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
|
|
||||||
dparam.eEcActiveIdc = ERROR_CON_SLICE_COPY;
|
|
||||||
|
|
||||||
WelsCreateDecoder(&decoder_);
|
|
||||||
decoder_->Initialize(&dparam);
|
|
||||||
|
|
||||||
int lv = WELS_LOG_DEBUG;
|
|
||||||
decoder_->SetOption(DECODER_OPTION_TRACE_LEVEL, &lv);
|
|
||||||
|
|
||||||
demuxer_ = {};
|
|
||||||
MP4D_open(&demuxer_, ReadCallback, this, static_cast<int64_t>(size_));
|
|
||||||
|
|
||||||
// find video track
|
|
||||||
track_ = SIZE_MAX;
|
|
||||||
for (size_t i = 0; i < demuxer_.track_count; ++i) {
|
|
||||||
if (demuxer_.track[i].handler_type == MP4D_HANDLER_TYPE_VIDE) {
|
|
||||||
if (track_ != SIZE_MAX) {
|
|
||||||
throw std::runtime_error {"there are many video tracks"};
|
|
||||||
}
|
|
||||||
track_ = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (track_ == SIZE_MAX) {
|
|
||||||
throw std::runtime_error {"there is no video track"};
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup decoder
|
|
||||||
std::vector<uint8_t> temp_;
|
|
||||||
|
|
||||||
for (size_t i = 0;; ++i) {
|
|
||||||
int size;
|
|
||||||
auto sps = static_cast<const uint8_t*>(MP4D_read_sps(
|
|
||||||
&demuxer_,
|
|
||||||
static_cast<unsigned int>(track_),
|
|
||||||
static_cast<int>(i),
|
|
||||||
&size));
|
|
||||||
if (!sps) break;
|
|
||||||
|
|
||||||
temp_.resize(static_cast<size_t>(4+size));
|
|
||||||
temp_[0] = 0, temp_[1] = 0, temp_[2] = 0, temp_[3] = 1;
|
|
||||||
std::memcpy(&temp_[4], sps, static_cast<size_t>(size));
|
|
||||||
|
|
||||||
if (decoder_->DecodeFrameNoDelay(temp_.data(), static_cast<int>(temp_.size()), yuv_, &frame_)) {
|
|
||||||
throw std::runtime_error {"failed to decode SPS"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (size_t i = 0;; ++i) {
|
|
||||||
int size;
|
|
||||||
auto pps = static_cast<const uint8_t*>(MP4D_read_pps(
|
|
||||||
&demuxer_,
|
|
||||||
static_cast<unsigned int>(track_),
|
|
||||||
static_cast<int>(i),
|
|
||||||
&size));
|
|
||||||
if (!pps) break;
|
|
||||||
|
|
||||||
temp_.resize(static_cast<size_t>(4+size));
|
|
||||||
temp_[0] = 0, temp_[1] = 0, temp_[2] = 0, temp_[3] = 1;
|
|
||||||
std::memcpy(&temp_[4], pps, static_cast<size_t>(size));
|
|
||||||
|
|
||||||
if (decoder_->DecodeFrameNoDelay(temp_.data(), static_cast<int>(temp_.size()), yuv_, &frame_)) {
|
|
||||||
throw std::runtime_error {"failed to decode SPS"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
temp_.clear();
|
|
||||||
}
|
|
||||||
~VideoDecoder() noexcept {
|
|
||||||
decoder_->Uninitialize();
|
|
||||||
WelsDestroyDecoder(decoder_);
|
|
||||||
|
|
||||||
MP4D_close(&demuxer_);
|
|
||||||
}
|
|
||||||
VideoDecoder(const VideoDecoder&) = delete;
|
|
||||||
VideoDecoder(VideoDecoder&&) = delete;
|
|
||||||
VideoDecoder& operator=(const VideoDecoder&) = delete;
|
|
||||||
VideoDecoder& operator=(VideoDecoder&&) = delete;
|
|
||||||
|
|
||||||
bool Decode() {
|
|
||||||
if (temp_consumed_ >= temp_.size()) {
|
|
||||||
if (count_ >= demuxer_.track[track_].sample_count) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned size, time, dur;
|
|
||||||
const auto off = MP4D_frame_offset(
|
|
||||||
&demuxer_,
|
|
||||||
static_cast<unsigned int>(track_),
|
|
||||||
static_cast<unsigned int>(count_),
|
|
||||||
&size, &time, &dur);
|
|
||||||
|
|
||||||
assert(size > 0);
|
|
||||||
temp_.resize(size);
|
|
||||||
temp_consumed_ = 0;
|
|
||||||
|
|
||||||
file_.seekg(static_cast<std::streamoff>(off));
|
|
||||||
assert(file_);
|
|
||||||
file_.read((char*) temp_.data(), size);
|
|
||||||
assert(file_);
|
|
||||||
|
|
||||||
Decode();
|
|
||||||
++count_;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
auto& i = temp_consumed_;
|
|
||||||
|
|
||||||
const uint32_t nal_size = 4 +
|
|
||||||
static_cast<uint32_t>((temp_[i+0] << 24) |
|
|
||||||
(temp_[i+1] << 16) |
|
|
||||||
(temp_[i+2] << 8) |
|
|
||||||
(temp_[i+3] << 0));
|
|
||||||
temp_[i ] = 0;
|
|
||||||
temp_[i+1] = 0;
|
|
||||||
temp_[i+2] = 0;
|
|
||||||
temp_[i+3] = 1;
|
|
||||||
if (decoder_->DecodeFrameNoDelay(temp_.data()+i, static_cast<int>(nal_size), yuv_, &frame_)) {
|
|
||||||
throw std::runtime_error {"failed to decode a frame"};
|
|
||||||
}
|
|
||||||
i += nal_size;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SBufferInfo& frame() const noexcept { return frame_; }
|
|
||||||
const uint8_t* const* yuv() const noexcept { return yuv_; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::ifstream file_;
|
|
||||||
size_t size_;
|
|
||||||
|
|
||||||
ISVCDecoder* decoder_;
|
|
||||||
MP4D_demux_t demuxer_;
|
|
||||||
|
|
||||||
size_t track_;
|
|
||||||
|
|
||||||
uint8_t* yuv_[3] = {0};
|
|
||||||
SBufferInfo frame_ = {};
|
|
||||||
size_t count_ = 0;
|
|
||||||
|
|
||||||
size_t temp_consumed_ = 0;
|
|
||||||
std::vector<uint8_t> temp_;
|
|
||||||
|
|
||||||
|
|
||||||
static int ReadCallback(int64_t off, void* buf, size_t size, void* ptr) noexcept {
|
|
||||||
auto self = (VideoDecoder*) ptr;
|
|
||||||
|
|
||||||
auto n = self->size_ - static_cast<size_t>(off) - size;
|
|
||||||
if (size < n) n = size;
|
|
||||||
|
|
||||||
self->file_.seekg(off);
|
|
||||||
assert(self->file_);
|
|
||||||
|
|
||||||
self->file_.read((char*) buf, static_cast<std::streamsize>(n));
|
|
||||||
assert(self->file_);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,82 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <exception>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <minimp4.h>
|
|
||||||
|
|
||||||
#include <wels/codec_api.h>
|
|
||||||
|
|
||||||
|
|
||||||
class VideoEncoder final {
|
|
||||||
public:
|
|
||||||
VideoEncoder() = delete;
|
|
||||||
VideoEncoder(const std::string& path, const SEncParamBase& p) :
|
|
||||||
file_(path, std::ofstream::binary), fps_(p.fMaxFrameRate) {
|
|
||||||
if (WelsCreateSVCEncoder(&encoder_)) {
|
|
||||||
throw std::runtime_error {"failed to init openh264 encoder"};
|
|
||||||
}
|
|
||||||
|
|
||||||
muxer_ = MP4E_open(false, false, this, WriteCallback);
|
|
||||||
if (MP4E_STATUS_OK != mp4_h26x_write_init(&writer_, muxer_, p.iPicWidth, p.iPicHeight, false)) {
|
|
||||||
throw std::runtime_error {"failed to init h26x writer"};
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder_->Initialize(&p);
|
|
||||||
|
|
||||||
int lv = WELS_LOG_DEBUG;
|
|
||||||
encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL, &lv);
|
|
||||||
int fmt = videoFormatI420;
|
|
||||||
encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, &fmt);
|
|
||||||
}
|
|
||||||
~VideoEncoder() noexcept {
|
|
||||||
encoder_->Uninitialize();
|
|
||||||
WelsDestroySVCEncoder(encoder_);
|
|
||||||
|
|
||||||
MP4E_close(muxer_);
|
|
||||||
mp4_h26x_write_close(&writer_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Encode(const SSourcePicture& pic) {
|
|
||||||
SFrameBSInfo info;
|
|
||||||
if (cmResultSuccess != encoder_->EncodeFrame(&pic, &info)) {
|
|
||||||
throw std::runtime_error {"failed to encode a frame"};
|
|
||||||
}
|
|
||||||
if (info.eFrameType == videoFrameTypeSkip) return;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < static_cast<size_t>(info.iLayerNum); ++i) {
|
|
||||||
const auto& lay = info.sLayerInfo[i];
|
|
||||||
|
|
||||||
uint8_t* buf = lay.pBsBuf;
|
|
||||||
for (size_t j = 0; j < static_cast<size_t>(lay.iNalCount); ++j) {
|
|
||||||
mp4_h26x_write_nal(&writer_, buf, lay.pNalLengthInByte[j], static_cast<unsigned int>(90000./fps_));
|
|
||||||
buf += lay.pNalLengthInByte[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void ForceIntraFrame() noexcept {
|
|
||||||
encoder_->ForceIntraFrame(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::ofstream file_;
|
|
||||||
|
|
||||||
ISVCEncoder* encoder_;
|
|
||||||
MP4E_mux_t* muxer_;
|
|
||||||
mp4_h26x_writer_t writer_;
|
|
||||||
|
|
||||||
double fps_;
|
|
||||||
|
|
||||||
|
|
||||||
static int WriteCallback(int64_t off, const void* buf, size_t size, void* ptr) noexcept {
|
|
||||||
auto self = (VideoEncoder*) ptr;
|
|
||||||
self->file_.seekp(off);
|
|
||||||
assert(self->file_);
|
|
||||||
self->file_.write(static_cast<const char*>(buf), static_cast<std::streamsize>(size));
|
|
||||||
return !self->file_;
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user