From ba2b742ef541b3e8f42e7eaa2dba3dc177d933c5 Mon Sep 17 00:00:00 2001 From: Martin Pulec Date: Thu, 18 Apr 2019 15:45:09 +0200 Subject: [PATCH] Decompress+lavd: fixes --- src/rtp/video_decoders.cpp | 150 ++++++++++++++++++++---------- src/video_codec.c | 4 + src/video_codec.h | 1 + src/video_decompress/libavcodec.c | 68 ++++++-------- 4 files changed, 137 insertions(+), 86 deletions(-) diff --git a/src/rtp/video_decoders.cpp b/src/rtp/video_decoders.cpp index bc9f85009..5f2f8be51 100644 --- a/src/rtp/video_decoders.cpp +++ b/src/rtp/video_decoders.cpp @@ -105,6 +105,7 @@ #include #include #include +#include #include #include @@ -114,6 +115,8 @@ #define MOD_NAME "[video dec.] " +#define FRAMEBUFFER_NOT_READY(decoder) (decoder->frame == NULL && decoder->out_codec != VIDEO_CODEC_END) + using namespace std; struct state_video_decoder; @@ -446,7 +449,7 @@ static void *fec_thread(void *args) { goto cleanup; } - if(!frame) { + if (FRAMEBUFFER_NOT_READY(decoder)) { goto cleanup; } @@ -891,6 +894,61 @@ void video_decoder_destroy(struct state_video_decoder *decoder) delete decoder; } +static vector> order_output_codecs(codec_t comp_int_fmt, codec_t *display_codecs, + int display_codecs_count) +{ + vector> ret; + set used; + // first add hw-accelerated codecs + for (int i = 0; i < display_codecs_count; ++i) { + if (codec_is_hw_accelerated(display_codecs[i])) { + ret.push_back({comp_int_fmt, display_codecs[i]}); + if (comp_int_fmt != VIDEO_CODEC_NONE) { + ret.push_back({VIDEO_CODEC_NONE, display_codecs[i]}); + } + used.insert(display_codecs[i]); + } + } + // then codecs matching exactly internal codec + for (int i = 0; i < display_codecs_count; ++i) { + if (used.find(display_codecs[i]) != used.end()) { + continue; + }; + if (display_codecs[i] == comp_int_fmt) { + ret.push_back({comp_int_fmt, display_codecs[i]}); + if (comp_int_fmt != VIDEO_CODEC_NONE) { + ret.push_back({VIDEO_CODEC_NONE, display_codecs[i]}); + } + used.insert(display_codecs[i]); + } + } + // then add also all other codecs + /// @todo + /// The rest should be ordered as well. Eg. when internal is + /// R12L, next should be perhaps R10k, RGBA, RGB and then YCbCr + /// codecs. + for (int i = 0; i < display_codecs_count; ++i) { + if (used.find(display_codecs[i]) != used.end()) { + continue; + }; + ret.push_back({comp_int_fmt, display_codecs[i]}); + if (comp_int_fmt != VIDEO_CODEC_NONE) { + ret.push_back({VIDEO_CODEC_NONE, display_codecs[i]}); + } + used.insert(display_codecs[i]); + } + + if (log_level >= LOG_LEVEL_VERBOSE) { + LOG(LOG_LEVEL_VERBOSE) << "Trying codecs:\n"; + for (auto it = ret.begin(); it != ret.end(); ++it) { + LOG(LOG_LEVEL_VERBOSE) << "\t" << get_codec_name((*it).second) << ", internal: " << get_codec_name((*it).first) << "\n"; + } + } + + return ret; +} + + /** * This function selects, according to given video description, appropriate * @@ -906,10 +964,8 @@ static codec_t choose_codec_and_decoder(struct state_video_decoder *decoder, str codec_t out_codec = VIDEO_CODEC_NONE; *decode_line = NULL; - size_t native; /* first check if the codec is natively supported */ - for(native = 0u; native < decoder->native_count; ++native) - { + for (size_t native = 0u; native < decoder->native_count; ++native) { out_codec = decoder->native_codecs[native]; if(desc.color_spec == out_codec) { if((out_codec == DXT1 || out_codec == DXT1_YUV || @@ -917,7 +973,7 @@ static codec_t choose_codec_and_decoder(struct state_video_decoder *decoder, str && decoder->video_mode != VIDEO_NORMAL) continue; /* it is a exception, see NOTES #1 */ - *decode_line = (decoder_t) memcpy; + *decode_line = static_cast([](unsigned char *dst, const unsigned char *src, int dst_len, int /* rshift */, int /* gshift */, int /* bshift */) { memcpy(dst, src, dst_len); }); decoder->decoder_type = LINE_DECODER; if(desc.color_spec == RGBA || /* another exception - we may change shifts */ @@ -930,8 +986,7 @@ static codec_t choose_codec_and_decoder(struct state_video_decoder *decoder, str } } /* otherwise if we have line decoder */ - for(native = 0; native < decoder->native_count; ++native) - { + for (size_t native = 0; native < decoder->native_count; ++native) { decoder_t decode; if ((decode = get_decoder_from_to(desc.color_spec, decoder->native_codecs[native], false)) != NULL) { *decode_line = decode; @@ -943,8 +998,7 @@ static codec_t choose_codec_and_decoder(struct state_video_decoder *decoder, str } } /* the same, but include also slow decoders */ - for(native = 0; native < decoder->native_count; ++native) - { + for (size_t native = 0; native < decoder->native_count; ++native) { decoder_t decode; if ((decode = get_decoder_from_to(desc.color_spec, decoder->native_codecs[native], true)) != NULL) { *decode_line = decode; @@ -963,6 +1017,7 @@ after_linedecoder_lookup: decoder->decompress_state = (struct state_decompress **) calloc(decoder->max_substreams, sizeof(struct state_decompress *)); + // try to probe video format if (comp_int_fmt == VIDEO_CODEC_NONE) { bool supports_autodetection = decompress_init_multi(desc.color_spec, VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, decoder->decompress_state, @@ -973,26 +1028,26 @@ after_linedecoder_lookup: } } - codec_t try_internal_codec[2] = { comp_int_fmt, VIDEO_CODEC_NONE }; + vector> formats_to_try; // (comp_int_fmt || VIDEO_CODEC_NONE), display_fmt + formats_to_try = order_output_codecs(comp_int_fmt, decoder->native_codecs, + decoder->native_count); - for (int i = 0; i < 2; i++) { - for (native = 0; native < decoder->native_count; ++native) { - out_codec = decoder->native_codecs[native]; - if (decompress_init_multi(desc.color_spec, try_internal_codec[i], - decoder->native_codecs[native], - decoder->decompress_state, - decoder->max_substreams)) { - int res = 0, ret; - size_t size = sizeof(res); - ret = decompress_get_property(decoder->decompress_state[0], - DECOMPRESS_PROPERTY_ACCEPTS_CORRUPTED_FRAME, - &res, - &size); - decoder->accepts_corrupted_frame = ret && res; + for (auto it = formats_to_try.begin(); it != formats_to_try.end(); ++it) { + out_codec = (*it).second; + if (decompress_init_multi(desc.color_spec, (*it).first, + (*it).second, + decoder->decompress_state, + decoder->max_substreams)) { + int res = 0, ret; + size_t size = sizeof(res); + ret = decompress_get_property(decoder->decompress_state[0], + DECOMPRESS_PROPERTY_ACCEPTS_CORRUPTED_FRAME, + &res, + &size); + decoder->accepts_corrupted_frame = ret && res; - decoder->decoder_type = EXTERNAL_DECODER; - goto after_decoder_lookup; - } + decoder->decoder_type = EXTERNAL_DECODER; + goto after_decoder_lookup; } } free(decoder->decompress_state); @@ -1003,7 +1058,7 @@ after_decoder_lookup: if(decoder->decoder_type == UNSET) { log_msg(LOG_LEVEL_ERROR, "Unable to find decoder for input codec \"%s\"!!!\n", get_codec_name(desc.color_spec)); log_msg(LOG_LEVEL_INFO, "Compression internal codec is \"%s\". Native codecs are:", get_codec_name(comp_int_fmt)); - for(native = 0; native < decoder->native_count; ++native) { + for (size_t native = 0; native < decoder->native_count; ++native) { log_msg(LOG_LEVEL_INFO, " %s", get_codec_name(decoder->native_codecs[native])); } log_msg(LOG_LEVEL_INFO, "\n"); @@ -1132,22 +1187,22 @@ static bool reconfigure_decoder(struct state_video_decoder *decoder, LOG(LOG_LEVEL_NOTICE) << MOD_NAME << "Sucessfully reconfigured display to " << display_desc << "\n"; decoder->display_desc = display_desc; - } - len = sizeof(display_requested_rgb_shift); - ret = display_get_property(decoder->display, DISPLAY_PROPERTY_RGB_SHIFT, - &display_requested_rgb_shift, &len); - if(!ret) { - debug_msg("Failed to get r,g,b shift property from video driver.\n"); - int rgb_shift[3] = {0, 8, 16}; - memcpy(&display_requested_rgb_shift, rgb_shift, sizeof(rgb_shift)); - } + len = sizeof(display_requested_rgb_shift); + ret = display_get_property(decoder->display, DISPLAY_PROPERTY_RGB_SHIFT, + &display_requested_rgb_shift, &len); + if(!ret) { + debug_msg("Failed to get r,g,b shift property from video driver.\n"); + int rgb_shift[3] = {0, 8, 16}; + memcpy(&display_requested_rgb_shift, rgb_shift, sizeof(rgb_shift)); + } - ret = display_get_property(decoder->display, DISPLAY_PROPERTY_BUF_PITCH, - &display_requested_pitch, &len); - if(!ret) { - debug_msg("Failed to get pitch from video driver.\n"); - display_requested_pitch = PITCH_DEFAULT; + ret = display_get_property(decoder->display, DISPLAY_PROPERTY_BUF_PITCH, + &display_requested_pitch, &len); + if(!ret) { + debug_msg("Failed to get pitch from video driver.\n"); + display_requested_pitch = PITCH_DEFAULT; + } } int linewidth; @@ -1255,7 +1310,9 @@ static bool reconfigure_decoder(struct state_video_decoder *decoder, send_message_to_receiver(decoder->mod.parent, (struct message *) msg); free_response(resp); - decoder->frame = display_get_frame(decoder->display); + if (out_codec != VIDEO_CODEC_END) { + decoder->frame = display_get_frame(decoder->display); + } return true; } @@ -1317,6 +1374,7 @@ static int reconfigure_if_needed(struct state_video_decoder *decoder, if (!ret) { log_msg(LOG_LEVEL_ERROR, "[video dec.] Reconfiguration failed!!!\n"); decoder->frame = NULL; + decoder->out_codec = VIDEO_CODEC_NONE; } #endif return TRUE; @@ -1341,8 +1399,6 @@ static int check_for_mode_change(struct state_video_decoder *decoder, #define ERROR_GOTO_CLEANUP ret = FALSE; goto cleanup; #define max(a, b) (((a) > (b))? (a): (b)) -#define FRAMEBUFFER_READY(decoder) (decoder->frame == NULL && decoder->out_codec != VIDEO_CODEC_END) - /** * @brief Decodes a participant buffer representing one video frame. * @param cdata PBUF buffer @@ -1547,7 +1603,7 @@ int decode_video_frame(struct coded_data *cdata, void *decoder_data, struct pbuf // hereafter, display framebuffer can be used, so we // check if we got it - if (FRAMEBUFFER_READY(decoder)) { + if (FRAMEBUFFER_NOT_READY(decoder)) { vf_free(frame); return FALSE; } @@ -1666,7 +1722,7 @@ next_packet: return FALSE; } - if (FRAMEBUFFER_READY(decoder) && (pt == PT_VIDEO || pt == PT_ENCRYPT_VIDEO)) { + if (FRAMEBUFFER_NOT_READY(decoder) && (pt == PT_VIDEO || pt == PT_ENCRYPT_VIDEO)) { ret = FALSE; goto cleanup; } diff --git a/src/video_codec.c b/src/video_codec.c index c85a5b65a..3216d58da 100644 --- a/src/video_codec.c +++ b/src/video_codec.c @@ -444,6 +444,10 @@ int codec_is_const_size(codec_t codec) } } +bool codec_is_hw_accelerated(codec_t codec) { + return codec == HW_VDPAU; +} + int get_halign(codec_t codec) { unsigned int i = (unsigned int) codec; diff --git a/src/video_codec.h b/src/video_codec.h index 956c1cad1..64a7b79a7 100644 --- a/src/video_codec.h +++ b/src/video_codec.h @@ -91,6 +91,7 @@ int vc_get_linesize(unsigned int width, codec_t codec) ATTRIBUTE(pure); int codec_is_a_rgb(codec_t codec) ATTRIBUTE(pure); bool codec_is_in_set(codec_t codec, codec_t *set) ATTRIBUTE(pure); int codec_is_const_size(codec_t codec) ATTRIBUTE(pure); +bool codec_is_hw_accelerated(codec_t codec) ATTRIBUTE(pure); void vc_deinterlace(unsigned char *src, long src_linesize, int lines); void vc_deinterlace_ex(unsigned char *src, size_t src_linesize, unsigned char *dst, size_t dst_pitch, size_t lines); diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index cb61db2d5..debfe2b46 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -34,12 +34,6 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/** - * @todo - * remove not_implemented_conv - this should be handled differently now - * (through internal codecs) - */ - #ifdef HAVE_CONFIG_H #include "config.h" @@ -1101,17 +1095,6 @@ static void yuv444p10le_to_rgb24(char *dst_buffer, AVFrame *in_frame, free(tmp); } -static void not_implemented_conv(char *dst_buffer, AVFrame *in_frame, - int width, int height, int pitch) -{ - UNUSED(dst_buffer); - UNUSED(in_frame); - UNUSED(width); - UNUSED(height); - UNUSED(pitch); - log_msg(LOG_LEVEL_ERROR, "Selected conversion is not implemented!\n"); -} - #ifdef HWACC_VDPAU static void av_vdpau_to_ug_vdpau(char *dst_buffer, AVFrame *in_frame, int width, int height, int pitch) @@ -1174,12 +1157,10 @@ static const struct { {AV_PIX_FMT_YUVJ444P, UYVY, yuv444p_to_yuv422, true}, {AV_PIX_FMT_YUVJ444P, RGB, yuv444p_to_rgb24, false}, // 8-bit YUV (NV12) - {AV_PIX_FMT_NV12, v210, not_implemented_conv, false}, {AV_PIX_FMT_NV12, UYVY, nv12_to_yuv422, true}, {AV_PIX_FMT_NV12, RGB, nv12_to_rgb24, false}, // RGB {AV_PIX_FMT_GBRP, RGB, gbrp_to_rgb, true}, - {AV_PIX_FMT_RGB24, v210, not_implemented_conv, false}, {AV_PIX_FMT_RGB24, UYVY, rgb24_to_uyvy, false}, {AV_PIX_FMT_RGB24, RGB, rgb24_to_rgb, true}, #ifdef HWACC_VDPAU @@ -1493,45 +1474,54 @@ static void libavcodec_decompress_done(void *state) free(s); } +static const codec_t supp_codecs[] = { H264, H265, JPEG, MJPG, J2K, VP8, VP9, + HFYU, FFV1, AV1 }; +static const struct decode_from_to dec_template[] = { + { VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, 500 }, // for probe + { VIDEO_CODEC_NONE, RGB, RGB, 500 }, + //{ VIDEO_CODEC_NONE, UYVY, RGB, 500 }, // there are conversions but don't enable now + { VIDEO_CODEC_NONE, UYVY, UYVY, 500 }, + { VIDEO_CODEC_NONE, v210, v210, 500 }, + { VIDEO_CODEC_NONE, v210, UYVY, 500 }, + { VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, RGB, 900 }, // provide also generic decoders + { VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, UYVY, 900 } // ditto +}; +#define SUPP_CODECS_CNT (sizeof supp_codecs / sizeof supp_codecs[0]) +#define DEC_TEMPLATE_CNT (sizeof dec_template / sizeof dec_template[0]) ADD_TO_PARAM(lavd_use_10bit, "lavd-use-10bit", "* lavd-use-10bit\n" " Indicates that we are using decoding to v210 (currently only H.264/HEVC).\n" " If so, it can be decompressed to v210. With this flag, v210 (10-bit YUV)\n" " will be announced as a supported codec.\n"); static const struct decode_from_to *libavcodec_decompress_get_decoders() { - const struct decode_from_to dec_static[] = { - { H264, VIDEO_CODEC_NONE, UYVY, 500 }, - { H265, VIDEO_CODEC_NONE, UYVY, 500 }, - { JPEG, VIDEO_CODEC_NONE, UYVY, 600 }, - { MJPG, VIDEO_CODEC_NONE, UYVY, 500 }, - { J2K, VIDEO_CODEC_NONE, RGB, 500 }, - { VP8, VIDEO_CODEC_NONE, UYVY, 500 }, - { VP9, VIDEO_CODEC_NONE, UYVY, 500 }, - { HFYU, VIDEO_CODEC_NONE, UYVY, 500 }, - { FFV1, VIDEO_CODEC_NONE, UYVY, 500 }, - { AV1, VIDEO_CODEC_NONE, UYVY, 500 }, - - { H264, VIDEO_CODEC_NONE, VIDEO_CODEC_NONE, 500 }, - { H264, RGB, RGB, 500 }, - }; static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - static struct decode_from_to ret[sizeof dec_static / sizeof dec_static[0] + 1 /* terminating zero */ + 10 /* place for additional decoders, see below */]; + static struct decode_from_to ret[SUPP_CODECS_CNT * DEC_TEMPLATE_CNT + 1 /* terminating zero */ + 10 /* place for additional decoders, see below */]; pthread_mutex_lock(&lock); // prevent concurent initialization if (ret[0].from == VIDEO_CODEC_NONE) { // not yet initialized - memcpy(ret, dec_static, sizeof dec_static); + int ret_idx = 0; + for (size_t t = 0; t < DEC_TEMPLATE_CNT; ++t) { + for (size_t c = 0; c < SUPP_CODECS_CNT; ++c) { + ret[ret_idx++] = (struct decode_from_to){supp_codecs[c], + dec_template[t].internal, dec_template[t].to, + dec_template[t].priority}; + } + } + + // add also decoder from H.264/HEVC to v210 if user explicitly indicated to do so if (get_commandline_param("lavd-use-10bit")) { - ret[sizeof dec_static / sizeof dec_static[0]] = + ret[ret_idx++] = (struct decode_from_to) {H264, VIDEO_CODEC_NONE, v210, 400}; - ret[sizeof dec_static / sizeof dec_static[0] + 1] = + ret[ret_idx++] = (struct decode_from_to) {H265, VIDEO_CODEC_NONE, v210, 400}; } if (get_commandline_param("use-hw-accel")) { - ret[sizeof dec_static / sizeof dec_static[0]] = + ret[ret_idx++] = (struct decode_from_to) {H264, VIDEO_CODEC_NONE, HW_VDPAU, 200}; } + assert(ret_idx < sizeof ret / sizeof ret[0]); // there needs to be at least one zero row } pthread_mutex_unlock(&lock); // prevent concurent initialization