diff --git a/src/rtp/decoders.c b/src/rtp/decoders.c index 9c6155e15..7ddee4031 100644 --- a/src/rtp/decoders.c +++ b/src/rtp/decoders.c @@ -70,6 +70,9 @@ static void restrict_returned_codecs(codec_t *display_codecs, static void decoder_set_video_mode(struct state_decoder *decoder, unsigned int video_mode); static int check_for_mode_change(struct state_decoder *decoder, uint32_t *hdr, struct video_frame **frame, struct vcodec_state *pbuf_data); +static int find_best_decompress(codec_t in_codec, codec_t out_codec, + int prio_min, int prio_max, uint32_t *magic); +static bool try_initialize_decompress(struct state_decoder * decoder, uint32_t magic); enum decoder_type_t { UNSET, @@ -329,6 +332,73 @@ void decoder_destroy(struct state_decoder *decoder) free(decoder); } +/** + * Attemps to initialize decompress of given magic + * + * @param decoder decoder state + * @param magic magic of the requested decompressor + * @return flat if initialization succeeded + */ +static bool try_initialize_decompress(struct state_decoder * decoder, uint32_t magic) { + decoder->ext_decoder = decompress_init(magic); + + if(!decoder->ext_decoder) { + debug_msg("Decompressor with magic %x was not found.\n"); + return false; + } + + int res = 0, ret; + size_t size = sizeof(res); + ret = decompress_get_property(decoder->ext_decoder, + DECOMPRESS_PROPERTY_ACCEPTS_CORRUPTED_FRAME, + &res, + &size); + if(ret && res) { + decoder->accepts_corrupted_frame = TRUE; + } else { + decoder->accepts_corrupted_frame = FALSE; + } + + decoder->decoder_type = EXTERNAL_DECODER; + return true; +} + +/** + * @param[in] in_codec input codec + * @param[in] out_codec output codec + * @param[in] prio_min minimal priority that can be probed + * @param[in] prio_max maximal priority that can be probed + * @param[out] magic if decompressor was found here is stored its magic + * @retval -1 if no found + * @retval priority best decoder's priority + */ +static int find_best_decompress(codec_t in_codec, codec_t out_codec, + int prio_min, int prio_max, uint32_t *magic) { + int trans; + int best_priority = prio_max + 1; + // first pass - find the one with best priority (least) + for(trans = 0; trans < decoders_for_codec_count; + ++trans) { + if(in_codec == decoders_for_codec[trans].from && + out_codec == decoders_for_codec[trans].to) { + int priority = decoders_for_codec[trans].priority; + if(priority <= prio_max && + priority >= prio_min && + priority < best_priority) { + if(decompress_is_available( + decoders_for_codec[trans].decompress_index)) { + best_priority = priority; + *magic = decoders_for_codec[trans].decompress_index; + } + } + } + } + + if(best_priority == prio_max + 1) + return -1; + return best_priority; +} + static codec_t choose_codec_and_decoder(struct state_decoder * const decoder, struct video_desc desc, codec_t *in_codec, decoder_t *decode_line) { @@ -373,14 +443,27 @@ static codec_t choose_codec_and_decoder(struct state_decoder * const decoder, st out_codec = decoder->native_codecs[native]; if(out_codec == DVS8 || out_codec == Vuy2) out_codec = UYVY; - if(*in_codec == line_decoders[trans].from && - out_codec == line_decoders[trans].to) { - - *decode_line = line_decoders[trans].line_decoder; - - decoder->decoder_type = LINE_DECODER; - goto after_linedecoder_lookup; + + int prio_max = 1000; + int prio_min = 0; + int prio_cur; + uint32_t decompress_magic = 0u; + + while(1) { + prio_cur = find_best_decompress(*in_codec, out_codec, + prio_min, prio_max, &decompress_magic); + // if found, init decoder + if(prio_cur != -1) { + if(try_initialize_decompress(decoder, decompress_magic)) { + goto after_decoder_lookup; + // failed, try to find another one + prio_min = prio_cur + 1; + } else { + break; + } + } } + } } diff --git a/src/rtp/pbuf.c b/src/rtp/pbuf.c index 58c61cc38..7b1f85a56 100644 --- a/src/rtp/pbuf.c +++ b/src/rtp/pbuf.c @@ -352,7 +352,7 @@ pbuf_decode(struct pbuf *playout_buf, struct timeval curr_time, curr = playout_buf->frst; while (curr != NULL) { if (!curr->decoded -#ifndef WIN32 +#ifdef WIN32 && tv_gt(curr_time, curr->playout_time) #endif ) { diff --git a/src/video_decompress.c b/src/video_decompress.c index 93a329291..858771f9b 100644 --- a/src/video_decompress.c +++ b/src/video_decompress.c @@ -122,15 +122,16 @@ static int decompress_fill_symbols(decoder_table_t *device) struct decode_from_to decoders_for_codec[] = { - { DXT1, RGBA, RTDXT_MAGIC }, - { DXT1_YUV, RGBA, RTDXT_MAGIC }, - { DXT5, RGBA, RTDXT_MAGIC }, - { DXT1, UYVY, RTDXT_MAGIC }, - { DXT1_YUV, UYVY, RTDXT_MAGIC }, - { DXT5, UYVY, RTDXT_MAGIC }, - { JPEG, RGB, JPEG_MAGIC }, - { JPEG, UYVY, JPEG_MAGIC }, - { H264, UYVY, LIBAVCODEC_MAGIC }, + { DXT1, RGBA, RTDXT_MAGIC, 500}, + { DXT1_YUV, RGBA, RTDXT_MAGIC, 500 }, + { DXT5, RGBA, RTDXT_MAGIC, 500 }, + { DXT1, UYVY, RTDXT_MAGIC, 500 }, + { DXT1_YUV, UYVY, RTDXT_MAGIC, 500 }, + { DXT5, UYVY, RTDXT_MAGIC, 500 }, + { JPEG, RGB, JPEG_MAGIC, 500 }, + { JPEG, UYVY, JPEG_MAGIC, 500 }, + { H264, UYVY, LIBAVCODEC_MAGIC, 500 }, + { JPEG, UYVY, LIBAVCODEC_MAGIC, 600 }, { (codec_t) -1, (codec_t) -1, NULL_MAGIC } }; const int decoders_for_codec_count = (sizeof(decoders_for_codec) / sizeof(struct decode_from_to)); @@ -186,6 +187,18 @@ void initialize_video_decompress(void) } } +int decompress_is_available(unsigned int decoder_index) +{ + int i; + + for(i = 0; i < available_decoders_count; ++i) { + if(available_decoders[i]->magic == decoder_index) { + return TRUE; + } + } + return FALSE; +} + struct state_decompress *decompress_init(unsigned int decoder_index) { int i; diff --git a/src/video_decompress.h b/src/video_decompress.h index d36187e61..53b07f73b 100644 --- a/src/video_decompress.h +++ b/src/video_decompress.h @@ -88,14 +88,36 @@ struct decode_from_to { codec_t to; uint32_t decompress_index; + /* priority to select this decoder if there are multiple matches + * range [0..100], lower is better + */ + int priority; }; extern struct decode_from_to decoders_for_codec[]; extern const int decoders_for_codec_count; - +/** + * must be called before initalization of decoders + */ void initialize_video_decompress(void); -struct state_decompress *decompress_init(unsigned int decoder_index); +/** + * Checks wheather there is decompressor with given magic present and thus can + * be initialized with decompress_init + * + * @see decompress_init + * @retval TRUE if decoder is present and can be initialized + * @retval FALSE if decoder could not be initialized (not found) + */ +int decompress_is_available(unsigned int decoder_index); + +/** + * Initializes decompressor or the given magic + * + * @retval NULL if initialization failed + * @retval not-NULL state of new decompressor + */ +struct state_decompress *decompress_init(unsigned int magic); int decompress_reconfigure(struct state_decompress *, struct video_desc, int rshift, int gshift, int bshift, int pitch, codec_t out_codec); /** * @param frame_seq sequential number of frame diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index 561f1c7e8..b6d9bb4f5 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -55,6 +55,7 @@ #include #include +#include #include "debug.h" #include "video_decompress.h" @@ -72,7 +73,8 @@ struct state_libavcodec_decompress { int last_frame_seq; }; -static void to_yuv422(char *dst_buffer, AVFrame *in_frame); +static void yuv420p_to_yuv422(char *dst_buffer, AVFrame *in_frame); +static void yuvj422p_to_yuv422(char *dst_buffer, AVFrame *in_frame); static void deconfigure(struct state_libavcodec_decompress *s) { @@ -90,37 +92,53 @@ static bool configure_with(struct state_libavcodec_decompress *s, switch(desc.color_spec) { case H264: codec_id = CODEC_ID_H264; - /* find the video encoder */ + break; + case JPEG: + codec_id = CODEC_ID_MJPEG; + fprintf(stderr, "[lavd] Warning: JPEG decoder " + "will use full-scale YUV.\n"); break; default: - fprintf(stderr, "[Libavcodec] Unsupported codec!!!\n"); + fprintf(stderr, "[lavd] Unsupported codec!!!\n"); return false; } s->codec = avcodec_find_decoder(codec_id); if(s->codec == NULL) { - fprintf(stderr, "[Libavcodec] Unable to find codec.\n"); + fprintf(stderr, "[lavd] Unable to find codec.\n"); return false; } s->codec_ctx = avcodec_alloc_context3(s->codec); if(s->codec_ctx == NULL) { - fprintf(stderr, "[Libavcodec] Unable to allocate codec context.\n"); + fprintf(stderr, "[lavd] Unable to allocate codec context.\n"); return false; } // zero should mean count equal to the number of virtual cores - s->codec_ctx->thread_count = 0; - s->codec_ctx->thread_type = FF_THREAD_SLICE; + if(s->codec->capabilities & CODEC_CAP_SLICE_THREADS) { + s->codec_ctx->thread_count = 0; + s->codec_ctx->thread_type = FF_THREAD_SLICE; + } else { + fprintf(stderr, "[lavd] Warning: Codec doesn't support slice-based multithreading.\n"); + if(s->codec->capabilities & CODEC_CAP_FRAME_THREADS) { + s->codec_ctx->thread_count = 0; + s->codec_ctx->thread_type = FF_THREAD_FRAME; + } else { + fprintf(stderr, "[lavd] Warning: Codec doesn't support frame-based multithreading.\n"); + } + } + + s->codec_ctx->pix_fmt = PIX_FMT_YUV420P; if(avcodec_open2(s->codec_ctx, s->codec, NULL) < 0) { - fprintf(stderr, "[Libavcodec] Unable to open decoder.\n"); + fprintf(stderr, "[lavd] Unable to open decoder.\n"); return false; } s->frame = avcodec_alloc_frame(); if(!s->frame) { - fprintf(stderr, "[Libavcodec] Unable allocate frame.\n"); + fprintf(stderr, "[lavd] Unable allocate frame.\n"); return false; } @@ -175,7 +193,8 @@ int libavcodec_decompress_reconfigure(void *state, struct video_desc desc, return s->max_compressed_len; } -static void to_yuv422(char *dst_buffer, AVFrame *in_frame) + +static void yuv420p_to_yuv422(char *dst_buffer, AVFrame *in_frame) { for(int y = 0; y < (int) in_frame->height; ++y) { char *src = (char *) in_frame->data[0] + in_frame->linesize[0] * y; @@ -199,7 +218,28 @@ static void to_yuv422(char *dst_buffer, AVFrame *in_frame) } } -int libavcodec_decompress(void *state, unsigned char *dst, unsigned char *buffer, +static void yuvj422p_to_yuv422(char *dst_buffer, AVFrame *in_frame) +{ + for(int y = 0; y < (int) in_frame->height; ++y) { + char *src = (char *) in_frame->data[0] + in_frame->linesize[0] * y; + char *dst = (char *) dst_buffer + in_frame->width * y * 2; + for(int x = 0; x < in_frame->width; ++x) { + dst[x * 2 + 1] = src[x]; + } + } + + for(int y = 0; y < (int) in_frame->height; ++y) { + char *src_cb = (char *) in_frame->data[1] + in_frame->linesize[1] * y; + char *src_cr = (char *) in_frame->data[2] + in_frame->linesize[2] * y; + char *dst = dst_buffer + in_frame->width * y * 2; + for(int x = 0; x < in_frame->width / 2; ++x) { + dst[x * 4] = src_cb[x]; + dst[x * 4 + 2] = src_cr[x]; + } + } +} + +int libavcodec_decompress(void *state, unsigned char *dst, unsigned char *src, unsigned int src_len, int frame_seq) { struct state_libavcodec_decompress *s = (struct state_libavcodec_decompress *) state; @@ -207,12 +247,12 @@ int libavcodec_decompress(void *state, unsigned char *dst, unsigned char *buffer int res = FALSE; s->pkt.size = src_len; - s->pkt.data = buffer; + s->pkt.data = src; while (s->pkt.size > 0) { len = avcodec_decode_video2(s->codec_ctx, s->frame, &got_frame, &s->pkt); if(len < 0) { - fprintf(stderr, "[Libavcodec] Error while decoding frame.\n"); + fprintf(stderr, "[lavd] Error while decoding frame.\n"); return FALSE; } @@ -223,11 +263,32 @@ int libavcodec_decompress(void *state, unsigned char *dst, unsigned char *buffer (s->frame->pict_type == AV_PICTURE_TYPE_P && s->last_frame_seq == frame_seq - 1) ) { - to_yuv422((char *) dst, s->frame); + switch(s->frame->format) { +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + case AV_PIX_FMT_YUVJ422P: +#else + case PIX_FMT_YUVJ422P: +#endif + yuvj422p_to_yuv422((char *) dst, s->frame); + break; +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + case AV_PIX_FMT_YUV420P: +#else + case PIX_FMT_YUV420P: +#endif + yuv420p_to_yuv422((char *) dst, s->frame); + break; + default: + fprintf(stderr, "Unsupported pixel " + "format: %s\n", + av_get_pix_fmt_name( + s->frame->format)); + res = FALSE; + } s->last_frame_seq = frame_seq; res = TRUE; } else { - fprintf(stderr, "[Libavcodec] Missing appropriate I-frame " + fprintf(stderr, "[lavd] Missing appropriate I-frame " "(last valid %d, this %d).\n", s->last_frame_seq, frame_seq); res = FALSE;