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