Coverage Report

Created: 2025-08-28 07:12

/src/ffmpeg/libavcodec/tscc.c
Line
Count
Source (jump to first uncovered line)
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 "libavutil/mem.h"
38
#include "avcodec.h"
39
#include "codec_internal.h"
40
#include "decode.h"
41
#include "msrledec.h"
42
#include "zlib_wrapper.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
    FFZStream zstream;
60
61
    uint32_t pal[256];
62
} CamtasiaContext;
63
64
static int decode_frame(AVCodecContext *avctx, AVFrame *rframe,
65
                        int *got_frame, AVPacket *avpkt)
66
165k
{
67
165k
    const uint8_t *buf = avpkt->data;
68
165k
    int buf_size = avpkt->size;
69
165k
    CamtasiaContext * const c = avctx->priv_data;
70
165k
    z_stream *const zstream = &c->zstream.zstream;
71
165k
    AVFrame *frame = c->frame;
72
165k
    int ret;
73
165k
    int palette_has_changed = 0;
74
75
165k
    if (c->avctx->pix_fmt == AV_PIX_FMT_PAL8) {
76
19.0k
        palette_has_changed = ff_copy_palette(c->pal, avpkt, avctx);
77
19.0k
    }
78
79
165k
    ret = inflateReset(zstream);
80
165k
    if (ret != Z_OK) {
81
0
        av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
82
0
        return AVERROR_UNKNOWN;
83
0
    }
84
165k
    zstream->next_in   = buf;
85
165k
    zstream->avail_in  = buf_size;
86
165k
    zstream->next_out  = c->decomp_buf;
87
165k
    zstream->avail_out = c->decomp_size;
88
165k
    ret = inflate(zstream, Z_FINISH);
89
    // Z_DATA_ERROR means empty picture
90
165k
    if (ret == Z_DATA_ERROR && !palette_has_changed) {
91
123k
        return buf_size;
92
123k
    }
93
94
42.0k
    if ((ret != Z_OK) && (ret != Z_STREAM_END) && (ret != Z_DATA_ERROR)) {
95
25.3k
        av_log(avctx, AV_LOG_ERROR, "Inflate error: %d\n", ret);
96
25.3k
        return AVERROR_UNKNOWN;
97
25.3k
    }
98
99
16.7k
    if ((ret = ff_reget_buffer(avctx, frame, 0)) < 0)
100
872
        return ret;
101
102
15.8k
    if (ret != Z_DATA_ERROR) {
103
15.8k
        bytestream2_init(&c->gb, c->decomp_buf,
104
15.8k
                         c->decomp_size - zstream->avail_out);
105
15.8k
        ff_msrle_decode(avctx, frame, c->bpp, &c->gb);
106
15.8k
    }
107
108
    /* make the palette available on the way out */
109
15.8k
    if (c->avctx->pix_fmt == AV_PIX_FMT_PAL8) {
110
6.16k
        memcpy(frame->data[1], c->pal, AVPALETTE_SIZE);
111
6.16k
    }
112
113
15.8k
    if ((ret = av_frame_ref(rframe, frame)) < 0)
114
0
        return ret;
115
15.8k
    *got_frame      = 1;
116
117
    /* always report that the buffer was completely consumed */
118
15.8k
    return buf_size;
119
15.8k
}
120
121
static av_cold int decode_init(AVCodecContext *avctx)
122
1.11k
{
123
1.11k
    CamtasiaContext * const c = avctx->priv_data;
124
125
1.11k
    c->avctx = avctx;
126
127
1.11k
    c->height = avctx->height;
128
129
1.11k
    switch(avctx->bits_per_coded_sample){
130
240
    case  8: avctx->pix_fmt = AV_PIX_FMT_PAL8; break;
131
266
    case 16: avctx->pix_fmt = AV_PIX_FMT_RGB555; break;
132
205
    case 24:
133
205
             avctx->pix_fmt = AV_PIX_FMT_BGR24;
134
205
             break;
135
278
    case 32: avctx->pix_fmt = AV_PIX_FMT_0RGB32; break;
136
130
    default: av_log(avctx, AV_LOG_ERROR, "Camtasia error: unknown depth %i bpp\n", avctx->bits_per_coded_sample);
137
130
             return AVERROR_PATCHWELCOME;
138
1.11k
    }
139
989
    c->bpp = avctx->bits_per_coded_sample;
140
    // buffer size for RLE 'best' case when 2-byte code precedes each pixel and there may be padding after it too
141
989
    c->decomp_size = (((avctx->width * c->bpp + 7) >> 3) + 3 * avctx->width + 2) * avctx->height + 2;
142
143
    /* Allocate decompression buffer */
144
989
    if (c->decomp_size) {
145
989
        if (!(c->decomp_buf = av_malloc(c->decomp_size))) {
146
0
            av_log(avctx, AV_LOG_ERROR, "Can't allocate decompression buffer.\n");
147
0
            return AVERROR(ENOMEM);
148
0
        }
149
989
    }
150
151
989
    c->frame = av_frame_alloc();
152
989
    if (!c->frame)
153
0
        return AVERROR(ENOMEM);
154
155
989
    return ff_inflate_init(&c->zstream, avctx);
156
989
}
157
158
static av_cold int decode_end(AVCodecContext *avctx)
159
1.11k
{
160
1.11k
    CamtasiaContext * const c = avctx->priv_data;
161
162
1.11k
    av_freep(&c->decomp_buf);
163
1.11k
    av_frame_free(&c->frame);
164
1.11k
    ff_inflate_end(&c->zstream);
165
166
1.11k
    return 0;
167
1.11k
}
168
169
const FFCodec ff_tscc_decoder = {
170
    .p.name         = "camtasia",
171
    CODEC_LONG_NAME("TechSmith Screen Capture Codec"),
172
    .p.type         = AVMEDIA_TYPE_VIDEO,
173
    .p.id           = AV_CODEC_ID_TSCC,
174
    .priv_data_size = sizeof(CamtasiaContext),
175
    .init           = decode_init,
176
    .close          = decode_end,
177
    FF_CODEC_DECODE_CB(decode_frame),
178
    .p.capabilities = AV_CODEC_CAP_DR1,
179
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
180
};