From patchwork Mon Nov 9 12:02:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul B Mahol X-Patchwork-Id: 23440 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 2466844B262 for ; Mon, 9 Nov 2020 14:02:56 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id EA0ED68BCC1; Mon, 9 Nov 2020 14:02:55 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ed1-f43.google.com (mail-ed1-f43.google.com [209.85.208.43]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 575D368B90D for ; Mon, 9 Nov 2020 14:02:50 +0200 (EET) Received: by mail-ed1-f43.google.com with SMTP id v4so8456897edi.0 for ; Mon, 09 Nov 2020 04:02:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:subject:date:message-id; bh=qG3rTuKtDFX8OOLEb61WfQL5+W5SZ1xa6ZLlPRNbJko=; b=qGtmIp1sjjlaJ+PReKT5UV7qDl4pJrvbIwNysXB6IYjLFKv5nbfnx6vh6IePVLOqoQ 1/b5xoM5UIgJI2+/woSjXu5QFk2m7s3CV8ATMA93303HDwfqf7y3Q03kteIl2TIFzPT+ vR3AacFeYp8fkLkHhaa1UQBrRYBw7S8Q20HevJG/ZsP+isqVSWqQ4mPNBCjs9QLAdV+q uqtCA8JD0l/A9XQLSsBplzbg+2kK23l5UjlccIJM3M27duOuqJgYK3oxWh0WfqajvxvG 6mYvBegVrMmhj3KDTiPotXKgHMR07rNKvaw0i+uiRd3JNx78VIyO6uD04KYx62v0V6i/ el8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id; bh=qG3rTuKtDFX8OOLEb61WfQL5+W5SZ1xa6ZLlPRNbJko=; b=I3UG8zzbh59/JltICfACgXsOjW/I9oJ/KNnA5PIJx3msNuV3b4MeXVNWHkeWt/mteY agaLjMloKvQn7uipzss+LhPKf4oBbyVFAsGGh5AaUm+1ckIqLfd9hX66XkNxnvOTY6MC ATXaGPwovq27p7RomJvINHQNP9AhBS6+e/6jluo3DZIRzDCPl2rQCG2AhaenopeS4GpW t/UNtgVdcQAHS/JlUdVw3P+gLnAN6Sxo5+PnMU72iTTbTq9MtHXzJa4nwnvARX3AcPhh abkOZmj5mBE8SNdWYJ7t8y9LPApq63JnDc664CX4Nlxv36nfnwmO1o365PyyOeHAjTZP GMIg== X-Gm-Message-State: AOAM533s1AQSMHpbGVus39lqVxK0gaFBw9r9plVwuA9e/RK+q1OcjqbM F2iOhVw45Ma4aaHLdOvMXZe/bntqpMt1qA== X-Google-Smtp-Source: ABdhPJxC6U0JrVx5UrH+xMYKF/0lFIkc99x3erJftR+IReFdpiklr/eOYEoXPRgJaEqp++1I2Hd1cg== X-Received: by 2002:a50:c945:: with SMTP id p5mr14784624edh.55.1604923368961; Mon, 09 Nov 2020 04:02:48 -0800 (PST) Received: from localhost.localdomain ([77.237.107.67]) by smtp.gmail.com with ESMTPSA id q9sm8650507ejd.66.2020.11.09.04.02.47 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 09 Nov 2020 04:02:48 -0800 (PST) From: Paul B Mahol To: ffmpeg-devel@ffmpeg.org Date: Mon, 9 Nov 2020 13:02:36 +0100 Message-Id: <20201109120236.8990-1-onemda@gmail.com> X-Mailer: git-send-email 2.17.1 Subject: [FFmpeg-devel] [PATCH] avcodec: add SpeedHQ encoder 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 MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Jean-Baptiste Kempf Signed-off-by: Paul B Mahol --- Changelog | 1 + doc/general_contents.texi | 2 +- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/mpegutils.h | 1 + libavcodec/mpegvideo.h | 5 +- libavcodec/mpegvideo_enc.c | 53 +++++- libavcodec/speedhq.c | 8 +- libavcodec/speedhqenc.c | 322 +++++++++++++++++++++++++++++++++++++ libavcodec/speedhqenc.h | 48 ++++++ 10 files changed, 433 insertions(+), 9 deletions(-) create mode 100644 libavcodec/speedhqenc.c create mode 100644 libavcodec/speedhqenc.h diff --git a/Changelog b/Changelog index 3aeada17a4..b200082630 100644 --- a/Changelog +++ b/Changelog @@ -43,6 +43,7 @@ version : - adenorm filter - ADPCM IMA AMV encoder - AMV muxer +- SpeedHQ encoder version 4.3: diff --git a/doc/general_contents.texi b/doc/general_contents.texi index 7033816b5a..1be6f9b683 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -972,7 +972,7 @@ following image formats are supported: @item MPEG-4 part 2 Microsoft variant version 1 @tab @tab X @item MPEG-4 part 2 Microsoft variant version 2 @tab X @tab X @item MPEG-4 part 2 Microsoft variant version 3 @tab X @tab X -@item Newtek SpeedHQ @tab @tab X +@item Newtek SpeedHQ @tab X @tab X @item Nintendo Gamecube THP video @tab @tab X @item NotchLC @tab @tab X @item NuppelVideo/RTjpeg @tab @tab X diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9d75dd68af..eed2a34e07 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -625,6 +625,7 @@ OBJS-$(CONFIG_SONIC_DECODER) += sonic.o OBJS-$(CONFIG_SONIC_ENCODER) += sonic.o OBJS-$(CONFIG_SONIC_LS_ENCODER) += sonic.o OBJS-$(CONFIG_SPEEDHQ_DECODER) += speedhq.o mpeg12.o mpeg12data.o simple_idct.o +OBJS-$(CONFIG_SPEEDHQ_ENCODER) += speedhq.o mpeg12data.o speedhqenc.o OBJS-$(CONFIG_SP5X_DECODER) += sp5xdec.o OBJS-$(CONFIG_SRGC_DECODER) += mscc.o OBJS-$(CONFIG_SRT_DECODER) += srtdec.o ass.o htmlsubtitles.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 8bdc0d6bf7..774d5670bf 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -297,6 +297,7 @@ extern AVCodec ff_snow_encoder; extern AVCodec ff_snow_decoder; extern AVCodec ff_sp5x_decoder; extern AVCodec ff_speedhq_decoder; +extern AVCodec ff_speedhq_encoder; extern AVCodec ff_srgc_decoder; extern AVCodec ff_sunrast_encoder; extern AVCodec ff_sunrast_decoder; diff --git a/libavcodec/mpegutils.h b/libavcodec/mpegutils.h index 1ed21c19be..81f0b73bb1 100644 --- a/libavcodec/mpegutils.h +++ b/libavcodec/mpegutils.h @@ -125,6 +125,7 @@ enum OutputFormat { FMT_H261, FMT_H263, FMT_MJPEG, + FMT_SPEEDHQ, }; diff --git a/libavcodec/mpegvideo.h b/libavcodec/mpegvideo.h index 29e692f245..cdeadef4cf 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -444,7 +444,10 @@ typedef struct MpegEncContext { int inter_intra_pred; int mspel; - /* decompression specific */ + /* SpeedHQ specific */ + int slice_start; + + /* decompression specific */ GetBitContext gb; /* MPEG-1 specific */ diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c index f16b26c9f2..7afc789ec0 100644 --- a/libavcodec/mpegvideo_enc.c +++ b/libavcodec/mpegvideo_enc.c @@ -51,6 +51,7 @@ #include "mathops.h" #include "mpegutils.h" #include "mjpegenc.h" +#include "speedhqenc.h" #include "msmpeg4.h" #include "pixblockdsp.h" #include "qpeldsp.h" @@ -326,6 +327,15 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) return AVERROR(EINVAL); } break; + case AV_CODEC_ID_SPEEDHQ: + if (avctx->pix_fmt != AV_PIX_FMT_YUV420P && + avctx->pix_fmt != AV_PIX_FMT_YUV422P && + avctx->pix_fmt != AV_PIX_FMT_YUV444P) { + av_log(avctx, AV_LOG_ERROR, + "only YUV420/YUV422/YUV444 are supported (no alpha support yet)\n"); + return AVERROR(EINVAL); + } + break; default: if (avctx->pix_fmt != AV_PIX_FMT_YUV420P) { av_log(avctx, AV_LOG_ERROR, "only YUV420 is supported\n"); @@ -732,7 +742,7 @@ FF_ENABLE_DEPRECATION_WARNINGS //return -1; } - if (s->mpeg_quant || s->codec_id == AV_CODEC_ID_MPEG1VIDEO || s->codec_id == AV_CODEC_ID_MPEG2VIDEO || s->codec_id == AV_CODEC_ID_MJPEG || s->codec_id==AV_CODEC_ID_AMV) { + if (s->mpeg_quant || s->codec_id == AV_CODEC_ID_MPEG1VIDEO || s->codec_id == AV_CODEC_ID_MPEG2VIDEO || s->codec_id == AV_CODEC_ID_MJPEG || s->codec_id == AV_CODEC_ID_AMV || s->codec_id == AV_CODEC_ID_SPEEDHQ) { // (a + x * 3 / 8) / x s->intra_quant_bias = 3 << (QUANT_BIAS_SHIFT - 3); s->inter_quant_bias = 0; @@ -783,6 +793,16 @@ FF_ENABLE_DEPRECATION_WARNINGS avctx->delay = 0; s->low_delay = 1; break; + case AV_CODEC_ID_SPEEDHQ: + s->out_format = FMT_SPEEDHQ; + s->intra_only = 1; /* force intra only for SHQ */ + if (!CONFIG_SPEEDHQ_ENCODER) + return AVERROR_ENCODER_NOT_FOUND; + if ((ret = ff_speedhq_encode_init(s)) < 0) + return ret; + avctx->delay = 0; + s->low_delay = 1; + break; case AV_CODEC_ID_H261: if (!CONFIG_H261_ENCODER) return AVERROR_ENCODER_NOT_FOUND; @@ -997,6 +1017,9 @@ FF_ENABLE_DEPRECATION_WARNINGS } else if (s->out_format == FMT_H263 || s->out_format == FMT_H261) { s->intra_matrix[j] = s->inter_matrix[j] = ff_mpeg1_default_non_intra_matrix[i]; + } else if (CONFIG_SPEEDHQ_ENCODER && s->codec_id == AV_CODEC_ID_SPEEDHQ) { + s->intra_matrix[j] = + s->inter_matrix[j] = ff_mpeg1_default_intra_matrix[i]; } else { /* MPEG-1/2 */ s->chroma_intra_matrix[j] = @@ -2562,6 +2585,10 @@ static av_always_inline void encode_mb_internal(MpegEncContext *s, if (CONFIG_MJPEG_ENCODER) ff_mjpeg_encode_mb(s, s->block); break; + case AV_CODEC_ID_SPEEDHQ: + if (CONFIG_SPEEDHQ_ENCODER) + ff_speedhq_encode_mb(s, s->block); + break; default: av_assert1(0); } @@ -2816,6 +2843,8 @@ static void write_slice_end(MpegEncContext *s){ ff_mpeg4_stuffing(&s->pb); }else if(CONFIG_MJPEG_ENCODER && s->out_format == FMT_MJPEG){ ff_mjpeg_encode_stuffing(s); + } else if (CONFIG_SPEEDHQ_ENCODER && s->out_format == FMT_SPEEDHQ) { + ff_speedhq_end_slice(s); } flush_put_bits(&s->pb); @@ -2905,7 +2934,7 @@ int ff_mpv_reallocate_putbitbuffer(MpegEncContext *s, size_t threshold, size_t s static int encode_thread(AVCodecContext *c, void *arg){ MpegEncContext *s= *(void**)arg; - int mb_x, mb_y; + int mb_x, mb_y, mb_y_order; int chr_h= 16>>s->chroma_y_shift; int i, j; MpegEncContext best_s = { 0 }, backup_s; @@ -2966,7 +2995,16 @@ static int encode_thread(AVCodecContext *c, void *arg){ s->resync_mb_y=0; s->first_slice_line = 1; s->ptr_lastgob = s->pb.buf; - for(mb_y= s->start_mb_y; mb_y < s->end_mb_y; mb_y++) { + for (mb_y_order = s->start_mb_y; mb_y_order < s->end_mb_y; mb_y_order++) { + if (s->codec_id == AV_CODEC_ID_SPEEDHQ) { + int first_in_slice; + mb_y = ff_speedhq_mb_y_order_to_mb(mb_y_order, s->mb_height, &first_in_slice); + if (first_in_slice && mb_y_order != s->start_mb_y) + ff_speedhq_end_slice(s); + s->last_dc[0] = s->last_dc[1] = s->last_dc[2] = 1024 << s->intra_dc_precision; + } else { + mb_y = mb_y_order; + } s->mb_x=0; s->mb_y= mb_y; @@ -3855,6 +3893,11 @@ static int encode_picture(MpegEncContext *s, int picture_number) s->qscale= 8; } + if (s->out_format == FMT_SPEEDHQ) { + s->y_dc_scale_table= + s->c_dc_scale_table= ff_mpeg2_dc_scale_table[3]; + } + //FIXME var duplication s->current_picture_ptr->f->key_frame = s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; //FIXME pic_ptr @@ -3872,6 +3915,10 @@ static int encode_picture(MpegEncContext *s, int picture_number) ff_mjpeg_encode_picture_header(s->avctx, &s->pb, &s->intra_scantable, s->pred, s->intra_matrix, s->chroma_intra_matrix); break; + case FMT_SPEEDHQ: + if (CONFIG_SPEEDHQ_ENCODER) + ff_speedhq_encode_picture_header(s); + break; case FMT_H261: if (CONFIG_H261_ENCODER) ff_h261_encode_picture_header(s, picture_number); diff --git a/libavcodec/speedhq.c b/libavcodec/speedhq.c index 30a6924321..748e8617f6 100644 --- a/libavcodec/speedhq.c +++ b/libavcodec/speedhq.c @@ -132,7 +132,7 @@ static const uint8_t speedhq_run[121] = { 31, }; -static RLTable rl_speedhq = { +RLTable ff_rl_speedhq = { 121, 121, (const uint16_t (*)[])speedhq_vlc, @@ -236,7 +236,7 @@ static inline int decode_dct_block(const SHQContext *s, GetBitContext *gb, int l for ( ;; ) { int level, run; UPDATE_CACHE_LE(re, gb); - GET_RL_VLC(level, run, re, gb, rl_speedhq.rl_vlc[0], + GET_RL_VLC(level, run, re, gb, ff_rl_speedhq.rl_vlc[0], TEX_VLC_BITS, 2, 0); if (level == 127) { break; @@ -570,8 +570,8 @@ static av_cold void speedhq_static_init(void) ff_mpeg12_vlc_dc_chroma_code, 2, 2, INIT_VLC_OUTPUT_LE, 514); - ff_rl_init(&rl_speedhq, speedhq_static_rl_table_store); - INIT_2D_VLC_RL(rl_speedhq, 674, INIT_VLC_LE); + ff_rl_init(&ff_rl_speedhq, speedhq_static_rl_table_store); + INIT_2D_VLC_RL(ff_rl_speedhq, 674, INIT_VLC_LE); compute_alpha_vlcs(); } diff --git a/libavcodec/speedhqenc.c b/libavcodec/speedhqenc.c new file mode 100644 index 0000000000..ec4c6dc9d6 --- /dev/null +++ b/libavcodec/speedhqenc.c @@ -0,0 +1,322 @@ +/* + * SpeedHQ encoder + * Copyright (c) 2000, 2001 Fabrice Bellard + * Copyright (c) 2003 Alex Beregszaszi + * Copyright (c) 2003-2004 Michael Niedermayer + * Copyright (c) 2020 FFmpeg + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * SpeedHQ encoder. + */ + +#include "libavutil/pixdesc.h" +#include "libavutil/thread.h" + +#include "avcodec.h" +#include "mpeg12.h" +#include "mpegvideo.h" +#include "speedhqenc.h" + +extern RLTable ff_rl_speedhq; +static uint8_t ff_speedhq_static_rl_table_store[2][2*MAX_RUN + MAX_LEVEL + 3]; + +static uint16_t ff_mpeg12_vlc_dc_lum_code_reversed[12]; +static uint16_t ff_mpeg12_vlc_dc_chroma_code_reversed[12]; + +/* simple include everything table for dc, first byte is bits + * number next 3 are code */ +static uint32_t speedhq_lum_dc_uni[512]; +static uint32_t speedhq_chr_dc_uni[512]; + +static uint8_t speedhq_index_run[2][64]; +static int8_t speedhq_max_level[2][64]; + +static uint8_t uni_speedhq_ac_vlc_len[64 * 64 * 2]; + +static uint32_t reverse(uint32_t num, int bits) +{ + return bitswap_32(num) >> (32 - bits); +} + +static void reverse_code(const uint16_t *code, const uint8_t *bits, + uint16_t *reversed_code, int num_entries) +{ + for (int i = 0; i < num_entries; i++) + reversed_code[i] = reverse(code[i], bits[i]); +} + +static av_cold void speedhq_init_static_data(void) +{ + /* Exactly the same as MPEG-2, except little-endian. */ + reverse_code(ff_mpeg12_vlc_dc_lum_code, + ff_mpeg12_vlc_dc_lum_bits, + ff_mpeg12_vlc_dc_lum_code_reversed, + 12); + reverse_code(ff_mpeg12_vlc_dc_chroma_code, + ff_mpeg12_vlc_dc_chroma_bits, + ff_mpeg12_vlc_dc_chroma_code_reversed, + 12); + + ff_rl_init(&ff_rl_speedhq, ff_speedhq_static_rl_table_store); + + for (int i = 0; i < 64; i++) { + speedhq_max_level[0][i] = ff_rl_speedhq.max_level[0][i]; + speedhq_index_run[0][i] = ff_rl_speedhq.index_run[0][i]; + } + + /* build unified dc encoding tables */ + for (int i = -255; i < 256; i++) { + int adiff, index; + int bits, code; + int diff = i; + + adiff = FFABS(diff); + if (diff < 0) + diff--; + index = av_log2(2 * adiff); + + bits = ff_mpeg12_vlc_dc_lum_bits[index] + index; + code = ff_mpeg12_vlc_dc_lum_code_reversed[index] + + (av_mod_uintp2(diff, index) << ff_mpeg12_vlc_dc_lum_bits[index]); + speedhq_lum_dc_uni[i + 255] = bits + (code << 8); + + bits = ff_mpeg12_vlc_dc_chroma_bits[index] + index; + code = ff_mpeg12_vlc_dc_chroma_code_reversed[index] + + (av_mod_uintp2(diff, index) << ff_mpeg12_vlc_dc_chroma_bits[index]); + speedhq_chr_dc_uni[i + 255] = bits + (code << 8); + } + + ff_init_uni_ac_vlc(&ff_rl_speedhq, uni_speedhq_ac_vlc_len); +} + +av_cold int ff_speedhq_encode_init(MpegEncContext *s) +{ + static AVOnce init_static_once = AV_ONCE_INIT; + + av_assert0(s->slice_context_count == 1); + + if (s->width > 65500 || s->height > 65500) { + av_log(s, AV_LOG_ERROR, "SpeedHQ does not support resolutions above 65500x65500\n"); + return AVERROR(EINVAL); + } + + s->min_qcoeff = -2048; + s->max_qcoeff = 2047; + + ff_thread_once(&init_static_once, speedhq_init_static_data); + + s->intra_ac_vlc_length = + s->intra_ac_vlc_last_length = + s->intra_chroma_ac_vlc_length = + s->intra_chroma_ac_vlc_last_length = uni_speedhq_ac_vlc_len; + + switch (s->avctx->pix_fmt) { + case AV_PIX_FMT_YUV420P: + s->avctx->codec_tag = MKTAG('S','H','Q','0'); + break; + case AV_PIX_FMT_YUV422P: + s->avctx->codec_tag = MKTAG('S','H','Q','2'); + break; + case AV_PIX_FMT_YUV444P: + s->avctx->codec_tag = MKTAG('S','H','Q','4'); + break; + default: + av_assert0(0); + } + + return 0; +} + +void ff_speedhq_encode_picture_header(MpegEncContext *s) +{ + avpriv_align_put_bits(&s->pb); + put_bits_le(&s->pb, 8, 100 - s->qscale * 2); /* FIXME why doubled */ + put_bits_le(&s->pb, 24, 4); /* no second field */ + + /* length of first slice, will be filled out later */ + s->slice_start = 4; + put_bits_le(&s->pb, 24, 0); +} + +void ff_speedhq_end_slice(MpegEncContext *s) +{ + int slice_len; + + flush_put_bits_le(&s->pb); + slice_len = s->pb.buf_ptr - (s->pb.buf + s->slice_start); + AV_WL24(s->pb.buf + s->slice_start, slice_len); + + /* length of next slice, will be filled out later */ + s->slice_start = s->pb.buf_ptr - s->pb.buf; + put_bits_le(&s->pb, 24, 0); +} + +static inline void encode_dc(PutBitContext *pb, int diff, int component) +{ + unsigned int diff_u = diff + 255; + if (diff_u >= 511) { + int index; + + if (diff < 0) { + index = av_log2_16bit(-2 * diff); + diff--; + } else { + index = av_log2_16bit(2 * diff); + } + if (component == 0) + put_bits_le(pb, + ff_mpeg12_vlc_dc_lum_bits[index] + index, + ff_mpeg12_vlc_dc_lum_code_reversed[index] + + (av_mod_uintp2(diff, index) << ff_mpeg12_vlc_dc_lum_bits[index])); + else + put_bits_le(pb, + ff_mpeg12_vlc_dc_chroma_bits[index] + index, + ff_mpeg12_vlc_dc_chroma_code_reversed[index] + + (av_mod_uintp2(diff, index) << ff_mpeg12_vlc_dc_chroma_bits[index])); + } else { + if (component == 0) + put_bits_le(pb, + speedhq_lum_dc_uni[diff + 255] & 0xFF, + speedhq_lum_dc_uni[diff + 255] >> 8); + else + put_bits_le(pb, + speedhq_chr_dc_uni[diff + 255] & 0xFF, + speedhq_chr_dc_uni[diff + 255] >> 8); + } +} + +static void encode_block(MpegEncContext *s, int16_t *block, int n) +{ + int alevel, level, last_non_zero, dc, i, j, run, last_index, sign; + int code; + int component, val; + + /* DC coef */ + component = (n <= 3 ? 0 : (n&1) + 1); + dc = block[0]; /* overflow is impossible */ + val = s->last_dc[component] - dc; /* opposite of most codecs */ + encode_dc(&s->pb, val, component); + s->last_dc[component] = dc; + + /* now quantify & encode AC coefs */ + last_non_zero = 0; + last_index = s->block_last_index[n]; + + for (i = 1; i <= last_index; i++) { + j = s->intra_scantable.permutated[i]; + level = block[j]; + + /* encode using VLC */ + if (level != 0) { + run = i - last_non_zero - 1; + + alevel = level; + MASK_ABS(sign, alevel); + sign &= 1; + + if (alevel <= speedhq_max_level[0][run]) { + code = speedhq_index_run[0][run] + alevel - 1; + /* store the VLC & sign at once */ + put_bits_le(&s->pb, ff_rl_speedhq.table_vlc[code][1] + 1, + ff_rl_speedhq.table_vlc[code][0] + (sign << ff_rl_speedhq.table_vlc[code][1])); + } else { + /* escape seems to be pretty rare <5% so I do not optimize it */ + put_bits_le(&s->pb, ff_rl_speedhq.table_vlc[121][1], ff_rl_speedhq.table_vlc[121][0]); + /* escape: only clip in this case */ + put_bits_le(&s->pb, 6, run); + put_bits_le(&s->pb, 12, level + 2048); + } + last_non_zero = i; + } + } + /* end of block */ + put_bits_le(&s->pb, ff_rl_speedhq.table_vlc[122][1], ff_rl_speedhq.table_vlc[122][0]); +} + +void ff_speedhq_encode_mb(MpegEncContext *s, int16_t block[12][64]) +{ + int i; + for(i=0;i<6;i++) { + encode_block(s, block[i], i); + } + if (s->chroma_format == CHROMA_444) { + encode_block(s, block[8], 8); + encode_block(s, block[9], 9); + + encode_block(s, block[6], 6); + encode_block(s, block[7], 7); + + encode_block(s, block[10], 10); + encode_block(s, block[11], 11); + } else if (s->chroma_format == CHROMA_422) { + encode_block(s, block[6], 6); + encode_block(s, block[7], 7); + } + + s->i_tex_bits += get_bits_diff(s); +} + +static int ff_speedhq_mb_rows_in_slice(int slice_num, int mb_height) +{ + return mb_height / 4 + (slice_num < (mb_height % 4)); +} + +int ff_speedhq_mb_y_order_to_mb(int mb_y_order, int mb_height, int *first_in_slice) +{ + int slice_num = 0; + while (mb_y_order >= ff_speedhq_mb_rows_in_slice(slice_num, mb_height)) { + mb_y_order -= ff_speedhq_mb_rows_in_slice(slice_num, mb_height); + slice_num++; + } + *first_in_slice = (mb_y_order == 0); + return mb_y_order * 4 + slice_num; +} + +static const AVOption options[] = { + FF_MPV_COMMON_OPTS + { NULL}, +}; + +#if CONFIG_SPEEDHQ_ENCODER +static const AVClass speedhq_class = { + .class_name = "speedhq encoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_speedhq_encoder = { + .name = "speedhq", + .long_name = NULL_IF_CONFIG_SMALL("NewTek SpeedHQ"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_SPEEDHQ, + .priv_data_size = sizeof(MpegEncContext), + .init = ff_mpv_encode_init, + .encode2 = ff_mpv_encode_picture, + .close = ff_mpv_encode_end, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_NONE + }, + .priv_class = &speedhq_class, +}; +#endif diff --git a/libavcodec/speedhqenc.h b/libavcodec/speedhqenc.h new file mode 100644 index 0000000000..5100bb2d34 --- /dev/null +++ b/libavcodec/speedhqenc.h @@ -0,0 +1,48 @@ +/* + * SpeedHQ encoder + * Copyright (c) 2000, 2001 Fabrice Bellard + * Copyright (c) 2003 Alex Beregszaszi + * Copyright (c) 2003-2004 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * SpeedHQ encoder. + */ + +#ifndef AVCODEC_SPEEDHQENC_H +#define AVCODEC_SPEEDHQENC_H + +#include + +#include "mjpeg.h" +#include "mjpegenc_common.h" +#include "mpegvideo.h" +#include "put_bits.h" + +int ff_speedhq_encode_init(MpegEncContext *s); +void ff_speedhq_encode_close(MpegEncContext *s); +void ff_speedhq_encode_mb(MpegEncContext *s, int16_t block[12][64]); + +void ff_speedhq_encode_picture_header(MpegEncContext *s); +void ff_speedhq_end_slice(MpegEncContext *s); + +int ff_speedhq_mb_y_order_to_mb(int mb_y_order, int mb_height, int *first_in_slice); + +#endif /* AVCODEC_SPEEDHQENC_H */