1 |
|
|
/* |
2 |
|
|
* TechSmith Camtasia decoder |
3 |
|
|
* Copyright (c) 2004 Konstantin Shishkov |
4 |
|
|
* |
5 |
|
|
* This file is part of FFmpeg. |
6 |
|
|
* |
7 |
|
|
* FFmpeg is free software; you can redistribute it and/or |
8 |
|
|
* modify it under the terms of the GNU Lesser General Public |
9 |
|
|
* License as published by the Free Software Foundation; either |
10 |
|
|
* version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
* |
12 |
|
|
* FFmpeg is distributed in the hope that it will be useful, |
13 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
|
|
* Lesser General Public License for more details. |
16 |
|
|
* |
17 |
|
|
* You should have received a copy of the GNU Lesser General Public |
18 |
|
|
* License along with FFmpeg; if not, write to the Free Software |
19 |
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
/** |
23 |
|
|
* @file |
24 |
|
|
* TechSmith Camtasia decoder |
25 |
|
|
* |
26 |
|
|
* Fourcc: TSCC |
27 |
|
|
* |
28 |
|
|
* Codec is very simple: |
29 |
|
|
* it codes picture (picture difference, really) |
30 |
|
|
* with algorithm almost identical to Windows RLE8, |
31 |
|
|
* only without padding and with greater pixel sizes, |
32 |
|
|
* then this coded picture is packed with ZLib |
33 |
|
|
* |
34 |
|
|
* Supports: BGR8,BGR555,BGR24 - only BGR8 and BGR555 tested |
35 |
|
|
*/ |
36 |
|
|
|
37 |
|
|
#include <stdio.h> |
38 |
|
|
#include <stdlib.h> |
39 |
|
|
|
40 |
|
|
#include "avcodec.h" |
41 |
|
|
#include "internal.h" |
42 |
|
|
#include "msrledec.h" |
43 |
|
|
|
44 |
|
|
#include <zlib.h> |
45 |
|
|
|
46 |
|
|
typedef struct TsccContext { |
47 |
|
|
|
48 |
|
|
AVCodecContext *avctx; |
49 |
|
|
AVFrame *frame; |
50 |
|
|
|
51 |
|
|
// Bits per pixel |
52 |
|
|
int bpp; |
53 |
|
|
// Decompressed data size |
54 |
|
|
unsigned int decomp_size; |
55 |
|
|
// Decompression buffer |
56 |
|
|
unsigned char* decomp_buf; |
57 |
|
|
GetByteContext gb; |
58 |
|
|
int height; |
59 |
|
|
z_stream zstream; |
60 |
|
|
|
61 |
|
|
uint32_t pal[256]; |
62 |
|
|
} CamtasiaContext; |
63 |
|
|
|
64 |
|
392 |
static int decode_frame(AVCodecContext *avctx, void *data, int *got_frame, |
65 |
|
|
AVPacket *avpkt) |
66 |
|
|
{ |
67 |
|
392 |
const uint8_t *buf = avpkt->data; |
68 |
|
392 |
int buf_size = avpkt->size; |
69 |
|
392 |
CamtasiaContext * const c = avctx->priv_data; |
70 |
|
392 |
AVFrame *frame = c->frame; |
71 |
|
|
int ret; |
72 |
|
392 |
int palette_has_changed = 0; |
73 |
|
|
|
74 |
✗✓ |
392 |
if (c->avctx->pix_fmt == AV_PIX_FMT_PAL8) { |
75 |
|
|
int size; |
76 |
|
|
const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, &size); |
77 |
|
|
|
78 |
|
|
if (pal && size == AVPALETTE_SIZE) { |
79 |
|
|
palette_has_changed = 1; |
80 |
|
|
memcpy(c->pal, pal, AVPALETTE_SIZE); |
81 |
|
|
} else if (pal) { |
82 |
|
|
av_log(avctx, AV_LOG_ERROR, "Palette size %d is wrong\n", size); |
83 |
|
|
} |
84 |
|
|
} |
85 |
|
|
|
86 |
|
392 |
ret = inflateReset(&c->zstream); |
87 |
✗✓ |
392 |
if (ret != Z_OK) { |
88 |
|
|
av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret); |
89 |
|
|
return AVERROR_UNKNOWN; |
90 |
|
|
} |
91 |
|
392 |
c->zstream.next_in = buf; |
92 |
|
392 |
c->zstream.avail_in = buf_size; |
93 |
|
392 |
c->zstream.next_out = c->decomp_buf; |
94 |
|
392 |
c->zstream.avail_out = c->decomp_size; |
95 |
|
392 |
ret = inflate(&c->zstream, Z_FINISH); |
96 |
|
|
// Z_DATA_ERROR means empty picture |
97 |
✓✓✓✗
|
392 |
if (ret == Z_DATA_ERROR && !palette_has_changed) { |
98 |
|
268 |
return buf_size; |
99 |
|
|
} |
100 |
|
|
|
101 |
✓✗✓✓ ✓✗ |
124 |
if ((ret != Z_OK) && (ret != Z_STREAM_END) && (ret != Z_DATA_ERROR)) { |
102 |
|
1 |
av_log(avctx, AV_LOG_ERROR, "Inflate error: %d\n", ret); |
103 |
|
1 |
return AVERROR_UNKNOWN; |
104 |
|
|
} |
105 |
|
|
|
106 |
✗✓ |
123 |
if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0) |
107 |
|
|
return ret; |
108 |
|
|
|
109 |
✓✗ |
123 |
if (ret != Z_DATA_ERROR) { |
110 |
|
123 |
bytestream2_init(&c->gb, c->decomp_buf, |
111 |
|
123 |
c->decomp_size - c->zstream.avail_out); |
112 |
|
123 |
ff_msrle_decode(avctx, frame, c->bpp, &c->gb); |
113 |
|
|
} |
114 |
|
|
|
115 |
|
|
/* make the palette available on the way out */ |
116 |
✗✓ |
123 |
if (c->avctx->pix_fmt == AV_PIX_FMT_PAL8) { |
117 |
|
|
frame->palette_has_changed = palette_has_changed; |
118 |
|
|
memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); |
119 |
|
|
} |
120 |
|
|
|
121 |
✗✓ |
123 |
if ((ret = av_frame_ref(data, frame)) < 0) |
122 |
|
|
return ret; |
123 |
|
123 |
*got_frame = 1; |
124 |
|
|
|
125 |
|
|
/* always report that the buffer was completely consumed */ |
126 |
|
123 |
return buf_size; |
127 |
|
|
} |
128 |
|
|
|
129 |
|
4 |
static av_cold int decode_init(AVCodecContext *avctx) |
130 |
|
|
{ |
131 |
|
4 |
CamtasiaContext * const c = avctx->priv_data; |
132 |
|
|
int zret; // Zlib return code |
133 |
|
|
|
134 |
|
4 |
c->avctx = avctx; |
135 |
|
|
|
136 |
|
4 |
c->height = avctx->height; |
137 |
|
|
|
138 |
|
|
// Needed if zlib unused or init aborted before inflateInit |
139 |
|
4 |
memset(&c->zstream, 0, sizeof(z_stream)); |
140 |
✗✓✗✓ ✗ |
4 |
switch(avctx->bits_per_coded_sample){ |
141 |
|
|
case 8: avctx->pix_fmt = AV_PIX_FMT_PAL8; break; |
142 |
|
2 |
case 16: avctx->pix_fmt = AV_PIX_FMT_RGB555; break; |
143 |
|
|
case 24: |
144 |
|
|
avctx->pix_fmt = AV_PIX_FMT_BGR24; |
145 |
|
|
break; |
146 |
|
2 |
case 32: avctx->pix_fmt = AV_PIX_FMT_0RGB32; break; |
147 |
|
|
default: av_log(avctx, AV_LOG_ERROR, "Camtasia error: unknown depth %i bpp\n", avctx->bits_per_coded_sample); |
148 |
|
|
return AVERROR_PATCHWELCOME; |
149 |
|
|
} |
150 |
|
4 |
c->bpp = avctx->bits_per_coded_sample; |
151 |
|
|
// buffer size for RLE 'best' case when 2-byte code precedes each pixel and there may be padding after it too |
152 |
|
4 |
c->decomp_size = (((avctx->width * c->bpp + 7) >> 3) + 3 * avctx->width + 2) * avctx->height + 2; |
153 |
|
|
|
154 |
|
|
/* Allocate decompression buffer */ |
155 |
✓✗ |
4 |
if (c->decomp_size) { |
156 |
✗✓ |
4 |
if (!(c->decomp_buf = av_malloc(c->decomp_size))) { |
157 |
|
|
av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n"); |
158 |
|
|
return AVERROR(ENOMEM); |
159 |
|
|
} |
160 |
|
|
} |
161 |
|
|
|
162 |
|
4 |
c->zstream.zalloc = Z_NULL; |
163 |
|
4 |
c->zstream.zfree = Z_NULL; |
164 |
|
4 |
c->zstream.opaque = Z_NULL; |
165 |
|
4 |
zret = inflateInit(&c->zstream); |
166 |
✗✓ |
4 |
if (zret != Z_OK) { |
167 |
|
|
av_log(avctx, AV_LOG_ERROR, "Inflate init error: %d\n", zret); |
168 |
|
|
return AVERROR_UNKNOWN; |
169 |
|
|
} |
170 |
|
|
|
171 |
|
4 |
c->frame = av_frame_alloc(); |
172 |
✗✓ |
4 |
if (!c->frame) |
173 |
|
|
return AVERROR(ENOMEM); |
174 |
|
|
|
175 |
|
4 |
return 0; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
4 |
static av_cold int decode_end(AVCodecContext *avctx) |
179 |
|
|
{ |
180 |
|
4 |
CamtasiaContext * const c = avctx->priv_data; |
181 |
|
|
|
182 |
|
4 |
av_freep(&c->decomp_buf); |
183 |
|
4 |
av_frame_free(&c->frame); |
184 |
|
|
|
185 |
|
4 |
inflateEnd(&c->zstream); |
186 |
|
|
|
187 |
|
4 |
return 0; |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
AVCodec ff_tscc_decoder = { |
191 |
|
|
.name = "camtasia", |
192 |
|
|
.long_name = NULL_IF_CONFIG_SMALL("TechSmith Screen Capture Codec"), |
193 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
194 |
|
|
.id = AV_CODEC_ID_TSCC, |
195 |
|
|
.priv_data_size = sizeof(CamtasiaContext), |
196 |
|
|
.init = decode_init, |
197 |
|
|
.close = decode_end, |
198 |
|
|
.decode = decode_frame, |
199 |
|
|
.capabilities = AV_CODEC_CAP_DR1, |
200 |
|
|
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP, |
201 |
|
|
}; |