# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# include <cassert>
# include "system/loggerd/video_writer.h"
# include "common/swaglog.h"
# include "common/util.h"
VideoWriter : : VideoWriter ( const char * path , const char * filename , bool remuxing , int width , int height , int fps , cereal : : EncodeIndex : : Type codec )
: remuxing ( remuxing ) {
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 ) {
bool raw = ( codec = = cereal : : EncodeIndex : : Type : : BIG_BOX_LOSSLESS ) ;
avformat_alloc_output_context2 ( & this - > ofmt_ctx , NULL , raw ? " matroska " : NULL , this - > vid_path . c_str ( ) ) ;
assert ( this - > ofmt_ctx ) ;
// set codec correctly. needed?
assert ( codec ! = cereal : : EncodeIndex : : Type : : FULL_H_E_V_C ) ;
const AVCodec * avcodec = avcodec_find_encoder ( raw ? AV_CODEC_ID_FFVHUFF : AV_CODEC_ID_H264 ) ;
assert ( avcodec ) ;
this - > codec_ctx = avcodec_alloc_context3 ( avcodec ) ;
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 ( codec = = cereal : : EncodeIndex : : Type : : BIG_BOX_LOSSLESS ) {
// without this, there's just noise
int err = avcodec_open2 ( this - > codec_ctx , avcodec , NULL ) ;
assert ( err > = 0 ) ;
}
this - > out_stream = avformat_new_stream ( this - > ofmt_ctx , raw ? avcodec : NULL ) ;
assert ( this - > out_stream ) ;
int err = avio_open ( & this - > ofmt_ctx - > pb , this - > vid_path . c_str ( ) , AVIO_FLAG_WRITE ) ;
assert ( err > = 0 ) ;
} 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 ( len > 0 ) {
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 len: %d ts: %lld " , len , timestamp ) ; }
av_packet_unref ( & pkt ) ;
}
}
}
VideoWriter : : ~ VideoWriter ( ) {
if ( this - > remuxing ) {
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 ( ) ) ;
}