1/*
2 * ZeroCodec Decoder
3 *
4 * Copyright (c) 2012, Derek Buitenhuis
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <zlib.h>
20
21#include "avcodec.h"
22#include "internal.h"
23#include "libavutil/common.h"
24
25typedef struct {
26    AVFrame  *previous_frame;
27    z_stream zstream;
28} ZeroCodecContext;
29
30static int zerocodec_decode_frame(AVCodecContext *avctx, void *data,
31                                  int *got_frame, AVPacket *avpkt)
32{
33    ZeroCodecContext *zc = avctx->priv_data;
34    AVFrame *pic         = data;
35    AVFrame *prev_pic    = zc->previous_frame;
36    z_stream *zstream    = &zc->zstream;
37    uint8_t *prev        = prev_pic->data[0];
38    uint8_t *dst;
39    int i, j, zret, ret;
40
41    if (avpkt->flags & AV_PKT_FLAG_KEY) {
42        pic->key_frame = 1;
43        pic->pict_type = AV_PICTURE_TYPE_I;
44    } else {
45        if (!prev) {
46            av_log(avctx, AV_LOG_ERROR, "Missing reference frame.\n");
47            return AVERROR_INVALIDDATA;
48        }
49
50        prev += (avctx->height - 1) * prev_pic->linesize[0];
51
52        pic->key_frame = 0;
53        pic->pict_type = AV_PICTURE_TYPE_P;
54    }
55
56    zret = inflateReset(zstream);
57    if (zret != Z_OK) {
58        av_log(avctx, AV_LOG_ERROR, "Could not reset inflate: %d.\n", zret);
59        return AVERROR_INVALIDDATA;
60    }
61
62    if ((ret = ff_get_buffer(avctx, pic, AV_GET_BUFFER_FLAG_REF)) < 0)
63        return ret;
64
65    zstream->next_in  = avpkt->data;
66    zstream->avail_in = avpkt->size;
67
68    dst = pic->data[0] + (avctx->height - 1) * pic->linesize[0];
69
70    /**
71     * ZeroCodec has very simple interframe compression. If a value
72     * is the same as the previous frame, set it to 0.
73     */
74
75    for (i = 0; i < avctx->height; i++) {
76        zstream->next_out  = dst;
77        zstream->avail_out = avctx->width << 1;
78
79        zret = inflate(zstream, Z_SYNC_FLUSH);
80        if (zret != Z_OK && zret != Z_STREAM_END) {
81            av_log(avctx, AV_LOG_ERROR,
82                   "Inflate failed with return code: %d.\n", zret);
83            return AVERROR_INVALIDDATA;
84        }
85
86        if (!(avpkt->flags & AV_PKT_FLAG_KEY))
87            for (j = 0; j < avctx->width << 1; j++)
88                dst[j] += prev[j] & -!dst[j];
89
90        prev -= prev_pic->linesize[0];
91        dst  -= pic->linesize[0];
92    }
93
94    av_frame_unref(zc->previous_frame);
95    if ((ret = av_frame_ref(zc->previous_frame, pic)) < 0)
96        return ret;
97
98    *got_frame = 1;
99
100    return avpkt->size;
101}
102
103static av_cold int zerocodec_decode_close(AVCodecContext *avctx)
104{
105    ZeroCodecContext *zc = avctx->priv_data;
106
107    av_frame_free(&zc->previous_frame);
108
109    inflateEnd(&zc->zstream);
110
111    return 0;
112}
113
114static av_cold int zerocodec_decode_init(AVCodecContext *avctx)
115{
116    ZeroCodecContext *zc = avctx->priv_data;
117    z_stream *zstream    = &zc->zstream;
118    int zret;
119
120    avctx->pix_fmt             = AV_PIX_FMT_UYVY422;
121    avctx->bits_per_raw_sample = 8;
122
123    zstream->zalloc = Z_NULL;
124    zstream->zfree  = Z_NULL;
125    zstream->opaque = Z_NULL;
126
127    zret = inflateInit(zstream);
128    if (zret != Z_OK) {
129        av_log(avctx, AV_LOG_ERROR, "Could not initialize inflate: %d.\n", zret);
130        return AVERROR(ENOMEM);
131    }
132
133    zc->previous_frame = av_frame_alloc();
134    if (!zc->previous_frame) {
135        zerocodec_decode_close(avctx);
136        return AVERROR(ENOMEM);
137    }
138
139    return 0;
140}
141
142AVCodec ff_zerocodec_decoder = {
143    .type           = AVMEDIA_TYPE_VIDEO,
144    .name           = "zerocodec",
145    .long_name      = NULL_IF_CONFIG_SMALL("ZeroCodec Lossless Video"),
146    .id             = AV_CODEC_ID_ZEROCODEC,
147    .priv_data_size = sizeof(ZeroCodecContext),
148    .init           = zerocodec_decode_init,
149    .decode         = zerocodec_decode_frame,
150    .close          = zerocodec_decode_close,
151    .capabilities   = CODEC_CAP_DR1,
152};
153