On 07/13/2012 05:10 AM, Luca Barbato wrote:
> From: Michael Bradshaw <[email protected]>
> 
> Based on FFmpeg commits
> commit 713a7854e06964abc8f7d015b94acbed27769d24
> commit 7544ef78051c2b0477687ac4d7da829e61dbf8ac
> commit f06269dda5cab9d5e5dc591fbe3f752aae2b0a2b
> commit 1ef5561a662e96c9b3f554ee76d3a6e7327aba4c
> commit b0f6df28de4f78decd0acec88d860e1719ea8b05
> commit 3f26a874884896641903cf41a0b93bc2b5349cc5
> commit 67d5fcc989a0ee3cc3b3a89e643ee7f099ca6229
> commit dec8eb6b29beb472e94e7e32ad89e180597b7b0d
> commit 8e45005a849c849bbbc28bc16d7ecb4d342b251a
> commit 06b0c6a6559e8f179faaba9641bcb2d68edc7160
> commit b222c28ee885187c5fcded69d8fa98fe60c70a7e
> commit a5b823368a3589dd1e5b11bd4cb0bf7b013a8314
> commit de0735502662c766c5cfccd8dca3473f3e77f65e
> commit df42dd73235c4d89be44a088cb9fb30d27260cf6
> commit 56af084cb5478bc3c0eb2d47d47e82bbe40fbf2d
> commit a02694c92618fae98e5ce85b0a5eed71988a9d40
> commit 0abe25aa7bfa017a18832c6d4b0f59e4fd18f7d5
> commit ee33eb40fc007fe354d381d253625157aa73c525
> commit 36397ea1c7e27ab850149cc61394c2baa26dd532
> commit ffd1017fb89e3fcb4853e2803b05e3d1847916c4
> commit 4093d130d1934edcd098014d306492173527f1f6
> commit ef611095f0d0c1256cbb6654f94cae61a60f2736
> commit ba10207bbe5ebd97b5afc3f19baf4a1ad8f974d5
> commit 40c36196d555496269656dd9f15c8520fa2301cd
> commit 0275b75a7e705ef5a6bd6610f1450671f78000b6
> commit 1b987c4bb03d09debceb667af5f9af897c5ab0cd
> commit 856b33f578d3ca0e17ae69273d1c77248bf61a66
> commit 7c92c03b8d0aec2b593ade85525c7a8f87fecc63

I squashed the whole thing since the bulk of the commits had been
refinements and additional pixel formats.

> ---
>  Changelog                   |    1 +
>  configure                   |    2 +
>  doc/general.texi            |    2 +
>  libavcodec/Makefile         |    1 +
>  libavcodec/allcodecs.c      |    2 +-
>  libavcodec/libopenjpegenc.c |  490 
> +++++++++++++++++++++++++++++++++++++++++++
>  libavcodec/version.h        |    2 +-
>  7 files changed, 498 insertions(+), 2 deletions(-)
>  create mode 100644 libavcodec/libopenjpegenc.c
> 
> diff --git a/Changelog b/Changelog
> index 66994b4..09dae13 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -32,6 +32,7 @@ version <next>:
>  - audio channel mapping filter
>  - Microsoft ATC Screen decoder
>  - RTSP listen mode
> +- JPEG2k encoding support through OpenJPEG
>  
>  
>  version 0.8:
> diff --git a/configure b/configure
> index ffb1e7e..158a0dd 100755
> --- a/configure
> +++ b/configure
> @@ -179,6 +179,7 @@ External library support:
>    --enable-libopencv       enable video filtering via libopencv [no]
>    --enable-libopenjpeg     enable JPEG 2000 decoding via OpenJPEG [no]
>    --enable-libpulse        enable Pulseaudio input via libpulse [no]
> +  --enable-libopenjpeg     enable JPEG 2000 encoding/decoding via OpenJPEG 
> [no]
>    --enable-librtmp         enable RTMP[E] support via librtmp [no]
>    --enable-libschroedinger enable Dirac support via libschroedinger [no]
>    --enable-libspeex        enable Speex support via libspeex [no]
> @@ -1457,6 +1458,7 @@ libopencore_amrnb_decoder_deps="libopencore_amrnb"
>  libopencore_amrnb_encoder_deps="libopencore_amrnb"
>  libopencore_amrwb_decoder_deps="libopencore_amrwb"
>  libopenjpeg_decoder_deps="libopenjpeg"
> +libopenjpeg_encoder_deps="libopenjpeg"
>  libschroedinger_decoder_deps="libschroedinger"
>  libschroedinger_encoder_deps="libschroedinger"
>  libspeex_decoder_deps="libspeex"
> diff --git a/doc/general.texi b/doc/general.texi
> index 4ea47b4..bf56b19 100644
> --- a/doc/general.texi
> +++ b/doc/general.texi
> @@ -18,6 +18,7 @@ explicitly requested by passing the appropriate flags to
>  
>  @section OpenCORE and VisualOn libraries
>  
> +

