feat: capture only video stream

- Захватываем только видео поток с камер
This commit is contained in:
amazing-hash 2024-05-11 15:55:29 +04:00
parent 702e0ff63d
commit 17f1a1c04c
9 changed files with 67 additions and 102 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.21)
project(va VERSION 0.5.0)
project(va VERSION 0.6.0)
configure_file (version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h @ONLY)

View File

@ -1,43 +1,37 @@
#include "archive.h"
#include "../../utils.h"
#include <chrono>
#include "libswscale/swscale.h"
namespace va {
void Archive::send_pkt(AVPacket *pkt, AVFormatContext *ctx_in) {
if (!time_base_) {
auto idx = pkt->stream_index;
auto in_stream = ctx_in->streams[idx];
time_base_ = in_stream->time_base;
}
if (pkt->pts != AV_NOPTS_VALUE) {
current_pts_pkt_ = pkt->pts;
if (start_pts_pkt_ == AV_NOPTS_VALUE) {
start_pts_pkt_ = pkt->pts;
}
}
auto is_key = pkt->flags & AV_PKT_FLAG_KEY;
if (is_key && ctx_) {
auto tp = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(tp - time_point_).count() > duration_file_) {
ctx_ = nullptr;
ctx_.reset(nullptr);
BOOST_LOG_TRIVIAL(debug) << "file " << current_file_ << " recording has finished";
if (start_pts_pkt_ != AV_NOPTS_VALUE && current_pts_pkt_ != AV_NOPTS_VALUE && time_base_) {
auto time_base = time_base_.value();
size_t extension_pos{current_file_.rfind(".ts")};
auto tmp = std::string(current_file_.begin(), current_file_.end());
fs::path path{tmp.replace(extension_pos, 3, ".meta")};
std::ofstream ofs(path);
ofs << "START_DATA" << std::endl;
ofs << static_cast<double>((current_pts_pkt_ - start_pts_pkt_)) * time_base.num / time_base.den
<< std::endl;
ofs << codec_id() << std::endl;
ofs << "END_DATA";
} else {
// TODO: Предполагаю длительность файла равна duration_file_
size_t extension_pos{current_file_.rfind(".ts")};
auto tmp = std::string(current_file_.begin(), current_file_.end());
fs::path path{tmp.replace(extension_pos, 3, ".meta")};
std::ofstream ofs(path);
ofs << "START_DATA" << std::endl;
ofs << static_cast<double>(duration_file_) << std::endl;
ofs << codec_id() << std::endl;
ofs << "END_DATA";
}
if (pkt->pts != AV_NOPTS_VALUE) {
start_pts_pkt_ = pkt->pts;
}
auto time_base = time_base_.value();
size_t extension_pos{current_file_.rfind(".ts")};
auto tmp = std::string(current_file_.begin(), current_file_.end());
fs::path path{tmp.replace(extension_pos, 3, ".meta")};
std::ofstream ofs(path);
ofs << "START_DATA" << std::endl;
ofs << static_cast<double>((current_pts_pkt_ - start_pts_pkt_)) * time_base.num / time_base.den
<< std::endl;
ofs << codec_id() << std::endl;
ofs << "END_DATA";
start_pts_pkt_ = AV_NOPTS_VALUE;
}
}
if (!ctx_) {
@ -48,22 +42,18 @@ namespace va {
auto intermediate_path = std::format("{}/{}/", prefix_path_, utils::format_datetime_to_path(secs));
auto filename = std::format("{}/{}.ts", intermediate_path, secs);
fs::create_directories(intermediate_path);
ctx_ = va_avformat_alloc_output_context(filename.c_str(), params_list_);
ctx_ = va_avformat_alloc_output_context(filename.c_str(), video_params_);
current_file_ = filename;
}
auto idx = pkt->stream_index;
auto in_stream = ctx_in->streams[idx];
auto out_stream = ctx_->streams[idx];
if (!time_base_) {
time_base_ = in_stream->time_base;
if (ctx_) {
auto idx = pkt->stream_index;
auto in_stream = ctx_in->streams[idx];
auto out_stream = ctx_->streams[idx];
pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
pkt->pos = -1;
av_interleaved_write_frame(ctx_.get(), pkt);
}
if (start_pts_pkt_ == AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE) {
start_pts_pkt_ = pkt->pts;
}
pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
pkt->pos = -1;
av_interleaved_write_frame(ctx_.get(), pkt);
}
} // namespace va

View File

@ -16,35 +16,22 @@ namespace fs = std::filesystem;
namespace va {
class Archive final {
public:
Archive(std::string &prefix_path, int64_t duration, std::vector<const AVCodecParameters *> params_list,
ssize_t video_idx)
: prefix_path_(prefix_path), duration_file_(duration), params_list_(params_list), video_idx_(video_idx) {
Archive(std::string &prefix_path, int64_t duration, const AVCodecParameters *video_params)
: prefix_path_(prefix_path), duration_file_(duration), video_params_(video_params) {
}
~Archive() {
if (ctx_) {
av_write_trailer(ctx_.get());
BOOST_LOG_TRIVIAL(debug) << "file " << current_file_ << " recording has finished";
if (start_pts_pkt_ != AV_NOPTS_VALUE && current_pts_pkt_ != AV_NOPTS_VALUE && time_base_) {
auto time_base = time_base_.value();
size_t extention_pos{current_file_.find(".ts")};
auto tmp = std::string(current_file_.begin(), current_file_.end());
fs::path path{tmp.replace(extention_pos, 3, ".meta")};
std::ofstream ofs(path);
ofs << "START_DATA" << std::endl;
ofs << static_cast<double>((current_pts_pkt_ - start_pts_pkt_)) * time_base.num / time_base.den;
ofs << codec_id() << std::endl;
ofs << "END_DATA";
} else {
// TODO: Предполагаю длительность файла равна duration_file_
size_t extension_pos{current_file_.find(".ts")};
auto tmp = std::string(current_file_.begin(), current_file_.end());
fs::path path{tmp.replace(extension_pos, 3, ".meta")};
std::ofstream ofs(path);
ofs << "START_DATA" << std::endl;
ofs << static_cast<double>(duration_file_) << std::endl;
ofs << codec_id() << std::endl;
ofs << "END_DATA";
}
auto time_base = time_base_.value();
size_t extention_pos{current_file_.find(".ts")};
auto tmp = std::string(current_file_.begin(), current_file_.end());
fs::path path{tmp.replace(extention_pos, 3, ".meta")};
std::ofstream ofs(path);
ofs << "START_DATA" << std::endl;
ofs << static_cast<double>((current_pts_pkt_ - start_pts_pkt_)) * time_base.num / time_base.den;
ofs << codec_id() << std::endl;
ofs << "END_DATA";
}
}
Archive(const Archive &) = delete;
@ -54,10 +41,7 @@ namespace va {
void send_pkt(AVPacket *pkt, AVFormatContext *ctx_in);
const char *codec_id() {
if (video_idx_ == -1) {
return "Undefined";
}
return avcodec_get_name(params_list_[static_cast<size_t>(video_idx_)]->codec_id);
return avcodec_get_name(video_params_->codec_id);
}
private:
@ -68,8 +52,7 @@ namespace va {
int64_t start_pts_pkt_ = AV_NOPTS_VALUE;
int64_t current_pts_pkt_ = AV_NOPTS_VALUE;
std::optional<AVRational> time_base_;
std::vector<const AVCodecParameters *> params_list_;
ssize_t video_idx_;
const AVCodecParameters *video_params_;
std::unique_ptr<AVFormatContext, void (*)(AVFormatContext *ctx)> ctx_ = va_avformat_null_alloc_output_context();
};
} // namespace va

View File

@ -14,19 +14,17 @@ namespace va {
}
}
std::unique_ptr<AVFormatContext, void (*)(AVFormatContext *ctx)>
va_avformat_alloc_output_context(const char *filename, std::vector<const AVCodecParameters *> &params_list) {
va_avformat_alloc_output_context(const char *filename, const AVCodecParameters *video_params) {
AVFormatContext *ctx = nullptr;
if (avformat_alloc_output_context2(&ctx, nullptr, nullptr, filename) < 0) {
throw std::runtime_error("failed to alocate output AVFormatContext");
}
for (auto codec_params : params_list) {
auto out_stream = avformat_new_stream(ctx, nullptr);
if (!out_stream) {
throw std::runtime_error("failed allocating output stream");
}
if (avcodec_parameters_copy((*out_stream).codecpar, codec_params) < 0) {
throw std::runtime_error("failed to copy codec parameters");
}
auto out_stream = avformat_new_stream(ctx, nullptr);
if (!out_stream) {
throw std::runtime_error("failed allocating output stream");
}
if (avcodec_parameters_copy((*out_stream).codecpar, video_params) < 0) {
throw std::runtime_error("failed to copy codec parameters");
}
if (avio_open(&ctx->pb, filename, AVIO_FLAG_WRITE) < 0) {
auto err_msg = std::format("could not open output file {}", filename);

View File

@ -19,7 +19,7 @@ namespace va {
}
std::unique_ptr<AVFormatContext, void (*)(AVFormatContext *ctx)>
va_avformat_alloc_output_context(const char *filename, std::vector<const AVCodecParameters *> &params_list);
va_avformat_alloc_output_context(const char *filename, const AVCodecParameters *video_params);
inline std::unique_ptr<AVFormatContext, void (*)(AVFormatContext *ctx)> va_avformat_null_alloc_output_context() {
return std::unique_ptr<AVFormatContext, void (*)(AVFormatContext * ctx)>(nullptr, release_output_context);

View File

@ -63,19 +63,21 @@ namespace va {
sleep_and_test(stoken, 5);
continue;
}
ssize_t video_idx = -1;
auto params_list = stream_params(ctx.get(), source_.id(), video_idx);
if (params_list.empty()) {
auto err_msg = std::format("not found audio and video channels for stream ({})", source_.id());
int video_idx;
auto video_params = stream_video_params(ctx.get(), source_.id(), video_idx);
if (!video_params) {
auto err_msg = std::format("not found video channels for stream ({})", source_.id());
BOOST_LOG_TRIVIAL(error) << err_msg;
sleep_and_test(stoken, 5);
continue;
}
try {
Archive archive(prefix_archive_path, duration, params_list, video_idx);
Archive archive(prefix_archive_path, duration, video_params);
const auto pkt = va::va_avpacket_alloc();
while (!stoken.stop_requested() && av_read_frame(ctx.get(), pkt.get()) == 0) {
archive.send_pkt(pkt.get(), ctx.get());
if (pkt.get()->stream_index == video_idx) {
archive.send_pkt(pkt.get(), ctx.get());
}
update_time_point();
av_packet_unref(pkt.get());
}

View File

@ -3,25 +3,18 @@
#include <format>
namespace va {
std::vector<const AVCodecParameters *> stream_params(AVFormatContext *ctx, const std::string &id,
ssize_t &video_idx) {
std::vector<const AVCodecParameters *> params_list;
const AVCodecParameters *stream_video_params(AVFormatContext *ctx, const std::string &id, int &video_idx) {
for (size_t idx = 0; idx < ctx->nb_streams; ++idx) {
auto codec_params = ctx->streams[idx]->codecpar;
auto media_type = codec_params->codec_type;
if (media_type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
video_idx = static_cast<ssize_t>(idx);
video_idx = static_cast<int>(idx);
auto info_msg = std::format("video stream {}, width {}, height {}, codec {}", id, codec_params->width,
codec_params->height, avcodec_get_name(codec_params->codec_id));
BOOST_LOG_TRIVIAL(info) << info_msg;
params_list.push_back(codec_params);
}
if (media_type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
auto info_msg = std::format("audio stream {}, codec {}", id, avcodec_get_name(codec_params->codec_id));
BOOST_LOG_TRIVIAL(info) << info_msg;
params_list.push_back(codec_params);
return codec_params;
}
}
return params_list;
return nullptr;
}
} // namespace va

View File

@ -6,6 +6,5 @@ extern "C" {
}
namespace va {
std::vector<const AVCodecParameters *> stream_params(AVFormatContext *ctx, const std::string &id,
ssize_t &video_idx);
const AVCodecParameters *stream_video_params(AVFormatContext *ctx, const std::string &id, int &video_idx);
} // namespace va

View File

@ -3,7 +3,7 @@
#define PROJECT_NAME "va"
#define VERSION_MAJOR "0"
#define VERSION_MINOR "5"
#define VERSION_MINOR "6"
#define VERSION_PATCH "0"
#endif