zerocodec.c 4.89 KB
Newer Older
Derek Buitenhuis's avatar
Derek Buitenhuis committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * ZeroCodec Decoder
 *
 * Copyright (c) 2012, Derek Buitenhuis
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <zlib.h>

#include "avcodec.h"
22
#include "internal.h"
23
#include "libavutil/common.h"
Derek Buitenhuis's avatar
Derek Buitenhuis committed
24 25 26 27 28 29 30

typedef struct {
    AVFrame  previous_frame;
    z_stream zstream;
} ZeroCodecContext;

static int zerocodec_decode_frame(AVCodecContext *avctx, void *data,
31
                                  int *got_frame, AVPacket *avpkt)
Derek Buitenhuis's avatar
Derek Buitenhuis committed
32 33 34 35 36
{
    ZeroCodecContext *zc = avctx->priv_data;
    AVFrame *pic         = avctx->coded_frame;
    AVFrame *prev_pic    = &zc->previous_frame;
    z_stream *zstream    = &zc->zstream;
Derek Buitenhuis's avatar
Derek Buitenhuis committed
37 38
    uint8_t *prev        = prev_pic->data[0];
    uint8_t *dst;
Derek Buitenhuis's avatar
Derek Buitenhuis committed
39 40 41 42
    int i, j, zret;

    pic->reference = 3;

43 44 45 46 47
    if (avpkt->flags & AV_PKT_FLAG_KEY) {
        pic->key_frame = 1;
        pic->pict_type = AV_PICTURE_TYPE_I;
    } else {
        if (!prev) {
Derek Buitenhuis's avatar
Derek Buitenhuis committed
48
            av_log(avctx, AV_LOG_ERROR, "Missing reference frame.\n");
49 50
            return AVERROR_INVALIDDATA;
        }
51 52 53

        prev += (avctx->height - 1) * prev_pic->linesize[0];

54 55 56 57
        pic->key_frame = 0;
        pic->pict_type = AV_PICTURE_TYPE_P;
    }

Derek Buitenhuis's avatar
Derek Buitenhuis committed
58 59
    zret = inflateReset(zstream);
    if (zret != Z_OK) {
Derek Buitenhuis's avatar
Derek Buitenhuis committed
60 61
        av_log(avctx, AV_LOG_ERROR, "Could not reset inflate: %d.\n", zret);
        return AVERROR_INVALIDDATA;
Derek Buitenhuis's avatar
Derek Buitenhuis committed
62 63
    }

64
    if (ff_get_buffer(avctx, pic) < 0) {
65 66 67 68
        av_log(avctx, AV_LOG_ERROR, "Could not allocate buffer.\n");
        return AVERROR(ENOMEM);
    }

Derek Buitenhuis's avatar
Derek Buitenhuis committed
69 70 71
    zstream->next_in  = avpkt->data;
    zstream->avail_in = avpkt->size;

72
    dst = pic->data[0] + (avctx->height - 1) * pic->linesize[0];
Derek Buitenhuis's avatar
Derek Buitenhuis committed
73 74 75 76 77 78

    /**
     * ZeroCodec has very simple interframe compression. If a value
     * is the same as the previous frame, set it to 0.
     */

79 80 81
    for (i = 0; i < avctx->height; i++) {
        zstream->next_out  = dst;
        zstream->avail_out = avctx->width << 1;
Derek Buitenhuis's avatar
Derek Buitenhuis committed
82

83 84
        zret = inflate(zstream, Z_SYNC_FLUSH);
        if (zret != Z_OK && zret != Z_STREAM_END) {
85
            avctx->release_buffer(avctx, pic);
86
            av_log(avctx, AV_LOG_ERROR,
Derek Buitenhuis's avatar
Derek Buitenhuis committed
87 88
                   "Inflate failed with return code: %d.\n", zret);
            return AVERROR_INVALIDDATA;
89
        }
Derek Buitenhuis's avatar
Derek Buitenhuis committed
90

91
        if (!(avpkt->flags & AV_PKT_FLAG_KEY))
Derek Buitenhuis's avatar
Derek Buitenhuis committed
92 93 94
            for (j = 0; j < avctx->width << 1; j++)
                dst[j] += prev[j] & -!dst[j];

95 96
        prev -= prev_pic->linesize[0];
        dst  -= pic->linesize[0];
Derek Buitenhuis's avatar
Derek Buitenhuis committed
97 98 99 100 101 102
    }

    /* Release the previous buffer if need be */
    if (prev_pic->data[0])
        avctx->release_buffer(avctx, prev_pic);

103
    *got_frame = 1;
Derek Buitenhuis's avatar
Derek Buitenhuis committed
104 105
    *(AVFrame *)data = *pic;

106 107 108 109
    /* Store the previous frame for use later.
     * FFSWAP ensures that e.g. pic->data is NULLed. */
    FFSWAP(AVFrame, *pic, *prev_pic);

Derek Buitenhuis's avatar
Derek Buitenhuis committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
    return avpkt->size;
}

static av_cold int zerocodec_decode_close(AVCodecContext *avctx)
{
    ZeroCodecContext *zc = avctx->priv_data;
    AVFrame *prev_pic    = &zc->previous_frame;

    inflateEnd(&zc->zstream);

    /* Release last frame */
    if (prev_pic->data[0])
        avctx->release_buffer(avctx, prev_pic);

    av_freep(&avctx->coded_frame);

    return 0;
}

static av_cold int zerocodec_decode_init(AVCodecContext *avctx)
{
    ZeroCodecContext *zc = avctx->priv_data;
    z_stream *zstream    = &zc->zstream;
    int zret;

135
    avctx->pix_fmt             = AV_PIX_FMT_UYVY422;
Derek Buitenhuis's avatar
Derek Buitenhuis committed
136 137 138 139 140 141 142 143
    avctx->bits_per_raw_sample = 8;

    zstream->zalloc = Z_NULL;
    zstream->zfree  = Z_NULL;
    zstream->opaque = Z_NULL;

    zret = inflateInit(zstream);
    if (zret != Z_OK) {
Derek Buitenhuis's avatar
Derek Buitenhuis committed
144
        av_log(avctx, AV_LOG_ERROR, "Could not initialize inflate: %d.\n", zret);
Derek Buitenhuis's avatar
Derek Buitenhuis committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
        return AVERROR(ENOMEM);
    }

    avctx->coded_frame = avcodec_alloc_frame();
    if (!avctx->coded_frame) {
        av_log(avctx, AV_LOG_ERROR, "Could not allocate frame buffer.\n");
        zerocodec_decode_close(avctx);
        return AVERROR(ENOMEM);
    }

    return 0;
}

AVCodec ff_zerocodec_decoder = {
    .type           = AVMEDIA_TYPE_VIDEO,
    .name           = "zerocodec",
161
    .id             = AV_CODEC_ID_ZEROCODEC,
Derek Buitenhuis's avatar
Derek Buitenhuis committed
162 163 164 165 166 167 168
    .priv_data_size = sizeof(ZeroCodecContext),
    .init           = zerocodec_decode_init,
    .decode         = zerocodec_decode_frame,
    .close          = zerocodec_decode_close,
    .capabilities   = CODEC_CAP_DR1,
    .long_name      = NULL_IF_CONFIG_SMALL("ZeroCodec Lossless Video"),
};