That's a stray, I'll amend it already

>  Spun off Google Android sources, OpenCore and VisualOn libraries provide
>  encoders for a number of audio codecs.



> +static void info_callback(const char *msg, void *data)
> +{
> +    av_log((AVCodecContext*)data, AV_LOG_DEBUG, "%s\n", msg);
> +}

Yes the name is misleading, but apparently it is used for debug messages.


> +
> +static opj_image_t *mj2_create_image(AVCodecContext *avctx,
> +                                     opj_cparameters_t *parameters)
> +{
> +    opj_image_cmptparm_t *cmptparm;
> +    OPJ_COLOR_SPACE color_space;
> +    opj_image_t *img;
> +    int i;
> +    int sub_dx[4];
> +    int sub_dy[4];
> +    int numcomps = av_pix_fmt_descriptors[avctx->pix_fmt].nb_components;
> +
> +    sub_dx[0] = sub_dx[3] = 1;
> +    sub_dy[0] = sub_dy[3] = 1;
> +    sub_dx[1] = sub_dx[2] =
> +        1 << av_pix_fmt_descriptors[avctx->pix_fmt].log2_chroma_w;
> +    sub_dy[1] = sub_dy[2] =
> +        1 << av_pix_fmt_descriptors[avctx->pix_fmt].log2_chroma_h;
> +
> +
> +    switch (avctx->pix_fmt) {
> +    case PIX_FMT_GRAY8:
> +    case PIX_FMT_GRAY16:
> +        color_space = CLRSPC_GRAY;
> +        break;
> +    case PIX_FMT_RGB24:
> +    case PIX_FMT_RGBA:
> +    case PIX_FMT_RGB48:
> +        color_space = CLRSPC_SRGB;
> +        break;
> +    case PIX_FMT_YUV410P:
> +    case PIX_FMT_YUV411P:
> +    case PIX_FMT_YUV420P:
> +    case PIX_FMT_YUV422P:
> +    case PIX_FMT_YUV440P:
> +    case PIX_FMT_YUV444P:
> +    case PIX_FMT_YUVA420P:
> +    case PIX_FMT_YUV420P9:
> +    case PIX_FMT_YUV422P9:
> +    case PIX_FMT_YUV444P9:
> +    case PIX_FMT_YUV420P10:
> +    case PIX_FMT_YUV422P10:
> +    case PIX_FMT_YUV444P10:
> +    case PIX_FMT_YUV420P16:
> +    case PIX_FMT_YUV422P16:
> +    case PIX_FMT_YUV444P16:
> +        color_space = CLRSPC_SYCC;
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR,
> +            "The requested pixel format '%s' is not supported\n",
> +            av_get_pix_fmt_name(avctx->pix_fmt));
> +        return NULL;
> +    }
> +
> +    cmptparm = av_mallocz(numcomps * sizeof(opj_image_cmptparm_t));
> +    if (!cmptparm) {
> +        av_log(avctx, AV_LOG_ERROR, "Not enough memory");
> +        return NULL;
> +    }
> +
> +    for (i = 0; i < numcomps; i++) {
> +        cmptparm[i].prec =
> +            av_pix_fmt_descriptors[avctx->pix_fmt].comp[i].depth_minus1 + 1;
> +        cmptparm[i].bpp =
> +            av_pix_fmt_descriptors[avctx->pix_fmt].comp[i].depth_minus1 + 1;
> +        cmptparm[i].sgnd = 0;
> +        cmptparm[i].dx = sub_dx[i];
> +        cmptparm[i].dy = sub_dy[i];
> +        cmptparm[i].w = avctx->width / sub_dx[i];
> +        cmptparm[i].h = avctx->height / sub_dy[i];
> +    }
> +
> +    img = opj_image_create(numcomps, cmptparm, color_space);
> +    av_freep(&cmptparm);
> +    return img;
> +}
> +
> +static av_cold int libopenjpeg_encode_init(AVCodecContext *avctx)
> +{
> +    LibOpenJPEGContext *ctx = avctx->priv_data;
> +    int err = AVERROR(ENOMEM);
> +
> +    opj_set_default_encoder_parameters(&ctx->enc_params);
> +
> +    ctx->enc_params.cp_rsiz          = ctx->profile;
> +    ctx->enc_params.mode             = !!avctx->global_quality;
> +    ctx->enc_params.cp_cinema        = ctx->cinema_mode;
> +    ctx->enc_params.prog_order       = ctx->prog_order;
> +    ctx->enc_params.numresolution    = ctx->numresolution;
> +    ctx->enc_params.cp_disto_alloc   = ctx->disto_alloc;
> +    ctx->enc_params.cp_fixed_alloc   = ctx->fixed_alloc;
> +    ctx->enc_params.cp_fixed_quality = ctx->fixed_quality;
> +    ctx->enc_params.tcp_numlayers    = ctx->numlayers;
> +    ctx->enc_params.tcp_rates[0]     = FFMAX(avctx->compression_level, 0) * 
> 2;
> +
> +    ctx->compress = opj_create_compress(ctx->format);
> +    if (!ctx->compress) {
> +        av_log(avctx, AV_LOG_ERROR, "Error creating the compressor\n");
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    avctx->coded_frame = avcodec_alloc_frame();
> +    if (!avctx->coded_frame) {
> +        av_log(avctx, AV_LOG_ERROR, "Error allocating coded frame\n");
> +        goto fail;
> +    }
> +
> +    ctx->image = mj2_create_image(avctx, &ctx->enc_params);
> +    if (!ctx->image) {
> +        av_log(avctx, AV_LOG_ERROR, "Error creating the mj2 image\n");
> +        err = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +    ctx->event_mgr.info_handler    = info_callback;
> +    ctx->event_mgr.error_handler   = error_callback;
> +    ctx->event_mgr.warning_handler = warning_callback;
> +    opj_set_event_mgr((opj_common_ptr)ctx->compress, &ctx->event_mgr, avctx);
> +
> +    return 0;
> +
> +fail:
> +    av_freep(&ctx->compress);
> +    av_freep(&avctx->coded_frame);
> +    return err;
> +}
> +
> +static int libopenjpeg_copy_packed8(AVCodecContext *avctx,
> +                                    const AVFrame *frame, opj_image_t *image)
> +{
> +    int compno;
> +    int x;
> +    int y;
> +    int image_index;
> +    int frame_index;
> +    const int numcomps = image->numcomps;
> +
> +    for (compno = 0; compno < numcomps; ++compno) {
> +        if (image->comps[compno].w > frame->linesize[0] / numcomps) {
> +            av_log(avctx, AV_LOG_ERROR,
> +                   "Error: frame's linesize is too small for the image\n");
> +            return 0;
> +        }
> +    }

This chunk looks always the same, probably could be factored out.

> +
> +    for (compno = 0; compno < numcomps; ++compno) {
> +        for (y = 0; y < avctx->height; ++y) {
> +            image_index = y * avctx->width;
> +            frame_index = y * frame->linesize[0] + compno;
> +            for (x = 0; x < avctx->width; ++x) {
> +                image->comps[compno].data[image_index++] =
> +                    frame->data[0][frame_index];
> +                frame_index += numcomps;
> +            }
> +        }
> +    }

not sure if we have a generic permute in dsputil but might be beneficial.

> +static int libopenjpeg_copy_unpacked8(AVCodecContext *avctx,
> +                                      const AVFrame *frame, opj_image_t 
> *image)
> +{
> +    int compno;
> +    int x;
> +    int y;
> +    int width;
> +    int height;
> +    int image_index;
> +    int frame_index;
> +    const int numcomps = image->numcomps;
> +
> +    for (compno = 0; compno < numcomps; ++compno) {
> +        if (image->comps[compno].w > frame->linesize[compno]) {
> +            av_log(avctx, AV_LOG_ERROR,
> +                   "Error: frame's linesize is too small for the image\n");
> +            return 0;
> +        }
> +    }
> +
> +    for (compno = 0; compno < numcomps; ++compno) {
> +        width = avctx->width / image->comps[compno].dx;
> +        height = avctx->height / image->comps[compno].dy;
> +        for (y = 0; y < height; ++y) {
> +            image_index = y * width;
> +            frame_index = y * frame->linesize[compno];
> +            for (x = 0; x < width; ++x) {
> +                image->comps[compno].data[image_index++] =
> +                    frame->data[compno][frame_index++];
> +            }
> +        }
> +    }

memcpy?


> +static int libopenjpeg_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
> +                                    const AVFrame *frame, int *got_packet)
> +{
> +    LibOpenJPEGContext *ctx = avctx->priv_data;
> +    opj_cinfo_t *compress = ctx->compress;
> +    opj_image_t *image = ctx->image;
> +    opj_cio_t *stream;
> +    int cpyresult = 0;
> +    int ret, len;
> +
> +    // x0, y0 is the top left corner of the image
> +    // x1, y1 is the width, height of the reference grid
> +    image->x0 = 0;
> +    image->y0 = 0;
> +    image->x1 = (avctx->width - 1) * ctx->enc_params.subsampling_dx + 1;
> +    image->y1 = (avctx->height - 1) * ctx->enc_params.subsampling_dy + 1;
> +
> +    switch (avctx->pix_fmt) {
> +    case PIX_FMT_RGB24:
> +    case PIX_FMT_RGBA:
> +        cpyresult = libopenjpeg_copy_packed8(avctx, frame, image);
> +        break;
> +    case PIX_FMT_RGB48:
> +        cpyresult = libopenjpeg_copy_packed16(avctx, frame, image);
> +        break;
> +    case PIX_FMT_GRAY8:
> +    case PIX_FMT_YUV410P:
> +    case PIX_FMT_YUV411P:
> +    case PIX_FMT_YUV420P:
> +    case PIX_FMT_YUV422P:
> +    case PIX_FMT_YUV440P:
> +    case PIX_FMT_YUV444P:
> +    case PIX_FMT_YUVA420P:
> +        cpyresult = libopenjpeg_copy_unpacked8(avctx, frame, image);
> +        break;
> +    case PIX_FMT_GRAY16:
> +    case PIX_FMT_YUV420P9:
> +    case PIX_FMT_YUV422P9:
> +    case PIX_FMT_YUV444P9:
> +    case PIX_FMT_YUV444P10:
> +    case PIX_FMT_YUV422P10:
> +    case PIX_FMT_YUV420P10:
> +    case PIX_FMT_YUV444P16:
> +    case PIX_FMT_YUV422P16:
> +    case PIX_FMT_YUV420P16:
> +        cpyresult = libopenjpeg_copy_unpacked16(avctx, frame, image);
> +        break;
> +    default:
> +        av_log(avctx, AV_LOG_ERROR,
> +               "The frame's pixel format '%s' is not supported\n",
> +               av_get_pix_fmt_name(avctx->pix_fmt));
> +        return AVERROR(EINVAL);
> +        break;
> +    }
> +
> +    if (!cpyresult) {
> +        av_log(avctx, AV_LOG_ERROR,
> +               "Could not copy the frame data to the internal image 
> buffer\n");
> +        return AVERROR_INVALIDDATA;
> +    }
> +
> +    opj_setup_encoder(compress, &ctx->enc_params, image);
> +    stream = opj_cio_open((opj_common_ptr)compress, NULL, 0);
> +    if (!stream) {
> +        av_log(avctx, AV_LOG_ERROR, "Error creating the cio stream\n");
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    if (!opj_encode(compress, stream, image, NULL)) {

this function is a wrapper to the opj_encode_info, we might call it
directly.

> +        opj_cio_close(stream);
> +        av_log(avctx, AV_LOG_ERROR, "Error during the opj encode\n");
> +        return -1;

> +    }
> +
> +    len = cio_tell(stream);
> +    if ((ret = ff_alloc_packet(pkt, len)) < 0) {
> +        opj_cio_close(stream);
> +        return ret;
> +    }
> +
> +    memcpy(pkt->data, stream->buffer, len);

I wonder if there is a way to make openjpeg use a provided buffer, we
are spending lots of time moving memory around.

> +    pkt->flags |= AV_PKT_FLAG_KEY;
> +    *got_packet = 1;
> +    opj_cio_close(stream);
> +    return 0;
> +}


-- 

Luca Barbato
Gentoo/linux
http://dev.gentoo.org/~lu_zero

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to