#include #include #include #include #include #include #include "common.hh" #include "bytes.hh" #include "embed.hh" #include "extract.hh" #include "features.hh" #include #include "video_decoder.hh" #include "video_encoder.hh" using namespace blky; args::ArgumentParser parser( "liblocky command line tool", "liblocky allow you to embed a bit array into video data secretly"); args::MapFlag from { parser, kDataFlowList, "input layer specifier", {"from"}, kDataFlowMap, args::Options::Required}; args::MapFlag 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 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 dst_video {dst_group, "path", "video output", {"dst-video"}}; args::Group param_group { parser, "general parameters", args::Group::Validators::DontCare }; args::ValueFlag> param_block_num { param_group, "int>0,int>0", "number of features", {"feature-num"}, {8, 8} }; args::ValueFlag 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 param_feat_bits { param_group, "int>0", "number of bits that can be represented by a single feature", {"feature-bits"}, 1 }; args::ValueFlag param_seed { param_group, "int>0", "seed number for hopping randomization", {"seed"}, 123 }; args::ValueFlag param_video { param_group, "path", "a video file where information is embed", {"path"}, }; args::ValueFlag param_utime { param_group, "int>0", "a duration (milliseconds) of features", {"utime"}, 10 }; args::ValueFlag> 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 probgen_false_positive { probgen_group, "0<=double<=1", "false positive ratio in feature probability generation", {"probgen-false-positive"}, 0 }; args::ValueFlag probgen_false_negative { probgen_group, "0<=double<=1", "false negative ratio in feature probability generation", {"probgen-false-negative"}, 0 }; args::ValueFlag 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 bytes; std::vector features; std::vector feature_probs; std::optional decoder; int main(int argc, char** argv) try { parser.ParseCLI(argc, argv); // read input 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((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(std::cin); } else { throw std::runtime_error {"invalid source format for features"}; } break; case kFeatureProbs: if (src_stdin) { feature_probs = ReadAll(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; return 0; } catch (const args::ParseError& e) { std::cerr << e.what() << std::endl; std::cerr << parser << std::endl; return 1; } catch (const args::ValidationError& e) { std::cerr << e.what() << std::endl; std::cerr << parser << std::endl; return 1; } catch (const std::runtime_error& e) { std::cerr << "runtime error: " << e.what() << std::endl; return 1; }