From patchwork Fri Feb 2 02:44:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 7480 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.156.27 with SMTP id q27csp318748jak; Thu, 1 Feb 2018 18:43:31 -0800 (PST) X-Google-Smtp-Source: AH8x224Iz5fVuESVRDQmW2e9Cdwl3ZoDIJbMd4KuAF2zDm1r7HzAf9WuNz2qv+nSjrjOBzcNc5Jb X-Received: by 10.28.239.19 with SMTP id n19mr9506753wmh.20.1517539411144; Thu, 01 Feb 2018 18:43:31 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1517539411; cv=none; d=google.com; s=arc-20160816; b=S0m3LMYoociQ7SNfJmqOSgJmt38sJXT3UI6yioiRW6CZue7wBvETBH5+1Q1LycJonu dWRKmpyoNc0xXxAlv6oxMHGuWTy1eHlZ5wB0ZWISNZDrIN4NJVlmmT2IlRAPKrEaIK+I Km1M6vS2HlTfuGnwxsyMgHrqOMIgR3rd/Lg2lz+F5vxa68LBALVu4jaqa9Ky7wS+0cka lPKTKqiu1tN5n9K3//3nZSemdjaWjqV/i6g5F7UFivsKRbztZMeAd5PukBoaDV+8dfty boqG0bt/Juva1nQfO+PYO988S5opiMC8gvrntGjk9Zx3Uf4CzHlhF6RfmrVmgEPLcbij XYfw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject :content-language:in-reply-to:mime-version:date:message-id:from :references:to:dkim-signature:delivered-to :arc-authentication-results; bh=I+plbgExCQMMiLCne5cocH8XrfZ9DTvB68Olq6DEj3Q=; b=txPB6fv68YxVXldXG8ol04nudqSAmSbYDuMpHu0WM1usHUgUn5gFO77iFwwjgcc8Oo jaXbFi6YO883vzrCXdwsqXDLFWREQ+LbywZ6PNl8Rwq4etv2l47wKQKnl5doJCDg2AfF Vyv7GoGsNqlMkYDCXhBEpbTpP1+vbGJaNyimZNcyuoDJ2Ucl1mWUI0PVib0pwAJCmFiD CIY/YAhDaG4kZHrY+SxNioScb/MT9yxUetu+YTKqYpsT/WIxj/AmEjIg+tmpsdycqXMB tZkcmgywL9SQ5VMf39vgnseJRGSGHfSS2eT7acimmFZ3Ji7uQsL0a0gkjKL0Kb+TjFrG SD/g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde201610 header.b=F0GIzszw; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=mail.de Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id q4si415707wmd.105.2018.02.01.18.43.30; Thu, 01 Feb 2018 18:43:31 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde201610 header.b=F0GIzszw; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=mail.de Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id CFEB9689731; Fri, 2 Feb 2018 04:43:07 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from shout02.mail.de (shout02.mail.de [213.128.151.217]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C523468834F for ; Fri, 2 Feb 2018 04:43:00 +0200 (EET) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout02.mail.de (Postfix) with ESMTP id BDD826CEB0 for ; Fri, 2 Feb 2018 03:43:06 +0100 (CET) Received: from smtp03.mail.de (smtp03.bt.mail.de [10.0.121.213]) by postfix03.mail.de (Postfix) with ESMTP id A6A5B40180 for ; Fri, 2 Feb 2018 03:43:06 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde201610; t=1517539386; bh=gH07Wp2NgQ61kirLOvwjsHSwInT/RRNb522BznuNA2o=; h=Subject:To:References:From:Date:In-Reply-To:From; b=F0GIzszw2HFOm5oXi48EphFIe4cd6JPWfpU/jCTXKoN2pYhVAJkZLl+6BoTJBZQY5 bfi/5+2eld6bgwYlNhE+avjJzi3eFDgOrDaefvrtIoR9twAtp++vdD2DDGP9NL65FE HshL1eGzMVQf+cXr/xnDfH6Dmz9SXE40GX2IPmKw= Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp03.mail.de (Postfix) with ESMTPSA id 5225F8049B for ; Fri, 2 Feb 2018 03:43:06 +0100 (CET) To: ffmpeg-devel@ffmpeg.org References: <7225d2d3-f8a6-f89b-826f-6d4b9a0c49c0@mail.de> From: Thilo Borgmann Message-ID: <72019de1-6da0-eafe-a0ea-2c4684b5424a@mail.de> Date: Fri, 2 Feb 2018 03:44:05 +0100 MIME-Version: 1.0 In-Reply-To: <7225d2d3-f8a6-f89b-826f-6d4b9a0c49c0@mail.de> Content-Language: en-US Subject: [FFmpeg-devel] [V2 PATCH 3/4] Add codec wrapper for librv11 X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Moved to non-free. Symbols handled at compile time, no more dlsym fun. Merged into one ENCDEC. -Thilo From f27957382ffb729a2454914bcd3a99554d6dff0d Mon Sep 17 00:00:00 2001 From: Thilo Borgmann Date: Fri, 2 Feb 2018 02:51:14 +0100 Subject: [PATCH 3/4] Add codec wrapper for librv11 --- MAINTAINERS | 1 + configure | 5 + fftools/ffmpeg_opt.c | 2 +- libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/librv11.h | 55 ++++ libavcodec/librv11dec.c | 296 ++++++++++++++++++++++ libavcodec/librv11enc.c | 649 ++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/version.h | 2 +- 11 files changed, 1019 insertions(+), 2 deletions(-) create mode 100644 libavcodec/librv11.h create mode 100644 libavcodec/librv11dec.c create mode 100644 libavcodec/librv11enc.c diff --git a/MAINTAINERS b/MAINTAINERS index ba7a728..d07fa75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -192,6 +192,7 @@ Codecs: libkvazaar.c Arttu Ylä-Outinen libopenjpeg.c Jaikrishnan Menon libopenjpegenc.c Michael Bradshaw + librv11* Thilo Borgmann, Qiang Luo libtheoraenc.c David Conrad libvorbis.c David Conrad libvpx* James Zern diff --git a/configure b/configure index 0b01a22..9cc78da 100755 --- a/configure +++ b/configure @@ -250,6 +250,7 @@ External library support: --enable-librsvg enable SVG rasterization via librsvg [no] --enable-librubberband enable rubberband needed for rubberband filter [no] --enable-librtmp enable RTMP[E] support via librtmp [no] + --enable-librv11 enable RV11 support via librv11 [no] --enable-libshine enable fixed-point MP3 encoding via libshine [no] --enable-libsmbclient enable Samba protocol via libsmbclient [no] --enable-libsnappy enable Snappy compression, needed for hap encoding [no] @@ -1565,6 +1566,7 @@ EXTERNAL_LIBRARY_NONFREE_LIST=" libndi_newtek libfdk_aac openssl + librv11 libtls " @@ -2959,6 +2961,8 @@ libopus_decoder_deps="libopus" libopus_encoder_deps="libopus" libopus_encoder_select="audio_frame_queue" librsvg_decoder_deps="librsvg" +librv11_decoder_deps="librv11" +librv11_encoder_deps="librv11" libshine_encoder_deps="libshine" libshine_encoder_select="audio_frame_queue" libspeex_decoder_deps="libspeex" @@ -5893,6 +5897,7 @@ enabled libpulse && require_pkg_config libpulse libpulse pulse/pulseaud enabled librsvg && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_render_cairo enabled librtmp && require_pkg_config librtmp librtmp librtmp/rtmp.h RTMP_Socket enabled librubberband && require_pkg_config librubberband "rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new -lstdc++ && append librubberband_extralibs "-lstdc++" +enabled librv11 && require_pkg_config librv11 librv11 librv11_sdk.h RV60toYUV420Init enabled libshine && require_pkg_config libshine shine shine/layer3.h shine_encode_buffer enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init || require libsmbclient libsmbclient.h smbc_init -lsmbclient; } diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 997d538..0f88786 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -1747,7 +1747,7 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in ost->logfile_prefix ? ost->logfile_prefix : DEFAULT_PASS_LOGFILENAME_PREFIX, i); - if (!strcmp(ost->enc->name, "libx264")) { + if (!strcmp(ost->enc->name, "libx264") || !strcmp(ost->enc->name, "librv11enc")) { av_dict_set(&ost->encoder_opts, "stats", logfilename, AV_DICT_DONT_OVERWRITE); } else { if (video_enc->flags & AV_CODEC_FLAG_PASS2) { diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 68777d9..ad7b9cd 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -948,6 +948,8 @@ OBJS-$(CONFIG_LIBOPUS_DECODER) += libopusdec.o libopus.o \ vorbis_data.o OBJS-$(CONFIG_LIBOPUS_ENCODER) += libopusenc.o libopus.o \ vorbis_data.o +OBJS-$(CONFIG_LIBRV11_DECODER) += librv11dec.o +OBJS-$(CONFIG_LIBRV11_ENCODER) += librv11enc.o OBJS-$(CONFIG_LIBSHINE_ENCODER) += libshine.o OBJS-$(CONFIG_LIBSPEEX_DECODER) += libspeexdec.o OBJS-$(CONFIG_LIBSPEEX_ENCODER) += libspeexenc.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index ed1e7ab..64bb723 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -548,6 +548,7 @@ static void register_all(void) REGISTER_ENCDEC (LIBOPENJPEG, libopenjpeg); REGISTER_ENCDEC (LIBOPUS, libopus); REGISTER_DECODER(LIBRSVG, librsvg); + REGISTER_ENCDEC (LIBRV11, librv11); REGISTER_ENCODER(LIBSHINE, libshine); REGISTER_ENCDEC (LIBSPEEX, libspeex); REGISTER_ENCODER(LIBTHEORA, libtheora); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 8fbbc79..b7c1fa1 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -409,6 +409,7 @@ enum AVCodecID { AV_CODEC_ID_DXV, AV_CODEC_ID_SCREENPRESSO, AV_CODEC_ID_RSCC, + AV_CODEC_ID_RV60, AV_CODEC_ID_Y41P = 0x8000, AV_CODEC_ID_AVRP, diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index c3688de..1ad2c9c 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -471,6 +471,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, }, { + .id = AV_CODEC_ID_RV60, + .type = AVMEDIA_TYPE_VIDEO, + .name = "rv60", + .long_name = NULL_IF_CONFIG_SMALL("RealVideo 11"), + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + }, + { .id = AV_CODEC_ID_VC1, .type = AVMEDIA_TYPE_VIDEO, .name = "vc1", diff --git a/libavcodec/librv11.h b/libavcodec/librv11.h new file mode 100644 index 0000000..ec0d64c --- /dev/null +++ b/libavcodec/librv11.h @@ -0,0 +1,55 @@ +/* + * This copyright notice applies to this file only + * This Software is distributed under MIT License + * + * API software for using RealVideo 11 (RV60) Codec + * + * * Copyright (c) 2017 Qiang Luo, RealNetworks, Inc. + * * Copyright (c) 2017 Thilo Borgmann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVCODEC_LIBRV11_H +#define AVCODEC_LIBRV11_H + +#include + +#include "avcodec.h" +#include "libavutil/intreadwrite.h" + +#ifdef _WIN32 +#include "compat/w32dlfcn.h" +#else +#include +#endif + +#define RV_MAX_INPUT_FRAME_RATE 120 +#define RV_NUM_OUT_FRAMES 6 + +#define XSTR(s) STR(s) +#define STR(s) #s +#define LIBRV11DEC_FILE XSTR(RV_DEC_LIB_FILE) +#define LIBRV11ENC_FILE XSTR(RV_ENC_LIB_FILE) + + +#endif // AVCODEC_LIBRV11_H diff --git a/libavcodec/librv11dec.c b/libavcodec/librv11dec.c new file mode 100644 index 0000000..f3ed688 --- /dev/null +++ b/libavcodec/librv11dec.c @@ -0,0 +1,296 @@ +/* + * This copyright notice applies to this file only + * This Software is distributed under MIT License + * + * API software for using RealVideo 11 (RV60) Decoder + * + * * Copyright (c) 2017 Qiang Luo, RealNetworks, Inc. + * * Copyright (c) 2017 Thilo Borgmann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "libavutil/imgutils.h" +#include "internal.h" +#include "get_bits.h" + +#include "librv11.h" + +typedef struct LIBRV11DecContext { + AVCodecContext *avctx; + AVFrame out_frame; + void *codec_status; + uint32_t input_buf_size; + uint32_t input_buf_num; + uint32_t output_buf_size; + uint32_t output_buf_num; + uint8_t *output_buf[RV_NUM_OUT_FRAMES]; + int frame_index; + uint32_t last_frame; + uint32_t more_frames; +} LIBRV11DecContext; + +static av_cold int librv11dec_init(AVCodecContext *avctx) +{ + LIBRV11DecContext *ctx = avctx->priv_data; + RVInitParams init_params; + RVMsgSimple msg; + HX_RESULT res; + uint32_t SPO_extra_flags; + uint32_t version; + int i; + ctx->avctx = avctx; + + if (!avctx->extradata || !avctx->extradata_size) { + return AVERROR_INVALIDDATA; + } + + SPO_extra_flags = AV_RL32(&avctx->extradata[0]); + version = AV_RL32(&avctx->extradata[5]); + + init_params.out_type = 0; + init_params.width = avctx->width; + init_params.height = avctx->height; + init_params.pad_width = avctx->width; + init_params.pad_height = avctx->height; + init_params.pad_to_32 = 0; + init_params.invariants = SPO_extra_flags; + init_params.packetize = 1; + init_params.version = version; + + res = RV60toYUV420Init(&init_params, &ctx->codec_status); + if (res) { + return res; + } + + msg.id = RV_MSG_ID_SMOOTHING_POSTFILTER; + msg.value1 = RV_MSG_DISABLE; + msg.value2 = 0; + + res = RV60toYUV420CustomMessage(&msg.id, ctx->codec_status); + if (res) { + return res; + } + + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + ctx->last_frame = 0; + ctx->more_frames = 0; + ctx->frame_index = 0; + + ctx->output_buf_size = (avctx->width * avctx->height * 12) >> 3; + for(i = 0; i < RV_NUM_OUT_FRAMES; i++) { + ctx->output_buf[i] = av_mallocz(ctx->output_buf_size); + } + av_image_fill_arrays(ctx->out_frame.data, ctx->out_frame.linesize, ctx->output_buf[0], AV_PIX_FMT_YUV420P, avctx->width, avctx->height, 1); + + return 0; +} + +static int librv11dec_decode_last(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + LIBRV11DecContext* ctx = avctx->priv_data; + AVFrame *pict = data; + const int stride = avctx->width * avctx->height; + RVInParams inParams; + RVOutParams outParams; + int32_t ret; + + inParams.length = 0; + inParams.interpolate = 0; + inParams.num_segments = 0; + inParams.segments = NULL; + inParams.timestamp = 0; + inParams.flags = RV_DECODE_MORE_FRAMES; + + pict->data[0] = ctx->output_buf[ctx->frame_index]; + pict->data[1] = ctx->output_buf[ctx->frame_index] + stride; + pict->data[2] = ctx->output_buf[ctx->frame_index] + stride + (stride >> 2); + + pict->linesize[0] = avctx->width; + pict->linesize[1] = avctx->width >> 1; + pict->linesize[2] = avctx->width >> 1; + + ret = RV60toYUV420Transform(NULL, ctx->output_buf[ctx->frame_index], &inParams, &outParams, ctx->codec_status); + if (ret) { + *got_frame = 0; + return ret; + } + + if(outParams.notes & RV_DECODE_LAST_FRAME) { + ctx->last_frame = 1; + } + + pict->pts = outParams.timestamp; + pict->pkt_dts = avpkt->dts; + pict->width = avctx->width; + pict->height = avctx->height; + pict->format = AV_PIX_FMT_YUV420P; + pict->key_frame = 0; + pict->pict_type = AV_PICTURE_TYPE_P; + + if (outParams.notes & RV_DECODE_KEY_FRAME) { + pict->key_frame = 1; + pict->pict_type = AV_PICTURE_TYPE_I; + } else if (outParams.notes & RV_DECODE_B_FRAME || + outParams.notes & RV_DECODE_FRU_FRAME) { + pict->key_frame = 0; + pict->pict_type = AV_PICTURE_TYPE_B; + } + + *got_frame = !(outParams.notes & RV_DECODE_DONT_DRAW); + + return 0; + +} + +static int librv11dec_decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + LIBRV11DecContext* ctx = avctx->priv_data; + const uint8_t *buf = avpkt->data; + int buf_size = avpkt->size; + const int stride = avctx->width * avctx->height; + AVFrame *pict = data; + int slice_count = 0; + int consumed_bytes = 0; + RVSegment *segment = NULL; + RVInParams inParams; + RVOutParams outParams; + int32_t ret; + int i; + + ret = ff_get_buffer(avctx, pict, 0); + if (ret < 0) { + return ret; + } + + ctx->frame_index++; + ctx->frame_index %= RV_NUM_OUT_FRAMES; + + if (!buf_size) { // maybe last frame + if (ctx->last_frame) { + *got_frame = 0; + return 0; + } else { + return librv11dec_decode_last(avctx, data, got_frame, avpkt); + } + } + + if (!avctx->slice_count) { + slice_count = (*buf++) + 1; + segment = av_mallocz(slice_count * sizeof(RVSegment)); + for (i = 0; i < slice_count; i++) { + segment[i].is_valid = *buf; + segment[i].offset = *(buf + 4); + buf += 8; + } + buf_size -= 1 + 8 * slice_count; + } else { + slice_count = avctx->slice_count; + } + + inParams.length = buf_size; + inParams.interpolate = 0; + inParams.num_segments = slice_count - 1; + inParams.segments = segment; + inParams.timestamp = avpkt->pts; + inParams.flags = 0; + + pict->data[0] = ctx->output_buf[ctx->frame_index]; + pict->data[1] = ctx->output_buf[ctx->frame_index] + stride; + pict->data[2] = ctx->output_buf[ctx->frame_index] + stride + (stride >> 2); + pict->linesize[0] = avctx->width; + pict->linesize[1] = avctx->width >> 1; + pict->linesize[2] = avctx->width >> 1; + + ret = RV60toYUV420Transform((UCHAR*)buf, ctx->output_buf[ctx->frame_index], &inParams, &outParams, ctx->codec_status); + if (ret) { + consumed_bytes = 0; + *got_frame = 0; + return ret; + } + + if (slice_count) { + av_freep(&segment); + } + + consumed_bytes = avpkt->size; + *got_frame = 1; + pict->key_frame = 0; + pict->pict_type = AV_PICTURE_TYPE_P; + + if (outParams.notes & RV_DECODE_KEY_FRAME) { + pict->pict_type = AV_PICTURE_TYPE_I; + pict->key_frame = 1; + } else if (outParams.notes & RV_DECODE_B_FRAME || + outParams.notes & RV_DECODE_FRU_FRAME) { + pict->pict_type = AV_PICTURE_TYPE_B; + } else if (outParams.notes & RV_DECODE_DONT_DRAW) { + consumed_bytes = 0; + *got_frame = 0; + } + + pict->pts = outParams.timestamp; + pict->pkt_dts = avpkt->dts; + pict->width = avctx->width; + pict->height = avctx->height; + pict->format = AV_PIX_FMT_YUV420P; + + return consumed_bytes; +} + +static av_cold int librv11dec_close(AVCodecContext *avctx) +{ + LIBRV11DecContext* ctx = avctx->priv_data; + int i; + + RV60toYUV420Free(ctx->codec_status); + + for(i = 0; i < RV_NUM_OUT_FRAMES; i++) { + if (ctx->output_buf[i]) { + av_freep(&ctx->output_buf[i]); + } + } + + return 0; +} + +static void librv11dec_flush(AVCodecContext *avctx) +{ + LIBRV11DecContext* ctx = avctx->priv_data; + uint32_t id = RV_MSG_ID_FLUSH; + RV60toYUV420HiveMessage(&id, ctx->codec_status); +} + +AVCodec ff_librv11_decoder = { + .name = "librv11", + .long_name = NULL_IF_CONFIG_SMALL("librv11 RealVideo 11 (RV60)"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_RV60, + .priv_data_size = sizeof(LIBRV11DecContext), + .init = librv11dec_init, + .decode = librv11dec_decode_frame, + .close = librv11dec_close, + .flush = librv11dec_flush, + .capabilities = AV_CODEC_CAP_DELAY, +}; diff --git a/libavcodec/librv11enc.c b/libavcodec/librv11enc.c new file mode 100644 index 0000000..bdf2ceb --- /dev/null +++ b/libavcodec/librv11enc.c @@ -0,0 +1,649 @@ +/* + * This copyright notice applies to this file only + * This Software is distributed under MIT License + * + * API software for using RealVideo 11 (RV60) Encoder + * + * * Copyright (c) 2017 Qiang Luo, RealNetworks, Inc. + * * Copyright (c) 2017 Thilo Borgmann + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" + +#include "librv11.h" + +typedef struct CodedFrameList { + AVPacket pkt; + struct CodedFrameList* pNext; +} CodedFrameList; + +typedef struct FrameRatioConverter { + uint32_t count; + double framerate; + uint8_t* selected_map; + uint32_t framerate_in; +} FrameRatioConverter; + +typedef struct LIBRV11EncContext +{ + AVClass *class; + void *codec_ref; + void *stream_ref; + RVEncodeParam rvenc_param; + uint32_t codec_4cc; + uint32_t frame_size; + uint8_t* frame_buf; + CodedFrameList* coded_frame_list; + int bitrate_vbr; ///< 0:VBRQuality, 1:VBRBitrate + int eos; ///< set to TURE when input frame was set to NULL + int last_packet; ///< set to 1 when the last codec frame was popped out + int seen_packet; + int64_t prev_dts; + FrameRatioConverter fr_convert; +} LIBRV11EncContext; + +static int32_t interval_search(uint8_t *buf, const uint32_t buf_size, const uint32_t num_frames) +{ + if (buf_size < num_frames || !num_frames) { + return -1; + } + + if (!(buf_size % num_frames)) { + uint32_t i; + const uint32_t den = buf_size / num_frames; + + for(i = 0; i < buf_size; i++) { + if (!((i+1) % den)) { + buf[i] = 1; + } + } + } else { + const uint32_t half = buf_size >> 1; + const uint32_t left = num_frames >> 1; + const uint32_t right = left + (num_frames & 1); + + interval_search(buf, half, left); + interval_search(buf + half, buf_size - half, right); + } + + return 0; +} + +static av_cold int32_t fr_convert_init(FrameRatioConverter* fr_convert, double fr_in, double fr_max) +{ + if (fr_in < fr_max || fr_in > RV_MAX_INPUT_FRAME_RATE) { + return -1; + } + + fr_convert->count = 0; + fr_convert->framerate = fr_max; + fr_convert->framerate_in = (int32_t)(fr_in + 0.5); + fr_convert->selected_map = av_mallocz(fr_convert->framerate_in); + + return interval_search(fr_convert->selected_map, fr_convert->framerate_in, (int32_t)(fr_max + 0.5)); +} + +static av_always_inline int32_t use_frame(FrameRatioConverter *fr_convert) +{ + return fr_convert->selected_map[fr_convert->count]; +} + +static void next_frame(FrameRatioConverter* fr_convert) +{ + fr_convert->count++; + if (fr_convert->count >= fr_convert->framerate_in) { + fr_convert->count = 0; + } +} + +static void coded_frame_add(void *list, CodedFrameList *cx_frame) +{ + CodedFrameList **p = list; + + while(*p) { + p = &(*p)->pNext; + } + + *p = cx_frame; + cx_frame->pNext = NULL; +} + +static CodedFrameList* coded_frame_remove_header(CodedFrameList** list) +{ + CodedFrameList *p = *list; + + if (p) { + *list = p->pNext; + } + + return p; +} + +static av_cold void free_coded_frame(CodedFrameList *cx_frame) +{ + av_packet_unref(&cx_frame->pkt); + av_freep(&cx_frame); +} + +static av_cold void free_frame_list(CodedFrameList *list) +{ + CodedFrameList *p = list; + + while(p) { + list = list->pNext; + free_coded_frame(p); + p = list; + } +} + +static int librv11_num_segments(uint32_t data_length) +{ + int num_packet; + + if (data_length < 1) { + return -1; + } + + num_packet = data_length / RV_MAX_PACKET_SIZE; + if (data_length % RV_MAX_PACKET_SIZE) { + num_packet++; + } + + return num_packet; +} + +static int librv11_write_frame_header(uint8_t *data, int num_packet) +{ + int i; + + if (num_packet < 1 || !data) { + return -1; + } + + data[0] = num_packet -1; + + for (i = 0; i < num_packet; i++) { + // the value of next 4 byte is the segment index + AV_WL32(data + 1 + 8 * i, i + 1); + // continuous 4 byte is the segment offset + AV_WL32(data + 5 + 8 * i, RV_MAX_PACKET_SIZE * i); + } + + return 0; +} + +static av_cold int CC librv11enc_data_callback(void *stream_ctx, void *stream_ref, HXCodecData* data) +{ + int ret = 0; + int pict_type; + int64_t cur_pts; + int64_t cur_dts; + AVCodecContext *avctx = (AVCodecContext*) stream_ctx; + LIBRV11EncContext *enctx = avctx->priv_data; + + if (data && data->length) { + int numPackets = 0; + int bufLength; + + CodedFrameList* node = av_mallocz(sizeof(CodedFrameList)); + if (!node) { + av_log(avctx, AV_LOG_ERROR, "Failed to allocate memory.\n"); + return AVERROR(ENOMEM); + } + + // for rv11 num_segments should be 1 + if (data->num_segments == 1) { + numPackets = librv11_num_segments(data->length); + if (numPackets > 127 || numPackets < 0) { + av_log(avctx, AV_LOG_WARNING, "Too many packets per frame (%d).\n", numPackets); + } + } else { + av_log(avctx, AV_LOG_WARNING, "Invalid number of segments (%u), should be 1.\n", data->num_segments); + } + + // allocate extra numPackets*8+1 byte for real video head saving segment information + bufLength = data->length + numPackets * 8 + 1; + ret = av_new_packet(&node->pkt, bufLength); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet.\n"); + return ret; + } + librv11_write_frame_header(node->pkt.data, numPackets); + memcpy((node->pkt.data + numPackets * 8 + 1), data->data, data->length); + + if (data->flags & HX_KEYFRAME_FLAG) { + node->pkt.flags |= AV_PKT_FLAG_KEY; + } + + if (data->last_packet) { + node->pkt.flags |= RV_PKT_FLAG_END; + } + + pict_type = ((*(data->data + 1)) >> 6) & 0x03; + cur_pts = data->timestamp; + if(!enctx->seen_packet) { + enctx->seen_packet = 1; + cur_dts = cur_pts - 1; // first frame + } else { + if (pict_type == 2) { + cur_dts = cur_pts; // B-frame + } else { + cur_dts = enctx->prev_dts + 1; // ref-frame + } + } + + node->pkt.pts = cur_pts; + node->pkt.dts = cur_dts; + enctx->prev_dts = cur_dts; + + coded_frame_add((void*)(&enctx->coded_frame_list), node); + } + + return 0; +} + +static av_cold int librv11enc_init(AVCodecContext *avctx) +{ + LIBRV11EncContext *enctx = avctx->priv_data; + HX_RESULT res; + HXMof hxInput, hxOutput; + HXFormatVideo mofin, mofout; + HXCodecInit ci; + double framerate; + uint32_t dummy_ui32; + float dummy_f; + + // Width and height of the video are required to be multiples of 4 + if (avctx->width % 4 || avctx->height % 4) { + av_log(avctx, AV_LOG_ERROR, + "Video dimensions have to be multiples of 4 (%dx%d).\n", + avctx->width, avctx->height); + return AVERROR(EINVAL); + } + + res = PNCodec_QueryMediaFormat(NULL, &hxInput, &hxOutput, 0); + if (FAILED(res)) { + av_log(avctx, AV_LOG_ERROR, "Failed to query in/out formats.\n"); + return AVERROR_EXTERNAL; + } + + res = PNCodec_Open(hxOutput.submoftag, &enctx->codec_ref); + if (FAILED(res)) { + av_log(avctx, AV_LOG_ERROR, "Failed to open the codec library.\n"); + return AVERROR_EXTERNAL; + } + + enctx->codec_4cc = hxOutput.submoftag; + + // init the librv11 encoder using default/user parameters + enctx->rvenc_param.in_width = avctx->width; + enctx->rvenc_param.in_height = avctx->height; + + if (!enctx->rvenc_param.out_width) { + enctx->rvenc_param.out_width = avctx->width; + } + if (!enctx->rvenc_param.out_height) { + enctx->rvenc_param.out_height = avctx->height; + } + + if (enctx->bitrate_vbr) { + enctx->rvenc_param.encode_type = ENCODE_VBR_BITRATE; + if (avctx->flags & AV_CODEC_FLAG_PASS1) { + enctx->rvenc_param.vbr_quality = 25; + } + } else { + enctx->rvenc_param.encode_type = ENCODE_VBR_QUALITY; + } + + if (avctx->flags & AV_CODEC_FLAG_PASS1) { + enctx->rvenc_param.encode_mode = ENCODE_CREATE_ANALYSIS; + } else if (avctx->flags & AV_CODEC_FLAG_PASS2){ + enctx->rvenc_param.encode_mode = ENCODE_USING_ANALYSIS; + } else { + enctx->rvenc_param.encode_mode = ENCODE_SINGLE_PASS; + } + + enctx->rvenc_param.avg_bitrate = avctx->bit_rate; + enctx->rvenc_param.max_bitrate = enctx->rvenc_param.avg_bitrate; + + enctx->rvenc_param.max_packet_size = RV_MAX_PACKET_SIZE; + + if (!avctx->framerate.den) { + return AVERROR(EINVAL); + } + framerate = av_q2d(avctx->framerate); + + res = fr_convert_init(&enctx->fr_convert, framerate, enctx->rvenc_param.max_framerate); + if (res) { + fr_convert_init(&enctx->fr_convert, framerate, framerate); + } + + // set input parameters for I420 + memset(&mofin, 0, sizeof(mofin)); + mofin.length = sizeof(HXFormatVideo); + mofin.moftag = HX_MEDIA_VIDEO; + mofin.submoftag = HX_YUV420_ID; + mofin.width = enctx->rvenc_param.in_width; + mofin.height = enctx->rvenc_param.in_height; + mofin.bit_count = 12; + mofin.pad_width = 0; + mofin.pad_height = 0; + mofin.moftag = HX_MEDIA_VIDEO; + + mofin.fps = (int)(enctx->fr_convert.framerate * (1L << 16) + 0.5); + + // set output parameters + memset(&mofout, 0, sizeof(mofout)); + mofout.length = sizeof(mofout); + mofout.moftag = HX_MEDIA_VIDEO; + mofout.submoftag = enctx->codec_4cc; + mofout.width = enctx->rvenc_param.out_width; + mofout.height = enctx->rvenc_param.out_height; + mofout.bit_count = mofin.bit_count; + mofout.pad_width = 0; + mofout.pad_height = 0; + mofout.moftag = HX_MEDIA_VIDEO; + mofout.fps = mofin.fps; + + ci.in_mof = (HXMof*)&mofin; + ci.out_mof = (HXMof*)&mofout; + ci.mem_ptr = NULL; + +#define SET_RV11_PROP(prop, value) { \ + res = PNStream_SetProperty(enctx->stream_ref, prop, value); \ + if (FAILED(res)) { \ + av_log(avctx, AV_LOG_ERROR, "Failed to set property %s.\n", #prop); \ + return AVERROR_EXTERNAL; \ + } \ +} + + res = PNCodec_StreamOpen(enctx->codec_ref, &enctx->stream_ref, &ci); + if (FAILED(res)) { + av_log(avctx, AV_LOG_ERROR, "Failed to open the stream.\n"); + return AVERROR_EXTERNAL; + } + + if (enctx->bitrate_vbr && !(avctx->flags & AV_CODEC_FLAG_PASS1)) { + SET_RV11_PROP(SP_BITRATE, &enctx->rvenc_param.avg_bitrate); + } else { + SET_RV11_PROP(SP_QUALITY, &enctx->rvenc_param.vbr_quality); + } + + SET_RV11_PROP(SP_MAXBITRATE, &enctx->rvenc_param.max_bitrate); + + dummy_f = (float)(enctx->rvenc_param.max_framerate); + SET_RV11_PROP(SP_MAX_FRAMERATE, &dummy_f); + + dummy_ui32 = 0; + SET_RV11_PROP(SP_CHECK_FRAME_SKIP, &dummy_ui32); + + dummy_ui32 = 0; + SET_RV11_PROP(SP_LIVE, &dummy_ui32); + + dummy_ui32 = enctx->rvenc_param.max_keyint * 1000; + SET_RV11_PROP(SP_KEYFRAMERATE, &dummy_ui32); + + SET_RV11_PROP(SP_ECC, &enctx->rvenc_param.loss_protect); + SET_RV11_PROP(SP_SET_FAST_LEVEL, &enctx->rvenc_param.enc_complexity); + + dummy_ui32 = (uint32_t) enctx->rvenc_param.max_start_latency * 1000; + SET_RV11_PROP(SP_TARGET_LATENCY, &dummy_ui32); + + if (enctx->rvenc_param.pon > 0) { + SET_RV11_PROP(SP_SET_PON_START, &enctx->rvenc_param.pon); + } + + res = PNStream_SetDataCallback(enctx->stream_ref, avctx, NULL, librv11enc_data_callback); + if (FAILED(res)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set data callback.\n"); + return AVERROR_EXTERNAL; + } + + if (avctx->flags & AV_CODEC_FLAG_PASS1) { + dummy_ui32 = SPCODINGMODE_ANALYSIS; + } else if (avctx->flags & AV_CODEC_FLAG_PASS2) { + dummy_ui32 = SPCODINGMODE_FROMFILE; + } else { + dummy_ui32 = SPCODINGMODE_ENCODE; + } + SET_RV11_PROP(SP_CODING_MODE, &dummy_ui32); + + // if vbr_opt is set to true the encoder can override bitrate settings to remain subjective quality. + // default is false + SET_RV11_PROP(SP_RATECONTROL_PLUS, &enctx->rvenc_param.vbr_opt); + + if (avctx->flags & AV_CODEC_FLAG_PASS1 || + avctx->flags & AV_CODEC_FLAG_PASS2) { + if (!enctx->rvenc_param.passlog_file) { + av_log(avctx, AV_LOG_ERROR, "Invalid passlogfile.\n"); + return AVERROR(EINVAL); + } + SET_RV11_PROP(SP_ANALYSIS_FILENAME, (void*)enctx->rvenc_param.passlog_file); + } + + dummy_ui32 = 1; + SET_RV11_PROP(SP_CODEC_SETUP_DONE, &dummy_ui32); + + dummy_ui32 = enctx->rvenc_param.max_packet_size; + res = PNStream_SetOutputPacketSize(enctx->stream_ref, dummy_ui32, dummy_ui32, &dummy_ui32); + if (FAILED(res)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set output packet size (%u).\n", dummy_ui32); + return AVERROR(EINVAL); + } + + enctx->frame_size = av_image_get_buffer_size(avctx->pix_fmt, avctx->width, avctx->height, 1); + + enctx->frame_buf = av_malloc(enctx->frame_size); + if (!enctx->frame_buf) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate frame buffer."); + return AVERROR(ENOMEM); + } + + if (!avctx->extradata_size) { + uint8_t *q; + + avctx->extradata_size = 14; + avctx->extradata = av_mallocz(avctx->extradata_size); + if (!avctx->extradata) { + avctx->extradata_size = 0; + return AVERROR(ENOMEM); + } + + q = avctx->extradata; + *q++ = 0x01; //spo_extra_flags + *q++ = 0x08; + *q++ = 0x10; + *q++ = 0x00; + *q++ = 0x40; //codec frontend version (NOT codec version!) + *q++ = 0x00; + *q++ = 0x00; + *q++ = 0x00; + PNCodec_GetHyperProperty(enctx->codec_ref, q); + } + + return 0; +} + +static int librv11enc_encode(AVCodecContext *avctx, AVPacket*pkt, const AVFrame* frame, int* got_packet) +{ + int ret = 0; + int nb; + HXCodecData cd; + HX_RESULT res; + LIBRV11EncContext *enctx = avctx->priv_data; + CodedFrameList* pCodedFrame; + +#define RETURN_PACKET(got_p, r) { \ + *got_packet = got_p; \ + return r; \ +} + + if (!frame && enctx->last_packet) { + RETURN_PACKET(0, 0); + } + + if (frame && enctx->frame_buf && enctx->frame_size > 0) { + if (use_frame(&enctx->fr_convert)) { + nb = av_image_copy_to_buffer(enctx->frame_buf, enctx->frame_size, + (const uint8_t * const *)frame->data, + frame->linesize, avctx->pix_fmt, + avctx->width, avctx->height, 1); + if (nb < 0) { + RETURN_PACKET(0, nb); + } + + cd.data = enctx->frame_buf; + cd.length = enctx->frame_size; + cd.timestamp = frame->pts;//XXX * 1000 * av_q2d(avctx->time_base); + cd.flags = (frame->pict_type == AV_PICTURE_TYPE_I) ? HX_KEYFRAME_FLAG : 0; + cd.last_packet = enctx->eos; + cd.num_segments = 1; + cd.segments[0].is_valid = 1; + cd.segments[0].segment_offset = 0; + + res = PNCodec_Input(enctx->codec_ref, &cd); + if (FAILED(res)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set input data.\n"); + RETURN_PACKET(0, AVERROR_EXTERNAL); + } + } + next_frame(&enctx->fr_convert); + } else if (!frame && !enctx->eos) { + enctx->eos = 1; + + cd.data = NULL; + cd.length = 0; + cd.timestamp = 0; + cd.flags = 0; + cd.last_packet = 1; + cd.num_segments = 1; + cd.segments[0].is_valid = 1; + cd.segments[0].segment_offset = 0; + + res = PNCodec_Input(enctx->codec_ref, &cd); + if (FAILED(res)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set input data.\n"); + RETURN_PACKET(0, AVERROR_EXTERNAL); + } + } + + pCodedFrame = coded_frame_remove_header(&enctx->coded_frame_list); + if (pCodedFrame) { + if (pCodedFrame->pkt.flags & RV_PKT_FLAG_END) { + pCodedFrame->pkt.flags &= ~RV_PKT_FLAG_END; + enctx->last_packet = 1; + } + + ret = av_packet_ref(pkt, &pCodedFrame->pkt); + if (ret < 0) { + av_log(enctx, AV_LOG_INFO, "Failed to recieve frame reference.\n"); + free_coded_frame(pCodedFrame); + RETURN_PACKET(0, ret); + } else { + pkt->pts = pCodedFrame->pkt.pts; + pkt->dts = pCodedFrame->pkt.dts; + + if (pCodedFrame->pkt.flags&AV_PKT_FLAG_KEY) { + pkt->flags |= AV_PKT_FLAG_KEY; + } + free_coded_frame(pCodedFrame); + RETURN_PACKET(1, 0); + } + } + + *got_packet = 0; + return ret; +} + +static av_cold int librv11enc_close(AVCodecContext *avctx) +{ + LIBRV11EncContext *enctx = avctx->priv_data; + + PNStream_Close(enctx->stream_ref); + PNCodec_Close(enctx->codec_ref); + enctx->stream_ref = NULL; + enctx->codec_ref = NULL; + + free_frame_list(enctx->coded_frame_list); + + av_freep(&enctx->frame_buf); + av_freep(&enctx->fr_convert.selected_map); + av_freep(&avctx->extradata); + + return 0; +} + +#define OFFSET(x) offsetof(LIBRV11EncContext, x) +#define OFFSETP(x) offsetof(RVEncodeParam, x) +#define OFFSETBASE OFFSET(rvenc_param) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM + +static const AVOption options[] = { + { "is_lossprotect", "enable loss protection feature", OFFSETBASE+OFFSETP(loss_protect), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, + { "output_width", "video encoded frame output width", OFFSETBASE+OFFSETP(out_width), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 4096, VE }, + { "output_height", "video encoded frame output height", OFFSETBASE+OFFSETP(out_height), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 4096, VE }, + { "rc_strategy", "which ratecontrol method to be used", OFFSET(bitrate_vbr), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE, "rc_strategy" }, + { "bitrate", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "rc_strategy" }, + { "quality", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "rc_strategy" }, + { "complexity", "encoding complexity", OFFSETBASE+OFFSETP(enc_complexity), AV_OPT_TYPE_INT, { .i64 = 75 }, 55, 85, VE, "complexity" }, + { "verylow", "", 0, AV_OPT_TYPE_CONST, {.i64=55}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" }, + { "low", "", 0, AV_OPT_TYPE_CONST, {.i64=65}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" }, + { "medium", "", 0, AV_OPT_TYPE_CONST, {.i64=75}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" }, + { "high", "", 0, AV_OPT_TYPE_CONST, {.i64=85}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "complexity" }, + { "framerate", "max frame rate value", OFFSETBASE+OFFSETP(max_framerate), AV_OPT_TYPE_INT, { .i64 = RV_MAX_INPUT_FRAME_RATE }, 0, RV_MAX_INPUT_FRAME_RATE, VE }, + { "max_keyint", "max keyframe interval", OFFSETBASE+OFFSETP(max_keyint), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, 10, VE }, + { "max_latency", "max video latency on start", OFFSETBASE+OFFSETP(max_start_latency), AV_OPT_TYPE_FLOAT, { .dbl = 4.0 }, 0.5, 60.0, VE }, + { "vbrquality", "vbr quality value", OFFSETBASE+OFFSETP(vbr_quality), AV_OPT_TYPE_INT, { .i64 = 60 }, 0, 100, VE }, + { "passlogfile", "filename for 2 pass encoding stats", OFFSETBASE+OFFSETP(passlog_file), AV_OPT_TYPE_STRING, { .str = "rv11passstats.log" }, 0, 0, VE }, + { "pon", "picture order number", OFFSETBASE+OFFSETP(pon), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, + { "vbr_opt", "vbr enabled", OFFSETBASE+OFFSETP(vbr_opt), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, "vbr_opt" }, + { "true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "vbr_opt" }, + { "false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "vbr_opt" }, + { NULL }, +}; + +static const AVClass librv11_class = { + .class_name = "librv11", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_librv11_encoder = { + .name = "librv11", + .long_name = NULL_IF_CONFIG_SMALL("librv11 RealVideo 11 (RV60)"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_RV60, + .priv_data_size = sizeof(LIBRV11EncContext), + .init = librv11enc_init, + .encode2 = librv11enc_encode, + .close = librv11enc_close, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AUTO_THREADS, + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE + }, + .priv_class = &librv11_class, +}; diff --git a/libavcodec/version.h b/libavcodec/version.h index 47a15d5..1050f60 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -28,7 +28,7 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 9 +#define LIBAVCODEC_VERSION_MINOR 10 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \