From patchwork Thu Jan 18 18:37:54 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thilo Borgmann X-Patchwork-Id: 7329 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.78.2 with SMTP id r2csp7242929jaa; Thu, 18 Jan 2018 10:37:55 -0800 (PST) X-Google-Smtp-Source: ACJfBouQGhvlb73XMJHdSyO+qMUW8NDKgQu3DZ/O4fuqVBxxTNS5Bnf46hXE2jAuak/TQL/CNG9f X-Received: by 10.28.62.20 with SMTP id l20mr6176921wma.153.1516300675714; Thu, 18 Jan 2018 10:37:55 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1516300675; cv=none; d=google.com; s=arc-20160816; b=OxlGEShBiT8J9NuG/to6QmSG7SKkZ3kFsYyQPAAJ4MbpG1WlfBR8DaL0sRoijf+z2w KlyDSHovzrItYejncjxE1hP5+DjloK9f38i+ZZyglS1Nt16XqXm2IQZV8/AahIvlK2wS hpyCV1aH4VswHVKAb5ihQtzdAye8epL+sf0VROA6/QaMG/ZUY6jgQ8kHX8f6Ntl/2IO5 kR9fhyQM8ppHAEYQDC8ogr5ayrL9cFnxU+FUdG6D03RO8iJb7sRldQJjuNbIrP6xSSgt 0CnfAP39XAn8+yKopUZpAJGotoq8I8R4J3sSfflWP55hOvmG3ykJcoSYd1DNzhey6Og7 a0CQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:content-disposition:content-language:in-reply-to :mime-version:date:message-id:references:to:from:dkim-signature :delivered-to:arc-authentication-results; bh=iq1Nd6Y18NLZqK0pZn2gZ+SZUd6GqXs2sf0nez4+AdA=; b=BciKLAXrDaCyPCQvzbwF/stTy5ABFSBBuO62/vowBvBdx4GRKa1QeF18TbMtL/ondD eqHNPmSZMpUMXAiqf6qhLxdzdmPAIt7jpQoQfP4EvXq9YuhWiRLNmYfMaGHjn+QvFp52 fEEgV9W3nSJMUtVZs6gVDE38e/BRg8+x41jRluKYCsSiMjIGD3pnmkVsqsh0zFsrghpo jtoxrebm/8dRY4ipBMqhbtSTs4vdsQ2oulSoWPsxTi5p9519MRtnoJX2fKotdOB7bwxQ FcdC+4CHgtyJUVyYrJR67IEE6IPMVYN/9kUEYwJvo6K1Me8cNUUHzUypNUd2tjxcJ3sn AKEA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@mail.de header.s=mailde201610 header.b=VD7jaK0I; 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 y11si6499667wry.12.2018.01.18.10.37.55; Thu, 18 Jan 2018 10:37:55 -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=VD7jaK0I; 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 0FD8568A2AA; Thu, 18 Jan 2018 20:37:54 +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 667FA68A293 for ; Thu, 18 Jan 2018 20:37:47 +0200 (EET) Received: from postfix03.mail.de (postfix03.bt.mail.de [10.0.121.127]) by shout02.mail.de (Postfix) with ESMTP id 782B266CAC for ; Thu, 18 Jan 2018 19:37:47 +0100 (CET) Received: from smtp03.mail.de (smtp03.bt.mail.de [10.0.121.213]) by postfix03.mail.de (Postfix) with ESMTP id 60FA0405EC for ; Thu, 18 Jan 2018 19:37:47 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.de; s=mailde201610; t=1516300667; bh=jH6f/FsPZS4cHr0SrEcL/PBYnJotAdRU6f/davoggh8=; h=Subject:From:To:Reply-To:References:Date:In-Reply-To:From; b=VD7jaK0IlrJTwkOr4Jpa/EJZ0QqMAqk8Teh0CjNVETwB/b3XFFV3cm6gV9artDlJy 4qc0Hk5eOYl7PlDEEvG9WQHKrT3ORdj7a62MicjFpqrG83h4HikxSsXKWW96e02uiu upTVbpwqTtDzIxC1PPdsbiDnToW6CxQNuWoZDVZc= 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 0D6F28016D for ; Thu, 18 Jan 2018 19:37:46 +0100 (CET) From: Thilo Borgmann To: FFmpeg development discussions and patches References: Message-ID: <76fd6b26-c177-5d0c-1371-4b2754e4c43d@mail.de> Date: Thu, 18 Jan 2018 19:37:54 +0100 MIME-Version: 1.0 In-Reply-To: Content-Language: en-US Content-Disposition: attachment; filename="0002-Add-codec-wrapper-for-librv11.patch" X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: [FFmpeg-devel] [PATCH 2/3] 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" From 0386c9e0a4c2ea1579378807ff5a7a04c508c50e Mon Sep 17 00:00:00 2001 From: Thilo Borgmann Date: Wed, 17 Jan 2018 23:13:53 +0100 Subject: [PATCH 2/3] Add codec wrapper for librv11 --- MAINTAINERS | 1 + configure | 8 + libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 2 + libavcodec/avcodec.h | 1 + libavcodec/codec_desc.c | 7 + libavcodec/librv11.h | 55 ++++ libavcodec/librv11dec.c | 367 ++++++++++++++++++++++++ libavcodec/librv11enc.c | 735 ++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/version.h | 2 +- 10 files changed, 1179 insertions(+), 1 deletion(-) 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 e583926..0067986 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 5d53362..1c27852 100755 --- a/configure +++ b/configure @@ -250,6 +250,8 @@ 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] + --disable-librv11dec enable RV11 decoding via rv11 [autodetect] + --disable-librv11enc enable RV11 encoding via rv11 [autodetect] --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] @@ -1535,6 +1537,8 @@ EXTERNAL_AUTODETECT_LIBRARY_LIST=" bzlib coreimage iconv + librv11enc + librv11dec libxcb libxcb_shm libxcb_shape @@ -2959,6 +2963,8 @@ libopus_decoder_deps="libopus" libopus_encoder_deps="libopus" libopus_encoder_select="audio_frame_queue" librsvg_decoder_deps="librsvg" +librv11dec_decoder_deps="librv11dec" +librv11enc_encoder_deps="librv11enc" libshine_encoder_deps="libshine" libshine_encoder_select="audio_frame_queue" libspeex_decoder_deps="libspeex" @@ -5890,6 +5896,8 @@ 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 librv11enc && require_pkg_config librv11 librv11 librv11_sdk.h fpinit +enabled librv11dec && require_pkg_config librv11 librv11 librv11_sdk.h fpinit 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/libavcodec/Makefile b/libavcodec/Makefile index cfacd6b..2e32815 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_LIBRV11DEC_DECODER) += librv11dec.o +OBJS-$(CONFIG_LIBRV11ENC_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..8eb8610 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -548,6 +548,8 @@ static void register_all(void) REGISTER_ENCDEC (LIBOPENJPEG, libopenjpeg); REGISTER_ENCDEC (LIBOPUS, libopus); REGISTER_DECODER(LIBRSVG, librsvg); + REGISTER_DECODER(LIBRV11DEC, librv11dec); + REGISTER_ENCODER(LIBRV11ENC, librv11enc); 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..44ec050 --- /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..269d684 --- /dev/null +++ b/libavcodec/librv11dec.c @@ -0,0 +1,367 @@ +/* + * 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 DecFnTable +{ + FPINIT init; + FPFREE free; + FPTRANSFORM transform; + FPCUSTOMMSG custom_message; + FPHIVEMSG hive_message; +} DecFnTable; + +typedef struct LIBRV11DecContext { + AVCodecContext *avctx; + AVFrame out_frame; + void *lib_handle; + DecFnTable *symbols; + 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_load_symbols(AVCodecContext *avctx) +{ + LIBRV11DecContext *ctx = avctx->priv_data; + + ctx->lib_handle = dlopen(LIBRV11DEC_FILE, RTLD_LAZY); + if (!ctx->lib_handle) { + av_log(avctx, AV_LOG_ERROR, "Cannot load dynamic library.\n"); + return AVERROR_EXTERNAL; + } + + ctx->symbols = av_mallocz(sizeof(DecFnTable)); + if (!ctx->symbols) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate symbol table.\n"); + return AVERROR(ENOMEM); + } + +#define LOAD_RV11_FUNC(handle, name) { \ + ctx->symbols->handle = dlsym(ctx->lib_handle, name); \ + if (!ctx->symbols->handle) { \ + av_log(avctx, AV_LOG_ERROR, "Cannot load symbol %s.", name); \ + return AVERROR_EXTERNAL; \ + } \ +} + + LOAD_RV11_FUNC(init, RV_CODEC_INIT); + LOAD_RV11_FUNC(free, RV_CODEC_FREE); + LOAD_RV11_FUNC(transform, RV_CODEC_TRANSFORM); + LOAD_RV11_FUNC(custom_message, RV_CODEC_CUSTOM_MSG); + LOAD_RV11_FUNC(hive_message, RV_CODEC_HIVE_MSG); + + return 0; +} + +static av_cold int librv11dec_init(AVCodecContext *avctx) +{ + LIBRV11DecContext *ctx = avctx->priv_data; + GetBitContext gb; + 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; + } + + init_get_bits(&gb, avctx->extradata, avctx->extradata_size * 8); + + SPO_extra_flags = get_bits_long(&gb, 32); + version = get_bits_long(&gb, 32); + + res = librv11dec_load_symbols(avctx); + if (res) { + av_freep(&ctx->symbols); + return res; + } + + 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 = ctx->symbols->init(&init_params, &ctx->codec_status); + if (res) { + av_free(ctx->symbols); + return res; + } + + msg.id = RV_MSG_ID_SMOOTHING_POSTFILTER; + msg.value1 = RV_MSG_DISABLE; + msg.value2 = 0; + + res = ctx->symbols->custom_message(&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 = ctx->symbols->transform(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; +#if FF_API_PKT_PTS +FF_DISABLE_DEPRECATION_WARNINGS + pict->pkt_pts = avpkt->pts; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + 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 = ctx->symbols->transform((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; +#if FF_API_PKT_PTS +FF_DISABLE_DEPRECATION_WARNINGS + pict->pkt_pts = avpkt->pts; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + 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; + + ctx->symbols->free(ctx->codec_status); + dlclose(ctx->lib_handle); + + if (ctx->symbols) { + av_freep(&ctx->symbols); + } + + 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; + if (ctx->symbols) { + uint32_t id = RV_MSG_ID_FLUSH; + ctx->symbols->hive_message(&id, ctx->codec_status); + } +} + +AVCodec ff_librv11dec_decoder = { + .name = "librv11dec", + .long_name = NULL_IF_CONFIG_SMALL("librv11dec 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..231a768 --- /dev/null +++ b/libavcodec/librv11enc.c @@ -0,0 +1,735 @@ +/* + * 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 EncFnTable +{ + FPCODECOPEN Codec_Open; + FPCODECCLOSE Codec_Close; + FPCODECINPUT Codec_Input; + FPCODECSTREAMOPEN Codec_StreamOpen; + FPCODECGETHYPERPROPERTY Codec_GetHyperProperty; + FPSTREAMCLOSE Stream_Close; + FPSTREAMSETDATACALLBACK Stream_SetDataCallback; + FPSTREAMGETSTREAMHEADER Stream_GetStreamHeader; + FPSTREAMINPUT Stream_Input; + FPSTREAMSETOUTPUTPACKETSIZE Stream_SetOutputPacketSize; + FPSTREAMGETPROPERTY Stream_GetProperty; + FPSTREAMSETPROPERTY Stream_SetProperty; + FPSTREAMPOSTPROCESS Stream_PostProcess; + FPSTREAMRELEASEFRAME Stream_ReleaseFrame; + FPCODECGETUINAME Codec_GetUIName; + FPCODECQUERYMEDIAFORMAT Codec_QueryMediaFormat; + FPCODECSETPROPERTY Codec_SetProperty; +} EncFnTable; + +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 *lib_handle; + EncFnTable *symbols; + 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 + FrameRatioConverter fr_convert; + uint32_t video_mode; +} 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; + 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; + } + + node->pkt.pts = data->timestamp * av_q2d(av_inv_q(avctx->time_base)) / 1000.0; + node->pkt.dts = data->timestamp; + coded_frame_add((void*)(&enctx->coded_frame_list), node); + } + + return 0; +} + +static av_cold int librv11enc_load_symbols(AVCodecContext *avctx) +{ + LIBRV11EncContext *enctx = avctx->priv_data; + + enctx->lib_handle = dlopen(LIBRV11ENC_FILE, RTLD_LAZY); + if (!enctx->lib_handle) { + av_log(avctx, AV_LOG_ERROR, "Cannot load dynamic library.\n"); + return AVERROR_EXTERNAL; + } + + enctx->symbols = av_mallocz(sizeof(EncFnTable)); + if (!enctx->symbols) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate symbol table.\n"); + return AVERROR(ENOMEM); + } + +#define LOAD_RV11_FUNC(handle, name) { \ + enctx->symbols->handle = dlsym(enctx->lib_handle, name); \ + if (!enctx->symbols->handle) { \ + av_log(avctx, AV_LOG_ERROR, "Cannot load symbol %s.", name); \ + return AVERROR_EXTERNAL; \ + } \ +} + + LOAD_RV11_FUNC(Codec_Open, RV_CODEC_OPEN); + LOAD_RV11_FUNC(Codec_Close, RV_CODEC_CLOSE); + LOAD_RV11_FUNC(Codec_StreamOpen, RV_STREAM_OPEN); + LOAD_RV11_FUNC(Codec_GetHyperProperty, RV_CODEC_GET_HYPER_PROPERTY); + LOAD_RV11_FUNC(Stream_Close, RV_STREAM_CLOSE); + LOAD_RV11_FUNC(Stream_SetDataCallback, RV_STREAM_SET_DATACALLBACK); + LOAD_RV11_FUNC(Stream_GetStreamHeader, RV_STREAM_GET_STREAMHEADER); + LOAD_RV11_FUNC(Stream_Input, RV_STREAM_INPUT); + LOAD_RV11_FUNC(Stream_SetOutputPacketSize, RV_STREAM_SET_OUTPUTPACKETSIZE); + LOAD_RV11_FUNC(Stream_GetProperty, RV_STREAM_GET_PROPERTY); + LOAD_RV11_FUNC(Stream_SetProperty, RV_STREAM_SET_PROPERTY); + LOAD_RV11_FUNC(Codec_GetUIName, RV_CODEC_GET_UINAME); + LOAD_RV11_FUNC(Codec_Input, RV_CODEC_INPUT); + LOAD_RV11_FUNC(Codec_QueryMediaFormat, RV_CODEC_QUERY_MEDIAFORMAT); + LOAD_RV11_FUNC(Codec_SetProperty, RV_CODEC_SET_PROPERTY); + + 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 = librv11enc_load_symbols(avctx); + if (res) { + av_freep(&enctx->symbols); + return res; + } + + res = enctx->symbols->Codec_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 = enctx->symbols->Codec_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 = enctx->symbols->Stream_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 = enctx->symbols->Codec_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); + SET_RV11_PROP(SP_QUALITY_MOTION, &enctx->video_mode); + + 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); + SET_RV11_PROP(SP_RESIZING_ACCURACY, &enctx->rvenc_param.resize_quality); + + if (enctx->rvenc_param.pon > 0) { + SET_RV11_PROP(SP_SET_PON_START, &enctx->rvenc_param.pon); + } + + res = enctx->symbols->Stream_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 = enctx->symbols->Stream_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); + } + +#if FF_API_CODED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + avctx->coded_frame = av_frame_alloc(); + if (!avctx->coded_frame) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate coded frame buffer."); + return AVERROR(ENOMEM); + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + 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; + enctx->symbols->Codec_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 * 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 = enctx->symbols->Codec_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 = enctx->symbols->Codec_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; +#if FF_API_CODED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + } + 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; + + enctx->symbols->Stream_Close(enctx->stream_ref); + enctx->symbols->Codec_Close(enctx->codec_ref); + enctx->stream_ref = NULL; + enctx->codec_ref = NULL; + + dlclose(enctx->lib_handle); + free_frame_list(enctx->coded_frame_list); + + av_freep(&enctx->frame_buf); + av_freep(&enctx->symbols); + av_freep(&enctx->fr_convert.selected_map); + av_freep(&avctx->extradata); + +#if FF_API_CODED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + av_frame_free(&avctx->coded_frame); +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + 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 }, + { "resize_quality", "video encoded frame resize quality", OFFSETBASE+OFFSETP(resize_quality), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE, "resize_quality" }, + { "high", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "resize_quality" }, + { "fast", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "resize_quality" }, + { "video_mode", "motion quality", OFFSET(video_mode), AV_OPT_TYPE_INT, { .i64 = 50 }, 1, 100, VE, "video_mode" }, + { "normal", "", 0, AV_OPT_TYPE_CONST, {.i64= 50}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "video_mode" }, + { "sharp", "", 0, AV_OPT_TYPE_CONST, {.i64= 1}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "video_mode" }, + { "smooth", "", 0, AV_OPT_TYPE_CONST, {.i64=100}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "video_mode" }, + { "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" }, + { "cpulow", "cpu low mode on/off", OFFSETBASE+OFFSETP(cpu_low), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, + { NULL }, +}; + +static const AVClass librv11enc_class = { + .class_name = "librv11enc", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_librv11enc_encoder = { + .name = "librv11enc", + .long_name = NULL_IF_CONFIG_SMALL("librv11enc 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 = &librv11enc_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, \