loggerd: Separate writer from encoder (#24257)
* seperate writer from encoder
* video writer in release
* confirm loggerd on PC works, add YUV option to compressed_vipc
* make raw_logger use video_writer
* put this back to master
* close codec
* put this back
* use unique_ptr
Co-authored-by: Comma Device <device@comma.ai>
old-commit-hash: 8d24655787
taco
parent
5a55e1bb79
commit
b872fe92ab
9 changed files with 208 additions and 242 deletions
@ -0,0 +1,118 @@ |
|||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations" |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
#include <cstdlib> |
||||||
|
|
||||||
|
#include "selfdrive/loggerd/video_writer.h" |
||||||
|
#include "selfdrive/common/swaglog.h" |
||||||
|
#include "selfdrive/common/util.h" |
||||||
|
|
||||||
|
VideoWriter::VideoWriter(const char *path, const char *filename, bool remuxing, int width, int height, int fps, bool h265, bool raw) |
||||||
|
: remuxing(remuxing), raw(raw) { |
||||||
|
vid_path = util::string_format("%s/%s", path, filename); |
||||||
|
lock_path = util::string_format("%s/%s.lock", path, filename); |
||||||
|
|
||||||
|
int lock_fd = HANDLE_EINTR(open(lock_path.c_str(), O_RDWR | O_CREAT, 0664)); |
||||||
|
assert(lock_fd >= 0); |
||||||
|
close(lock_fd); |
||||||
|
|
||||||
|
LOGD("encoder_open %s remuxing:%d", this->vid_path.c_str(), this->remuxing); |
||||||
|
if (this->remuxing) { |
||||||
|
avformat_alloc_output_context2(&this->ofmt_ctx, NULL, raw ? "matroska" : NULL, this->vid_path.c_str()); |
||||||
|
assert(this->ofmt_ctx); |
||||||
|
|
||||||
|
// set codec correctly. needed?
|
||||||
|
av_register_all(); |
||||||
|
|
||||||
|
AVCodec *codec = NULL; |
||||||
|
assert(!h265); |
||||||
|
codec = avcodec_find_encoder(raw ? AV_CODEC_ID_FFVHUFF : AV_CODEC_ID_H264); |
||||||
|
assert(codec); |
||||||
|
|
||||||
|
this->codec_ctx = avcodec_alloc_context3(codec); |
||||||
|
assert(this->codec_ctx); |
||||||
|
this->codec_ctx->width = width; |
||||||
|
this->codec_ctx->height = height; |
||||||
|
this->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; |
||||||
|
this->codec_ctx->time_base = (AVRational){ 1, fps }; |
||||||
|
|
||||||
|
if (raw) { |
||||||
|
// since the codec is actually used, we open it
|
||||||
|
int err = avcodec_open2(this->codec_ctx, codec, NULL); |
||||||
|
assert(err >= 0); |
||||||
|
} |
||||||
|
|
||||||
|
this->out_stream = avformat_new_stream(this->ofmt_ctx, raw ? codec : NULL); |
||||||
|
assert(this->out_stream); |
||||||
|
|
||||||
|
int err = avio_open(&this->ofmt_ctx->pb, this->vid_path.c_str(), AVIO_FLAG_WRITE); |
||||||
|
assert(err >= 0); |
||||||
|
|
||||||
|
this->wrote_codec_config = false; |
||||||
|
} else { |
||||||
|
this->of = util::safe_fopen(this->vid_path.c_str(), "wb"); |
||||||
|
assert(this->of); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void VideoWriter::write(uint8_t *data, int len, long long timestamp, bool codecconfig, bool keyframe) { |
||||||
|
if (of && data) { |
||||||
|
size_t written = util::safe_fwrite(data, 1, len, of); |
||||||
|
if (written != len) { |
||||||
|
LOGE("failed to write file.errno=%d", errno); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (remuxing) { |
||||||
|
if (codecconfig) { |
||||||
|
if (data) { |
||||||
|
codec_ctx->extradata = (uint8_t*)av_mallocz(len + AV_INPUT_BUFFER_PADDING_SIZE); |
||||||
|
codec_ctx->extradata_size = len; |
||||||
|
memcpy(codec_ctx->extradata, data, len); |
||||||
|
} |
||||||
|
int err = avcodec_parameters_from_context(out_stream->codecpar, codec_ctx); |
||||||
|
assert(err >= 0); |
||||||
|
err = avformat_write_header(ofmt_ctx, NULL); |
||||||
|
assert(err >= 0); |
||||||
|
} else { |
||||||
|
// input timestamps are in microseconds
|
||||||
|
AVRational in_timebase = {1, 1000000}; |
||||||
|
|
||||||
|
AVPacket pkt; |
||||||
|
av_init_packet(&pkt); |
||||||
|
pkt.data = data; |
||||||
|
pkt.size = len; |
||||||
|
|
||||||
|
enum AVRounding rnd = static_cast<enum AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); |
||||||
|
pkt.pts = pkt.dts = av_rescale_q_rnd(timestamp, in_timebase, ofmt_ctx->streams[0]->time_base, rnd); |
||||||
|
pkt.duration = av_rescale_q(50*1000, in_timebase, ofmt_ctx->streams[0]->time_base); |
||||||
|
|
||||||
|
if (keyframe) { |
||||||
|
pkt.flags |= AV_PKT_FLAG_KEY; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: can use av_write_frame for non raw?
|
||||||
|
int err = av_interleaved_write_frame(ofmt_ctx, &pkt); |
||||||
|
if (err < 0) { LOGW("ts encoder write issue"); } |
||||||
|
|
||||||
|
av_free_packet(&pkt); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
VideoWriter::~VideoWriter() { |
||||||
|
if (this->remuxing) { |
||||||
|
if (this->raw) { avcodec_close(this->codec_ctx); } |
||||||
|
int err = av_write_trailer(this->ofmt_ctx); |
||||||
|
if (err != 0) LOGE("av_write_trailer failed %d", err); |
||||||
|
avcodec_free_context(&this->codec_ctx); |
||||||
|
err = avio_closep(&this->ofmt_ctx->pb); |
||||||
|
if (err != 0) LOGE("avio_closep failed %d", err); |
||||||
|
avformat_free_context(this->ofmt_ctx); |
||||||
|
} else { |
||||||
|
util::safe_fflush(this->of); |
||||||
|
fclose(this->of); |
||||||
|
this->of = nullptr; |
||||||
|
} |
||||||
|
unlink(this->lock_path.c_str()); |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#include <libavformat/avformat.h> |
||||||
|
} |
||||||
|
|
||||||
|
class VideoWriter { |
||||||
|
public: |
||||||
|
VideoWriter(const char *path, const char *filename, bool remuxing, int width, int height, int fps, bool h265, bool raw); |
||||||
|
void write(uint8_t *data, int len, long long timestamp, bool codecconfig, bool keyframe); |
||||||
|
~VideoWriter(); |
||||||
|
AVCodecContext *codec_ctx; |
||||||
|
private: |
||||||
|
std::string vid_path, lock_path; |
||||||
|
|
||||||
|
FILE *of = nullptr; |
||||||
|
|
||||||
|
AVFormatContext *ofmt_ctx; |
||||||
|
AVStream *out_stream; |
||||||
|
bool remuxing, raw; |
||||||
|
|
||||||
|
bool wrote_codec_config; |
||||||
|
}; |
Loading…
Reference in new issue