From 5197a11c85c774ea36c80f76a818f3aa4deb5045 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Fri, 22 Jul 2016 13:08:59 +0200 Subject: [PATCH] Video postprocess: moved from decoder to display This allows postprocess replacement without reconfiguring the whole decoder (under smoe circumstances). --- src/control_socket.cpp | 8 +- src/hd-rum-translator/hd-rum-decompress.cpp | 4 +- src/main.cpp | 4 +- src/rtp/video_decoders.cpp | 3 +- src/video_capture/ug_input.cpp | 2 +- src/video_display.c | 221 +++++++++++++++++++- src/video_display.h | 8 +- src/video_display/aggregate.c | 4 +- src/video_display/pipe.cpp | 2 +- src/video_display/proxy.cpp | 6 +- src/video_rxtx/sage.cpp | 4 +- 11 files changed, 236 insertions(+), 30 deletions(-) diff --git a/src/control_socket.cpp b/src/control_socket.cpp index fe71eea63..ff50abe16 100644 --- a/src/control_socket.cpp +++ b/src/control_socket.cpp @@ -509,10 +509,10 @@ static int process_msg(struct control_state *s, fd_t client_fd, char *message, s msg->type = RECEIVER_MSG_MUTE; resp = send_message(s->root_module, path, (struct message *) msg); } else if (prefix_matches(message, "postprocess ")) { - strncpy(path, "receiver", sizeof path); - struct msg_receiver *msg = (struct msg_receiver *) new_message(sizeof(struct msg_receiver)); - msg->type = RECEIVER_MSG_POSTPROCESS; - strncpy(msg->postprocess_cfg, suffix(message, "postprocess "), sizeof msg->postprocess_cfg - 1); + strncpy(path, "display", sizeof path); + struct msg_universal *msg = (struct msg_universal *) new_message(sizeof(struct msg_universal)); + strncpy(msg->text, message, sizeof msg->text - 1); + msg->text[sizeof msg->text - 1] = '\0'; resp = send_message(s->root_module, path, (struct message *) msg); } else if(strcasecmp(message, "bye") == 0) { ret = CONTROL_CLOSE_HANDLE; diff --git a/src/hd-rum-translator/hd-rum-decompress.cpp b/src/hd-rum-translator/hd-rum-decompress.cpp index 314c73e27..b03760922 100644 --- a/src/hd-rum-translator/hd-rum-decompress.cpp +++ b/src/hd-rum-translator/hd-rum-decompress.cpp @@ -197,11 +197,11 @@ void *hd_rum_decompress_init(struct module *parent, bool blend, const char *capt if (blend) { char cfg[128] = ""; snprintf(cfg, sizeof cfg, "pipe:%p", s); - assert (initialize_video_display(parent, "proxy", cfg, 0, &s->display) == 0); + assert (initialize_video_display(parent, "proxy", cfg, 0, NULL, &s->display) == 0); } else { char cfg[2 + sizeof(void *) * 2 + 1] = ""; snprintf(cfg, sizeof cfg, "%p", s); - assert (initialize_video_display(parent, "pipe", cfg, 0, &s->display) == 0); + assert (initialize_video_display(parent, "pipe", cfg, 0, NULL, &s->display) == 0); } map params; diff --git a/src/main.cpp b/src/main.cpp index aac98d00d..e54f8dbbe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1030,7 +1030,7 @@ int main(int argc, char *argv[]) // Display initialization should be prior to modules that may use graphic card (eg. GLSL) in order // to initalize shared resource (X display) first ret = - initialize_video_display(&uv.root_module, requested_display, display_cfg, display_flags, &uv.display_device); + initialize_video_display(&uv.root_module, requested_display, display_cfg, display_flags, postprocess, &uv.display_device); if (ret < 0) { printf("Unable to open display device: %s\n", requested_display); @@ -1132,7 +1132,7 @@ int main(int argc, char *argv[]) params["video_delay"].ptr = (void *) &video_offset; // UltraGrid RTP - params["postprocess"].ptr = (void *) postprocess; + params["postprocess"].ptr = (void *) NULL; params["decoder_mode"].l = (long) decoder_mode; params["display_device"].ptr = uv.display_device; diff --git a/src/rtp/video_decoders.cpp b/src/rtp/video_decoders.cpp index cabefcee2..9ab7a457a 100644 --- a/src/rtp/video_decoders.cpp +++ b/src/rtp/video_decoders.cpp @@ -1170,7 +1170,7 @@ static bool reconfigure_decoder(struct state_video_decoder *decoder, { int ret; /* reconfigure VO and give it opportunity to pass us pitch */ - ret = display_reconfigure(decoder->display, display_desc); + ret = display_reconfigure(decoder->display, display_desc, decoder->video_mode); if(!ret) { log_msg(LOG_LEVEL_ERROR, "[decoder] Unable to reconfigure display.\n"); return false; @@ -1215,6 +1215,7 @@ static bool reconfigure_decoder(struct state_video_decoder *decoder, decoder->pitch = vc_get_linesize(linewidth, out_codec); } + if(decoder->display_requested_pitch == PITCH_DEFAULT) { decoder->display_pitch = vc_get_linesize(display_desc.width, out_codec); } else { diff --git a/src/video_capture/ug_input.cpp b/src/video_capture/ug_input.cpp index 810a5253d..85fe4c3f9 100644 --- a/src/video_capture/ug_input.cpp +++ b/src/video_capture/ug_input.cpp @@ -102,7 +102,7 @@ static int vidcap_ug_input_init(const struct vidcap_params *cap_params, void **s char cfg[128] = ""; snprintf(cfg, sizeof cfg, "pipe:%p", s); - assert (initialize_video_display(vidcap_params_get_parent(cap_params), "proxy", cfg, 0, &s->display) == 0); + assert (initialize_video_display(vidcap_params_get_parent(cap_params), "proxy", cfg, 0, NULL, &s->display) == 0); map params; diff --git a/src/video_display.c b/src/video_display.c index f035b6004..72a960dfb 100644 --- a/src/video_display.c +++ b/src/video_display.c @@ -52,6 +52,12 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ +/** + * @todo + * On-fly postprocess reconfiguration is not ok in general case. The pitch or + * supported codecs might have changed due to the reconfiguration, which is, + * however, not reflected by decoder which queried the data before. + */ #include "config.h" #include "config_unix.h" @@ -60,7 +66,9 @@ #include "lib_common.h" #include "module.h" #include "perf.h" +#include "video.h" #include "video_display.h" +#include "vo_postprocess.h" #define DISPLAY_MAGIC 0x01ba7ef1 @@ -70,6 +78,11 @@ struct display { uint32_t magic; ///< For debugging. Conatins @ref DISPLAY_MAGIC const struct video_display_info *funcs; void *state; ///< state of the created video capture driver + + struct vo_postprocess_state *postprocess; + int pp_output_frames_count, display_pitch; + struct video_desc saved_desc; + enum video_mode saved_mode; }; /**This variable represents a pseudostate and may be returned when initialization @@ -97,8 +110,13 @@ void list_video_display_devices() * @retval 1 if successfully shown help (no state returned) */ int initialize_video_display(struct module *parent, const char *requested_display, - const char *fmt, unsigned int flags, struct display **out) + const char *fmt, unsigned int flags, const char *postprocess, struct display **out) { + if (postprocess && strcmp(postprocess, "help") == 0) { + show_vo_postprocess_help(); + return 1; + } + const struct video_display_info *vdi = (const struct video_display_info *) load_library(requested_display, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION); @@ -124,6 +142,15 @@ int initialize_video_display(struct module *parent, const char *requested_displa free(d); return 1; } + + if (postprocess) { + d->postprocess = vo_postprocess_init(postprocess); + if (!d->postprocess) { + display_done(d); + return 1; + } + } + *out = d; return 0; } @@ -142,6 +169,7 @@ void display_done(struct display *d) assert(d->magic == DISPLAY_MAGIC); d->funcs->done(d->state); module_done(&d->mod); + vo_postprocess_done(d->postprocess); free(d); } @@ -163,6 +191,37 @@ void display_run(struct display *d) d->funcs->run(d->state); } +static struct response *process_message(struct display *d, struct msg_universal *msg) +{ + if (strncasecmp(msg->text, "postprocess ", strlen("postprocess ")) == 0) { + log_msg(LOG_LEVEL_WARNING, "On fly changing postprocessing is currently " + "only an experimental feature! Use with caution!\n"); + const char *text = msg->text + strlen("postprocess "); + + struct vo_postprocess_state *postprocess_old = d->postprocess; + + if (strcmp(text, "flush") != 0) { + d->postprocess = vo_postprocess_init(text); + if (!d->postprocess) { + d->postprocess = postprocess_old; + log_msg(LOG_LEVEL_ERROR, "Unable to create postprocess '%s'.\n", text); + return new_response(RESPONSE_BAD_REQUEST, NULL); + } + } else { + d->postprocess = NULL; + } + + vo_postprocess_done(postprocess_old); + + display_reconfigure(d, d->saved_desc, d->saved_mode); + + return new_response(RESPONSE_OK, NULL); + } else { + log_msg(LOG_LEVEL_ERROR, "Unknown command '%s'.\n", msg->text); + return new_response(RESPONSE_BAD_REQUEST, NULL); + } +} + /** * @brief Returns video framebuffer which will be written to. * @@ -174,9 +233,19 @@ void display_run(struct display *d) */ struct video_frame *display_get_frame(struct display *d) { + struct message *msg; + while((msg = check_message(&d->mod))) { + struct response *r = process_message(d, (struct msg_universal *) msg); + free_message(msg, r); + } + perf_record(UVP_GETFRAME, d); assert(d->magic == DISPLAY_MAGIC); - return d->funcs->getf(d->state); + if (d->postprocess) { + return vo_postprocess_getf(d->postprocess); + } else { + return d->funcs->getf(d->state); + } } /** @@ -186,13 +255,39 @@ struct video_frame *display_get_frame(struct display *d) * @param d display to be putted frame to * @param frame frame that has been obtained from display_get_frame() and has not yet been put. * Should not be NULL unless we want to quit display mainloop. - * @param nonblock specifies blocking behavior (@ref display_put_frame_flags) + * @param flags specifies blocking behavior (@ref display_put_frame_flags) + * @retval 0 if displayed succesfully + * @retval 1 if not displayed */ -int display_put_frame(struct display *d, struct video_frame *frame, int nonblock) +int display_put_frame(struct display *d, struct video_frame *frame, int flags) { perf_record(UVP_PUTFRAME, frame); assert(d->magic == DISPLAY_MAGIC); - return d->funcs->putf(d->state, frame, nonblock); + + if (!frame) { + return d->funcs->putf(d->state, frame, flags); + } + + if (d->postprocess) { + int display_ret = 0; + for (int i = 0; i < d->pp_output_frames_count; ++i) { + struct video_frame *display_frame = d->funcs->getf(d->state); + int ret = vo_postprocess(d->postprocess, + frame, + display_frame, + d->display_pitch); + frame = NULL; + if (!ret) { + d->funcs->putf(d->state, display_frame, PUTF_DISCARD); + return 1; + } + + display_ret = d->funcs->putf(d->state, display_frame, flags); + } + return display_ret; + } else { + return d->funcs->putf(d->state, frame, flags); + } } /** @@ -207,10 +302,77 @@ int display_put_frame(struct display *d, struct video_frame *frame, int nonblock * @retval TRUE if reconfiguration succeeded * @retval FALSE if reconfiguration failed */ -int display_reconfigure(struct display *d, struct video_desc desc) +int display_reconfigure(struct display *d, struct video_desc desc, enum video_mode video_mode) { assert(d->magic == DISPLAY_MAGIC); - return d->funcs->reconfigure_video(d->state, desc); + + d->saved_desc = desc; + d->saved_mode = video_mode; + + if (d->postprocess) { + bool pp_does_change_tiling_mode = false; + size_t len = sizeof(pp_does_change_tiling_mode); + if (vo_postprocess_get_property(d->postprocess, VO_PP_DOES_CHANGE_TILING_MODE, + &pp_does_change_tiling_mode, &len)) { + if(len == 0) { + // just for sake of completness since it shouldn't be a case + log_msg(LOG_LEVEL_WARNING, "Warning: unable to get pp tiling mode!\n"); + } + } + struct video_desc pp_desc = desc; + + if (!pp_does_change_tiling_mode) { + pp_desc.width *= get_video_mode_tiles_x(video_mode); + pp_desc.height *= get_video_mode_tiles_y(video_mode); + pp_desc.tile_count = 1; + } + if (!vo_postprocess_reconfigure(d->postprocess, pp_desc)) { + log_msg(LOG_LEVEL_ERROR, "[video dec.] Unable to reconfigure video " + "postprocess.\n"); + return false; + } + struct video_desc display_desc; + int render_mode; // WTF ? + vo_postprocess_get_out_desc(d->postprocess, &display_desc, &render_mode, &d->pp_output_frames_count); + int rc = d->funcs->reconfigure_video(d->state, display_desc); + len = sizeof d->display_pitch; + d->display_pitch = PITCH_DEFAULT; + d->funcs->get_property(d->state, DISPLAY_PROPERTY_BUF_PITCH, + &d->display_pitch, &len); + if (d->display_pitch == PITCH_DEFAULT) { + d->display_pitch = vc_get_linesize(display_desc.width, display_desc.color_spec); + } + + return rc; + } else { + return d->funcs->reconfigure_video(d->state, desc); + } +} + +static void restrict_returned_codecs(codec_t *display_codecs, + size_t *display_codecs_count, codec_t *pp_codecs, + int pp_codecs_count) +{ + int i; + + for (i = 0; i < (int) *display_codecs_count; ++i) { + int j; + + int found = FALSE; + + for (j = 0; j < pp_codecs_count; ++j) { + if(display_codecs[i] == pp_codecs[j]) { + found = TRUE; + } + } + + if(!found) { + memmove(&display_codecs[i], (const void *) &display_codecs[i + 1], + sizeof(codec_t) * (*display_codecs_count - i - 1)); + --*display_codecs_count; + --i; + } + } } /** @@ -226,7 +388,50 @@ int display_reconfigure(struct display *d, struct video_desc desc) int display_get_property(struct display *d, int property, void *val, size_t *len) { assert(d->magic == DISPLAY_MAGIC); - return d->funcs->get_property(d->state, property, val, len); + if (d->postprocess) { + switch (property) { + case DISPLAY_PROPERTY_BUF_PITCH: + *(int *) val = PITCH_DEFAULT; + *len = sizeof(int); + return TRUE; + case DISPLAY_PROPERTY_CODECS: + { + codec_t display_codecs[20], pp_codecs[20]; + size_t display_codecs_count, pp_codecs_count; + size_t nlen; + bool ret; + nlen = sizeof display_codecs; + ret = d->funcs->get_property(d->state, DISPLAY_PROPERTY_CODECS, display_codecs, &nlen); + if (!ret) return FALSE; + display_codecs_count = nlen / sizeof(codec_t); + nlen = sizeof pp_codecs; + ret = vo_postprocess_get_property(d->postprocess, VO_PP_PROPERTY_CODECS, pp_codecs, &nlen); + if (ret) { + if (nlen == 0) { // problem detected + log_msg(LOG_LEVEL_ERROR, "[Decoder] Unable to get supported codecs.\n"); + return FALSE; + + } + pp_codecs_count = nlen / sizeof(codec_t); + restrict_returned_codecs(display_codecs, &display_codecs_count, + pp_codecs, pp_codecs_count); + } + nlen = display_codecs_count * sizeof(codec_t); + if (nlen <= *len) { + *len = nlen; + memcpy(val, display_codecs, nlen); + return TRUE; + } else { + return FALSE; + } + } + break; + default: + return d->funcs->get_property(d->state, property, val, len); + } + } else { + return d->funcs->get_property(d->state, property, val, len); + } } /** diff --git a/src/video_display.h b/src/video_display.h index 99ebd798c..a4e8117d8 100644 --- a/src/video_display.h +++ b/src/video_display.h @@ -161,20 +161,20 @@ extern int display_init_noerr; void list_video_display_devices(void); int initialize_video_display(struct module *parent, const char *requested_display, const char *fmt, unsigned int flags, - struct display **out); + const char *postprocess, struct display **out); void display_run(struct display *d); void display_done(struct display *d); struct video_frame *display_get_frame(struct display *d); -/** @brief putf blocking behavior control */ +/** @brief putf flags */ enum display_put_frame_flags { PUTF_BLOCKING = 0, ///< Block until frame can be displayed. PUTF_NONBLOCK = 1, ///< Do not block. PUTF_DISCARD = 2, }; -int display_put_frame(struct display *d, struct video_frame *frame, int nonblock); -int display_reconfigure(struct display *d, struct video_desc desc); +int display_put_frame(struct display *d, struct video_frame *frame, int flags); +int display_reconfigure(struct display *d, struct video_desc desc, enum video_mode mode); int display_get_property(struct display *d, int property, void *val, size_t *len); /** * @defgroup display_audio Audio diff --git a/src/video_display/aggregate.c b/src/video_display/aggregate.c index bf153b46b..d1587a5ef 100644 --- a/src/video_display/aggregate.c +++ b/src/video_display/aggregate.c @@ -152,7 +152,7 @@ static void *display_aggregate_init(struct module *parent, const char *fmt, unsi } int ret = initialize_video_display(parent, device, - device_cfg, dev_flags, &s->devices[i]); + device_cfg, dev_flags, NULL, &s->devices[i]); if(ret != 0) { fprintf(stderr, "[aggregate] Unable to initialize device %d (%s:%s).\n", i, device, device_cfg); free(config); @@ -252,7 +252,7 @@ static int display_aggregate_reconfigure(void *state, struct video_desc desc) desc.tile_count = 1; for(i = 0; i < s->devices_cnt; ++i) { - ret = display_reconfigure(s->devices[i], desc); + ret = display_reconfigure(s->devices[i], desc, VIDEO_NORMAL); if(!ret) break; } diff --git a/src/video_display/pipe.cpp b/src/video_display/pipe.cpp index 3e7602f17..33548db18 100644 --- a/src/video_display/pipe.cpp +++ b/src/video_display/pipe.cpp @@ -59,7 +59,7 @@ static struct display *display_pipe_fork(void *state) snprintf(fmt, sizeof fmt, "%p", s->delegate); int rc = initialize_video_display(s->parent, - "pipe", fmt, 0, &out); + "pipe", fmt, 0, NULL, &out); if (rc == 0) return out; else return NULL; } diff --git a/src/video_display/proxy.cpp b/src/video_display/proxy.cpp index 1f55799e1..b6e5ecdb6 100644 --- a/src/video_display/proxy.cpp +++ b/src/video_display/proxy.cpp @@ -108,7 +108,7 @@ static struct display *display_proxy_fork(void *state) snprintf(fmt, sizeof fmt, "%p", state); int rc = initialize_video_display(s->parent, - "proxy", fmt, 0, &out); + "proxy", fmt, 0, NULL, &out); if (rc == 0) return out; else return NULL; return out; @@ -140,7 +140,7 @@ static void *display_proxy_init(struct module *parent, const char *fmt, unsigned } } s->common = shared_ptr(new state_proxy_common()); - assert (initialize_video_display(parent, requested_display, cfg, flags, &s->common->real_display) == 0); + assert (initialize_video_display(parent, requested_display, cfg, flags, NULL, &s->common->real_display) == 0); free(fmt_copy); int ret = pthread_create(&s->common->thread_id, NULL, (void *(*)(void *)) display_run, @@ -157,7 +157,7 @@ static void check_reconf(struct state_proxy_common *s, struct video_desc desc) if (!video_desc_eq(desc, s->display_desc)) { s->display_desc = desc; fprintf(stderr, "RECONFIGURED\n"); - display_reconfigure(s->real_display, s->display_desc); + display_reconfigure(s->real_display, s->display_desc, VIDEO_NORMAL); } } diff --git a/src/video_rxtx/sage.cpp b/src/video_rxtx/sage.cpp index d700fc390..3f6809df0 100644 --- a/src/video_rxtx/sage.cpp +++ b/src/video_rxtx/sage.cpp @@ -66,7 +66,7 @@ sage_video_rxtx::sage_video_rxtx(map const ¶ms) : oss << "fs=" << static_cast(params.at("receiver").ptr); oss << ":tx"; // indicates that we are in tx mode int ret = initialize_video_display(&m_sender_mod, "sage", - oss.str().c_str(), 0, &m_sage_tx_device); + oss.str().c_str(), 0, NULL, &m_sage_tx_device); if(ret != 0) { throw string("Unable to initialize SAGE TX."); } @@ -81,7 +81,7 @@ void sage_video_rxtx::send_frame(shared_ptr tx_frame) if(!video_desc_eq(m_saved_video_desc, video_desc_from_frame(tx_frame.get()))) { display_reconfigure(m_sage_tx_device, - video_desc_from_frame(tx_frame.get())); + video_desc_from_frame(tx_frame.get()), VIDEO_NORMAL); m_saved_video_desc = video_desc_from_frame(tx_frame.get()); } struct video_frame *frame =