From 00a4315ee406adc890a6ba84d03836fb937abb74 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 31 Aug 2023 10:20:23 +0200 Subject: [PATCH] file disp: use worker thread will be needed to process video and audio inside single thread --- src/video_display/file.c | 181 +++++++++++++++++++++++++++++---------- 1 file changed, 136 insertions(+), 45 deletions(-) diff --git a/src/video_display/file.c b/src/video_display/file.c index 4f6ce1b84..306aceae2 100644 --- a/src/video_display/file.c +++ b/src/video_display/file.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "debug.h" #include "lib_common.h" @@ -54,10 +55,11 @@ #define MOD_NAME "[File disp.] " struct output_stream { - AVStream *st; - AVCodecContext *enc; - AVPacket *pkt; - AVFrame *frame; + AVStream *st; + AVCodecContext *enc; + AVPacket *pkt; + long long int cur_pts; + struct video_frame *frame; }; struct state_file { @@ -65,8 +67,15 @@ struct state_file { struct output_stream video; struct video_desc video_desc; char filename[MAX_PATH_SIZE]; + pthread_t thread_id; + pthread_mutex_t lock; + pthread_cond_t cv; + bool initialized; + bool should_exit; }; +static void *worker(void *arg); + static void display_file_probe(struct device_info **available_cards, int *count, void (**deleter)(void *)) @@ -82,12 +91,16 @@ static void display_file_done(void *state) { struct state_file *s = state; - av_write_trailer(s->format_ctx); + pthread_join(s->thread_id, NULL); + pthread_mutex_destroy(&s->lock); + pthread_cond_destroy(&s->cv); + if (s->initialized) { + av_write_trailer(s->format_ctx); + } avcodec_free_context(&s->video.enc); if (!(s->format_ctx->oformat->flags & AVFMT_NOFILE)) { avio_closep(&s->format_ctx->pb); } - av_frame_free(&s->video.frame); av_packet_free(&s->video.pkt); free(s); } @@ -135,55 +148,70 @@ display_file_init(struct module *parent, const char *fmt, unsigned int flags) return NULL; } } - s->video.frame = av_frame_alloc(); s->video.pkt = av_packet_alloc(); + int ret = pthread_mutex_init(&s->lock, NULL); + ret |= pthread_cond_init(&s->cv, NULL); + ret |= pthread_create(&s->thread_id, NULL, worker, s); + assert(ret == 0); + return s; } +static void +delete_frame(struct video_frame *frame) +{ + AVFrame *avfrm = frame->callbacks.dispose_udata; + av_frame_free(&avfrm); +} + static struct video_frame * display_file_getf(void *state) { struct state_file *s = state; - struct video_frame *out = vf_alloc_desc(s->video_desc); - out->tiles[0].data = (char *) s->video.frame->data[0]; + AVFrame *frame = av_frame_alloc(); + + frame->format = get_ug_to_av_pixfmt(s->video_desc.color_spec); + frame->width = (int) s->video_desc.width; + frame->height = (int) s->video_desc.height; + frame->pts = s->video.cur_pts++; + int ret = av_frame_get_buffer(frame, 0); + if (ret < 0) { + error_msg(MOD_NAME "Could not allocate frame data: %s.\n", + av_err2str(ret)); + av_frame_free(&frame); + return NULL; + } + struct video_frame *out = vf_alloc_desc(s->video_desc); + out->tiles[0].data = (char *) frame->data[0]; + out->callbacks.dispose_udata = frame; + out->callbacks.data_deleter = delete_frame; return out; } static bool display_file_putf(void *state, struct video_frame *frame, long long timeout_ns) { - vf_free(frame); // not needed if (timeout_ns == PUTF_DISCARD) { return true; } struct state_file *s = state; - - int ret = avcodec_send_frame(s->video.enc, s->video.frame); - s->video.frame->pts += 1; - if (ret < 0) { - error_msg(MOD_NAME "avcodec_send_frame: %s\n", av_err2str(ret)); + pthread_mutex_lock(&s->lock); + if (frame == NULL) { + s->should_exit = true; + pthread_mutex_unlock(&s->lock); + pthread_cond_signal(&s->cv); + return true; + } + if (s->video.frame != NULL) { + log_msg(LOG_LEVEL_WARNING, MOD_NAME "Frame dropped!\n"); + vf_free(frame); + pthread_mutex_unlock(&s->lock); return false; } - while (ret >= 0) { - ret = avcodec_receive_packet(s->video.enc, s->video.pkt); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { - break; - } - if (ret < 0) { - error_msg(MOD_NAME "avcodec_receive_frame: %s\n", - av_err2str(ret)); - return false; - } - av_packet_rescale_ts(s->video.pkt, s->video.enc->time_base, - s->video.st->time_base); - s->video.pkt->stream_index = s->video.st->index; - ret = av_interleaved_write_frame(s->format_ctx, s->video.pkt); - if (ret < 0) { - error_msg(MOD_NAME "error writting packet: %s\n", - av_err2str(ret)); - } - } + s->video.frame = frame; + pthread_mutex_unlock(&s->lock); + pthread_cond_signal(&s->cv); return true; } @@ -214,7 +242,13 @@ display_file_reconfigure(void *state, struct video_desc desc) { struct state_file *s = state; - s->video_desc = desc; + s->video_desc = desc; + return true; +} + +static bool +reconfigure_video(struct state_file *s, struct video_desc desc) +{ s->video.st->time_base = (AVRational){ get_framerate_d(desc.fps), get_framerate_n(desc.fps) }; const AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_RAWVIDEO); @@ -236,16 +270,6 @@ display_file_reconfigure(void *state, struct video_desc desc) av_err2str(ret)); return false; } - s->video.frame->format = s->video.enc->pix_fmt; - s->video.frame->width = (int) desc.width; - s->video.frame->height = (int) desc.height; - s->video.frame->pts = 0; - ret = av_frame_get_buffer(s->video.frame, 0); - if (ret < 0) { - error_msg(MOD_NAME "Could not allocate frame data: %s.\n", - av_err2str(ret)); - return false; - } av_dump_format(s->format_ctx, 0, s->filename, 1); @@ -257,9 +281,76 @@ display_file_reconfigure(void *state, struct video_desc desc) return false; } + s->initialized = true; return true; } +static void +write_video_frame(struct state_file *s, AVFrame *frame) +{ + int ret = avcodec_send_frame(s->video.enc, frame); + if (ret < 0) { + error_msg(MOD_NAME "avcodec_send_frame: %s\n", av_err2str(ret)); + return; + } + while (ret >= 0) { + ret = avcodec_receive_packet(s->video.enc, s->video.pkt); + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } + if (ret < 0) { + error_msg(MOD_NAME "avcodec_receive_frame: %s\n", + av_err2str(ret)); + return; + } + av_packet_rescale_ts(s->video.pkt, s->video.enc->time_base, + s->video.st->time_base); + s->video.pkt->stream_index = s->video.st->index; + ret = av_interleaved_write_frame(s->format_ctx, s->video.pkt); + if (ret < 0) { + error_msg(MOD_NAME "error writting packet: %s\n", + av_err2str(ret)); + } + } +} + +static void * +worker(void *arg) +{ + struct state_file *s = arg; + + struct video_desc saved_vid_desc = { 0 }; + + while (!s->should_exit) { + struct video_frame *frame = NULL; + pthread_mutex_lock(&s->lock); + while (s->video.frame == NULL && !s->should_exit) { + pthread_cond_wait(&s->cv, &s->lock); + } + if (s->should_exit) { + break; + } + frame = s->video.frame; + s->video.frame = NULL; + pthread_mutex_unlock(&s->lock); + const struct video_desc frame_desc = + video_desc_from_frame(frame); + if (!video_desc_eq(saved_vid_desc, frame_desc)) { + if (!reconfigure_video(s, frame_desc)) { + exit_uv(1); + vf_free(frame); + continue; + } + saved_vid_desc = frame_desc; + } + write_video_frame(s, frame->callbacks.dispose_udata); + vf_free(frame); + } + + pthread_mutex_unlock(&s->lock); + return NULL; +} + static const void * display_file_info_get() {