diff mbox series

[FFmpeg-devel] avcodec: add SpeedHQ encoder

Message ID 20201109120236.8990-1-onemda@gmail.com
State Superseded
Headers show
Series [FFmpeg-devel] avcodec: add SpeedHQ encoder | expand

Checks

Context Check Description
andriy/x86_make_warn warning New warnings during build
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Paul B Mahol Nov. 9, 2020, 12:02 p.m. UTC
From: Jean-Baptiste Kempf <jb@videolan.org>

Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 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

Comments

Andreas Rheinhardt Nov. 9, 2020, 12:54 p.m. UTC | #1
Paul B Mahol:
> From: Jean-Baptiste Kempf <jb@videolan.org>
> 
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  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 <next>:
>  - 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];
> +

static and ff prefix is incompatible.

> +/* 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);

This has been recently unavpriv'ed (the avpriv function is now
legacy-only), but it makes me wonder: Does it even work?
avpriv_align_put_bits() aligns it in BE-fashion. For both the LE and BE
PutBitContext the lsb bits contain the current cache, the higher bits
are currently unused (and are always zero). When using a LE write
function, the valid bits currently in the cache stay where they are and
the value to put is shifted to its destination place; when using a BE
write function, the current cache is shifted upwards and the new data to
be written is put into the LSB bits. So using avpriv_align_put_bits()
here should not even work (unless the position is already automatically
aligned, in which case one can of course remove the above call).

And is it actually guaranteed that nothing in mpegvideo_enc.c will write
anything in BE fashion to this PutBitContext?

> +    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]);

You can inline the values.

> +                /* 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]);

You can inline the values.

> +}
> +
> +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},
> +};

Use ff_mpv_generic_options.

> +
> +#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 <stdint.h>
> +
> +#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 */
>
Derek Buitenhuis Nov. 9, 2020, 1:11 p.m. UTC | #2
On 09/11/2020 12:02, Paul B Mahol wrote:
> +    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,

I think you can add FF_CODEC_CAP_INIT_THREADSAFE since ff_thread_once is
used properly.

- Derek
Paul B Mahol Nov. 9, 2020, 3:22 p.m. UTC | #3
On Mon, Nov 9, 2020 at 4:11 PM Derek Buitenhuis <derek.buitenhuis@gmail.com>
wrote:

> On 09/11/2020 12:02, Paul B Mahol wrote:
> > +    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
>
> I think you can add FF_CODEC_CAP_INIT_THREADSAFE since ff_thread_once is
> used properly.
>

What about the rest of the mpv init code that calls this function, it is
safe too?


>
> - Derek
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Paul B Mahol Nov. 19, 2020, 9:23 a.m. UTC | #4
Will apply soon.

On Mon, Nov 9, 2020 at 4:22 PM Paul B Mahol <onemda@gmail.com> wrote:

>
>
> On Mon, Nov 9, 2020 at 4:11 PM Derek Buitenhuis <
> derek.buitenhuis@gmail.com> wrote:
>
>> On 09/11/2020 12:02, Paul B Mahol wrote:
>> > +    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
>>
>> I think you can add FF_CODEC_CAP_INIT_THREADSAFE since ff_thread_once is
>> used properly.
>>
>
> What about the rest of the mpv init code that calls this function, it is
> safe too?
>
>
>>
>> - Derek
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>
>> To unsubscribe, visit link above, or email
>> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
>
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index 3aeada17a4..b200082630 100644
--- a/Changelog
+++ b/Changelog
@@ -43,6 +43,7 @@  version <next>:
 - 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 <stdint.h>
+
+#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 */