diff --git a/src/video.h b/src/video.h index f2bab2f6d..604aafb68 100644 --- a/src/video.h +++ b/src/video.h @@ -65,7 +65,8 @@ typedef enum { DPX10, JPEG, RAW, - H264 + H264, + MJPG } codec_t; enum interlacing_t { diff --git a/src/video_codec.c b/src/video_codec.c index 04ce2ab10..949b235e8 100644 --- a/src/video_codec.c +++ b/src/video_codec.c @@ -79,7 +79,8 @@ const struct codec_info_t codec_info[] = { {DPX10, "DPX10", to_fourcc('D','P','1','0'), 1, 4.0, TRUE, FALSE}, {JPEG, "JPEG", to_fourcc('J','P','E','G'), 0, 0.0, FALSE, TRUE}, {RAW, "raw", to_fourcc('r','a','w','s'), 0, 1.0, FALSE, TRUE}, /* raw SDI */ - {H264, "H264", to_fourcc('A','V','V','1'), 0, 1.0, FALSE, TRUE}, + {H264, "H.264", to_fourcc('A','V','C','1'), 0, 1.0, FALSE, TRUE}, + {MJPG, "MJPEG", to_fourcc('M','J','P','G'), 0, 1.0, FALSE, TRUE}, {(codec_t) 0, NULL, 0, 0, 0.0, FALSE, FALSE} }; diff --git a/src/video_compress/libavcodec.c b/src/video_compress/libavcodec.c index d1b8556a7..5a8bd9fcc 100644 --- a/src/video_compress/libavcodec.c +++ b/src/video_compress/libavcodec.c @@ -63,7 +63,7 @@ #include "video.h" #include "video_codec.h" -#define DEFAULT_CODEC CODEC_ID_H264 +#define DEFAULT_CODEC H264 struct libav_video_compress { struct video_frame *out[2]; @@ -79,15 +79,29 @@ struct libav_video_compress { unsigned char *decoded; decoder_t decoder; + codec_t selected_codec_id; + int requested_bitrate; + bool configured; }; static void to_yuv420(AVFrame *out_frame, unsigned char *in_data); +static void usage(void); + +static void usage() { + printf("Libavcodec encoder usage:\n"); + printf("\t-c libavcodec[:codec=][:bitrate=]" + "[qscale=\n"); + printf("\t\t may be one of \"MJPEG\" " + "or \"H.264\" (default)\n"); + printf("\t\t specifies requested bitrate\n"); + printf("\t\t\t0 means codec default (same as when parameter omitted)\n"); +} void * libavcodec_compress_init(char * fmt) { - UNUSED(fmt); struct libav_video_compress *s; + char *item, *save_ptr = NULL; s = (struct libav_video_compress *) malloc(sizeof(struct libav_video_compress)); s->out[0] = s->out[1] = NULL; @@ -96,6 +110,57 @@ void * libavcodec_compress_init(char * fmt) s->codec = NULL; s->codec_ctx = NULL; s->in_frame = NULL; + s->selected_codec_id = DEFAULT_CODEC; + + s->requested_bitrate = -1; + + if(fmt) { + while((item = strtok_r(fmt, ":", &save_ptr)) != NULL) { + if(strncasecmp("help", item, strlen("help")) == 0) { + usage(); + return NULL; + } else if(strncasecmp("codec=", item, strlen("codec=")) == 0) { + char *codec = item + strlen("codec="); + int i; + for (i = 0; codec_info[i].name != NULL; i++) { + if (strcmp(codec, codec_info[i].name) == 0) { + s->selected_codec_id = codec_info[i].codec; + break; + } + } + if(codec_info[i].name == NULL) { + fprintf(stderr, "[lavd] Unable to find codec: \"%s\"\n", codec); + return NULL; + } + } else if(strncasecmp("bitrate=", item, strlen("bitrate=")) == 0) { + char *bitrate_str = item + strlen("bitrate="); + char *end_ptr; + char unit_prefix_u; + s->requested_bitrate = strtoul(bitrate_str, &end_ptr, 10); + unit_prefix_u = toupper(*end_ptr); + switch(unit_prefix_u) { + case 'G': + s->requested_bitrate *= 1000; + case 'M': + s->requested_bitrate *= 1000; + case 'K': + s->requested_bitrate *= 1000; + break; + case '\0': + break; + default: + fprintf(stderr, "[lavc] Error: unknown unit prefix %c.\n", + *end_ptr); + return NULL; + } + } else { + fprintf(stderr, "[lavc] Error: unknown option %s.\n", + item); + return NULL; + } + fmt = NULL; + } + } s->decoded = NULL; @@ -117,21 +182,42 @@ void * libavcodec_compress_init(char * fmt) static bool configure_with(struct libav_video_compress *s, struct video_frame *frame) { int ret; - int codec_id = DEFAULT_CODEC; + int codec_id; + int pix_fmt; + double avg_bpp; // average bite per pixel // implement multiple tiles support if needed assert(frame->tile_count == 1); s->saved_desc = video_desc_from_frame(frame); struct video_desc compressed_desc; compressed_desc = video_desc_from_frame(frame); - switch(codec_id) { - case CODEC_ID_H264: + switch(s->selected_codec_id) { + case H264: + codec_id = CODEC_ID_H264; +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + pix_fmt = AV_PIX_FMT_YUV420P; +#else + pix_fmt = PIX_FMT_YUV420P; +#endif compressed_desc.color_spec = H264; + // from H.264 Primer + avg_bpp = + 4 * /* for H.264: 1 - low motion, 2 - medium motion, 4 - high motion */ + 0.07; + break; + case MJPG: + codec_id = CODEC_ID_MJPEG; +#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 + pix_fmt = AV_PIX_FMT_YUVJ420P; +#else + pix_fmt = PIX_FMT_YUVJ420P; +#endif + compressed_desc.color_spec = MJPG; + avg_bpp = 0.7; break; default: - fprintf(stderr, "[Libavcodec] Unable to match " - "desired codec to UltraGrid internal " - "one.\n"); + fprintf(stderr, "[lavc] Requested output codec isn't " + "supported by libavcodec.\n"); return false; } @@ -147,7 +233,7 @@ static bool configure_with(struct libav_video_compress *s, struct video_frame *f /* find the video encoder */ s->codec = avcodec_find_encoder(codec_id); if (!s->codec) { - fprintf(stderr, "Libavcodec doesn't contain specified codec (H.264).\n" + fprintf(stderr, "Libavcodec doesn't contain specified codec.\n" "Hint: Check if you have libavcodec-extra package installed.\n"); return false; } @@ -160,9 +246,14 @@ static bool configure_with(struct libav_video_compress *s, struct video_frame *f } /* put parameters */ - s->codec_ctx->bit_rate = frame->tiles[0].width * frame->tiles[0].height * - 4 * /* for H.264: 1 - low motion, 2 - medium motion, 4 - high motion */ - 0.07 * frame->fps; + if(s->requested_bitrate > 0) { + s->codec_ctx->bit_rate = s->requested_bitrate; + } else { + s->codec_ctx->bit_rate = frame->tiles[0].width * frame->tiles[0].height * + avg_bpp * frame->fps; + } + s->codec_ctx->bit_rate_tolerance = s->codec_ctx->bit_rate / 4; + /* resolution must be a multiple of two */ s->codec_ctx->width = frame->tiles[0].width; s->codec_ctx->height = frame->tiles[0].height; @@ -187,11 +278,8 @@ static bool configure_with(struct libav_video_compress *s, struct video_frame *f "appropriate pixel format.\n"); return false; } -#ifdef HAVE_AVCODEC_ENCODE_VIDEO2 - s->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; -#else - s->codec_ctx->pix_fmt = PIX_FMT_YUV420P; -#endif + + s->codec_ctx->pix_fmt = pix_fmt; s->decoded = malloc(frame->tiles[0].width * frame->tiles[0].height * 4); @@ -199,6 +287,20 @@ static bool configure_with(struct libav_video_compress *s, struct video_frame *f av_opt_set(s->codec_ctx->priv_data, "preset", "ultrafast", 0); //av_opt_set(s->codec_ctx->priv_data, "tune", "fastdecode", 0); av_opt_set(s->codec_ctx->priv_data, "tune", "zerolatency", 0); + } else { + // zero should mean count equal to the number of virtual cores + 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"); + } + } } /* open it */ diff --git a/src/video_decompress.c b/src/video_decompress.c index 858771f9b..342344bf9 100644 --- a/src/video_decompress.c +++ b/src/video_decompress.c @@ -132,6 +132,7 @@ struct decode_from_to decoders_for_codec[] = { { JPEG, UYVY, JPEG_MAGIC, 500 }, { H264, UYVY, LIBAVCODEC_MAGIC, 500 }, { JPEG, UYVY, LIBAVCODEC_MAGIC, 600 }, + { MJPG, UYVY, LIBAVCODEC_MAGIC, 500 }, { (codec_t) -1, (codec_t) -1, NULL_MAGIC } }; const int decoders_for_codec_count = (sizeof(decoders_for_codec) / sizeof(struct decode_from_to)); diff --git a/src/video_decompress/libavcodec.c b/src/video_decompress/libavcodec.c index b6892c057..afd646558 100644 --- a/src/video_decompress/libavcodec.c +++ b/src/video_decompress/libavcodec.c @@ -96,6 +96,7 @@ static bool configure_with(struct state_libavcodec_decompress *s, case H264: codec_id = CODEC_ID_H264; break; + case MJPG: case JPEG: codec_id = CODEC_ID_MJPEG; fprintf(stderr, "[lavd] Warning: JPEG decoder "