History

avcodec: add PhotoCD decoder

Paul B Mahol committed 6 years ago

Commit c8e38951 parent f901d758 files · +487 −1Browse files

Unified · Split

8 files
  1. Changelog +1 −0
  2. libavcodec/Makefile +1 −0
  3. libavcodec/allcodecs.c +1 −0
  4. libavcodec/codec_desc.c +7 −0
  5. libavcodec/codec_id.h +1 −0
  6. libavcodec/photocd.c +474 −0
  7. libavcodec/version.h +1 −1
  8. libavformat/img2.c +1 −0
Changelog+1−0
@@ -20,6 +20,7 @@ version <next>: · 19 unmodified lines
2020 - MobiClip video decoder
2121 - MOFLEX demuxer
2222 - MODS demuxer
23+- PhotoCD decoder
2324
2425
2526 version 4.3:
libavcodec/Makefile+1−0
@@ -541,6 +541,7 @@ OBJS-$(CONFIG_PGMYUV_DECODER) += pnmdec.o pnm.o · 540 unmodified lines
541541 OBJS-$(CONFIG_PGMYUV_ENCODER) += pnmenc.o
542542 OBJS-$(CONFIG_PGSSUB_DECODER) += pgssubdec.o
543543 OBJS-$(CONFIG_PGX_DECODER) += pgxdec.o
544+OBJS-$(CONFIG_PHOTOCD_DECODER) += photocd.o
544545 OBJS-$(CONFIG_PICTOR_DECODER) += pictordec.o cga_data.o
545546 OBJS-$(CONFIG_PIXLET_DECODER) += pixlet.o
546547 OBJS-$(CONFIG_PJS_DECODER) += textdec.o ass.o
libavcodec/allcodecs.c+1−0
@@ -241,6 +241,7 @@ extern AVCodec ff_pgm_decoder; · 240 unmodified lines
241241 extern AVCodec ff_pgmyuv_encoder;
242242 extern AVCodec ff_pgmyuv_decoder;
243243 extern AVCodec ff_pgx_decoder;
244+extern AVCodec ff_photocd_decoder;
244245 extern AVCodec ff_pictor_decoder;
245246 extern AVCodec ff_pixlet_decoder;
246247 extern AVCodec ff_png_encoder;
libavcodec/codec_desc.c+7−0
@@ -1791,6 +1791,13 @@ static const AVCodecDescriptor codec_descriptors[] = { · 1790 unmodified lines
17911791 .long_name = NULL_IF_CONFIG_SMALL("MobiClip Video"),
17921792 .props = AV_CODEC_PROP_LOSSY,
17931793 },
1794+ {
1795+ .id = AV_CODEC_ID_PHOTOCD,
1796+ .type = AVMEDIA_TYPE_VIDEO,
1797+ .name = "photocd",
1798+ .long_name = NULL_IF_CONFIG_SMALL("Kodak Photo CD"),
1799+ .props = AV_CODEC_PROP_LOSSY,
1800+ },
17941801
17951802 /* various PCM "codecs" */
17961803 {
libavcodec/codec_id.h+1−0
@@ -297,6 +297,7 @@ enum AVCodecID { · 296 unmodified lines
297297 AV_CODEC_ID_NOTCHLC,
298298 AV_CODEC_ID_PFM,
299299 AV_CODEC_ID_MOBICLIP,
300+ AV_CODEC_ID_PHOTOCD,
300301
301302 /* various PCM "codecs" */
302303 AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
libavcodec/photocd.c+474−0
@@ -0,0 +1,474 @@
1+/*
2+ * Kodak PhotoCD (a.k.a. ImagePac) image decoder
3+ *
4+ * Copyright (c) 1996-2002 Gerd Knorr
5+ * Copyright (c) 2010 Kenneth Vermeirsch
6+ * Copyright (c) 2020 Paul B Mahol
7+ *
8+ * This file is part of FFmpeg.
9+ *
10+ * FFmpeg is free software; you can redistribute it and/or
11+ * modify it under the terms of the GNU Lesser General Public
12+ * License as published by the Free Software Foundation; either
13+ * version 2.1 of the License, or (at your option) any later version.
14+ *
15+ * FFmpeg is distributed in the hope that it will be useful,
16+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
17+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18+ * Lesser General Public License for more details.
19+ *
20+ * You should have received a copy of the GNU Lesser General Public
21+ * License along with FFmpeg; if not, write to the Free Software
22+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23+ */
24+
25+/**
26+ * @file
27+ * Kodak PhotoCD (a.k.a. ImagePac) image decoder
28+ *
29+ * Supports resolutions up to 3072x2048.
30+ */
31+
32+#define CACHED_BITSTREAM_READER !ARCH_X86_32
33+
34+#include "libavutil/avassert.h"
35+#include "libavutil/intreadwrite.h"
36+#include "libavutil/opt.h"
37+#include "avcodec.h"
38+#include "bytestream.h"
39+#include "get_bits.h"
40+#include "internal.h"
41+#include "thread.h"
42+
43+typedef struct PhotoCDContext {
44+ AVClass *class;
45+ int lowres;
46+
47+ GetByteContext gb;
48+ int thumbnails; //* number of thumbnails; 0 for normal image */
49+ int resolution;
50+ int orientation;
51+
52+ int streampos;
53+
54+ uint8_t bits[256];
55+ uint16_t codes[256];
56+ uint8_t syms[256];
57+
58+ VLC vlc[3];
59+} PhotoCDContext;
60+
61+typedef struct ImageInfo {
62+ uint32_t start;
63+ uint16_t width, height;
64+} ImageInfo;
65+
66+static const ImageInfo img_info[6] = {
67+ {8192, 192, 128},
68+ {47104, 384, 256},
69+ {196608, 768, 512},
70+ {0, 1536, 1024},
71+ {0, 3072, 2048},
72+ {0, 6144, 4096},
73+};
74+
75+static av_noinline void interp_lowres(PhotoCDContext *s, AVFrame *picture,
76+ int width, int height)
77+{
78+ GetByteContext *gb = &s->gb;
79+ int start = s->streampos + img_info[2].start;
80+ uint8_t *ptr, *ptr1, *ptr2;
81+ uint8_t *dst;
82+ int fill;
83+
84+ ptr = picture->data[0];
85+ ptr1 = picture->data[1];
86+ ptr2 = picture->data[2];
87+
88+ bytestream2_seek(gb, start, SEEK_SET);
89+
90+ for (int y = 0; y < height; y += 2) {
91+ dst = ptr;
92+ for (int x = 0; x < width - 1; x++) {
93+ fill = bytestream2_get_byte(gb);
94+ *(dst++) = fill;
95+ *(dst++) = (fill + bytestream2_peek_byte(gb) + 1) >> 1;
96+ }
97+ fill = bytestream2_get_byte(gb);
98+ *(dst++) = fill;
99+ *(dst++) = fill;
100+
101+ ptr += picture->linesize[0] << 1;
102+
103+ dst = ptr;
104+ for (int x = 0; x < width - 1; x++) {
105+ fill = bytestream2_get_byte(gb);
106+ *(dst++) = fill;
107+ *(dst++) = (fill + bytestream2_peek_byte(gb) + 1) >> 1;
108+ }
109+ fill = bytestream2_get_byte(gb);
110+ *(dst++) = fill;
111+ *(dst++) = fill;
112+
113+ ptr += picture->linesize[0] << 1;
114+
115+ dst = ptr1;
116+ for (int x = 0; x < (width >> 1) - 1; x++) {
117+ fill = bytestream2_get_byte(gb);
118+ *(dst++) = fill;
119+ *(dst++) = (fill + bytestream2_peek_byte(gb) + 1) >> 1;
120+ }
121+ fill = bytestream2_get_byte(gb);
122+ *(dst++) = fill;
123+ *(dst++) = fill;
124+
125+ ptr1 += picture->linesize[1] << 1;
126+
127+ dst = ptr2;
128+ for (int x = 0; x < (width >> 1) - 1; x++) {
129+ fill = bytestream2_get_byte(gb);
130+ *(dst++) = fill;
131+ *(dst++) = (fill + bytestream2_peek_byte(gb) + 1) >> 1;
132+ }
133+ fill = bytestream2_get_byte(gb);
134+ *(dst++) = fill;
135+ *(dst++) = fill;
136+
137+ ptr2 += picture->linesize[2] << 1;
138+ }
139+
140+ s->streampos += bytestream2_tell(gb) - start;
141+}
142+
143+static av_noinline void interp_lines(uint8_t *ptr, int linesize,
144+ int width, int height)
145+{
146+ const uint8_t *src1;
147+ uint8_t *dst;
148+ int x;
149+
150+ for (int y = 0; y < height - 2; y += 2) {
151+ const uint8_t *src1 = ptr;
152+ uint8_t *dst = ptr + linesize;
153+ const uint8_t *src2 = dst + linesize;
154+ for (x = 0; x < width - 2; x += 2) {
155+ dst[x] = (src1[x] + src2[x] + 1) >> 1;
156+ dst[x + 1] = (src1[x] + src2[x] + src1[x + 2] + src2[x + 2] + 2) >> 2;
157+ }
158+ dst[x] = dst[x + 1] = (src1[x] + src2[x] + 1) >> 1;
159+
160+ ptr += linesize << 1;
161+ }
162+
163+ src1 = ptr;
164+ dst = ptr + linesize;
165+ for (x = 0; x < width - 2; x += 2) {
166+ dst[x] = src1[x];
167+ dst[x + 1] = (src1[x] + src1[x + 2] + 1) >> 1;
168+ }
169+ dst[x] = dst[x + 1] = src1[x];
170+}
171+
172+static av_noinline void interp_pixels(uint8_t *ptr, int linesize,
173+ int width, int height)
174+{
175+ for (int y = height - 2; y >= 0; y -= 2) {
176+ const uint8_t *src = ptr + (y >> 1) * linesize;
177+ uint8_t *dst = ptr + y * linesize;
178+
179+ dst[width - 2] = dst[width - 1] = src[(width >> 1) - 1];
180+ for (int x = width - 4; x >= 0; x -= 2) {
181+ dst[x] = src[x >> 1];
182+ dst[x + 1] = (src[x >> 1] + src[(x >> 1) + 1] + 1) >> 1;
183+ }
184+ }
185+}
186+
187+static av_noinline int read_hufftable(AVCodecContext *avctx, VLC *vlc)
188+{
189+ PhotoCDContext *s = avctx->priv_data;
190+ GetByteContext *gb = &s->gb;
191+ int start = s->streampos;
192+ int count, ret;
193+
194+ bytestream2_seek(gb, start, SEEK_SET);
195+
196+ count = bytestream2_get_byte(gb) + 1;
197+ if (bytestream2_get_bytes_left(gb) < count * 4)
198+ return AVERROR_INVALIDDATA;
199+
200+ for (int j = 0; j < count; j++) {
201+ const int bit = bytestream2_get_byteu(gb) + 1;
202+ const int code = bytestream2_get_be16u(gb);
203+ const int sym = bytestream2_get_byteu(gb);
204+
205+ if (bit > 16)
206+ return AVERROR_INVALIDDATA;
207+
208+ s->bits[j] = bit;
209+ s->codes[j] = code >> (16 - bit);
210+ s->syms[j] = sym;
211+ }
212+
213+ ff_free_vlc(vlc);
214+ ret = ff_init_vlc_sparse(vlc, 12, count,
215+ s->bits, sizeof(*s->bits), sizeof(*s->bits),
216+ s->codes, sizeof(*s->codes), sizeof(*s->codes),
217+ s->syms, sizeof(*s->syms), sizeof(*s->syms), 0);
218+
219+ s->streampos = bytestream2_tell(gb);
220+
221+ return ret;
222+}
223+
224+static av_noinline int decode_huff(AVCodecContext *avctx, AVFrame *frame,
225+ int target_res, int curr_res)
226+{
227+ PhotoCDContext *s = avctx->priv_data;
228+ GetBitContext g;
229+ GetByteContext *gb = &s->gb;
230+ int ret, y = 0, type, height, y2;
231+ int start = s->streampos;
232+ unsigned shiftreg, bit;
233+ const int scaling = target_res - curr_res;
234+ const uint8_t type2idx[] = { 0, 0xff, 1, 2 };
235+
236+ bytestream2_seek(gb, start, SEEK_SET);
237+ ret = init_get_bits8(&g, gb->buffer, bytestream2_get_bytes_left(gb));
238+ if (ret < 0)
239+ return ret;
240+
241+ height = img_info[curr_res].height;
242+ y2 = avctx->height >> scaling;
243+
244+ while (y < height) {
245+ uint8_t *data;
246+ int x2, idx;
247+
248+ bit = 0;
249+ for (; get_bits_left(&g) > 0;) {
250+ if ((show_bits(&g, 24) & 0xfff000) == 0xfff000)
251+ break;
252+ skip_bits(&g, 8);
253+ }
254+
255+ shiftreg = show_bits_long(&g, 32) & 0xffffff00;
256+ while (shiftreg != 0xfffffe00) {
257+ if (get_bits_left(&g) <= 0)
258+ return AVERROR_INVALIDDATA;
259+ skip_bits(&g, 1);
260+ shiftreg = show_bits_long(&g, 32) & 0xffffff00;
261+ }
262+ skip_bits(&g, 16);
263+ y = show_bits_long(&g, 23) & 0x1fff;
264+ skip_bits(&g, 8);
265+ if (y >= height)
266+ break;
267+ type = get_bits(&g, 2);
268+ skip_bits(&g, 14);
269+
270+ if (type == 1)
271+ return AVERROR_INVALIDDATA;
272+ idx = type2idx[type];
273+
274+ data = frame->data[idx] + (y >> !!idx) * frame->linesize[idx];
275+
276+ x2 = avctx->width >> (scaling + !!idx);
277+ for (int x = 0; x < x2; x++) {
278+ int m;
279+
280+ if (get_bits_left(&g) <= 0)
281+ return AVERROR_INVALIDDATA;
282+ m = get_vlc2(&g, s->vlc[idx].table, s->vlc[idx].bits, 2);
283+ if (m < 0)
284+ return AVERROR_INVALIDDATA;
285+ m = sign_extend(m, 8);
286+ data[x] = av_clip_uint8(data[x] + m);
287+ }
288+ }
289+
290+ s->streampos += (get_bits_count(&g) + 7) >> 3;
291+ s->streampos = (s->streampos + 0x6000 + 2047) & ~0x7ff;
292+
293+ return 0;
294+}
295+
296+static int photocd_decode_frame(AVCodecContext *avctx, void *data,
297+ int *got_frame, AVPacket *avpkt)
298+{
299+ PhotoCDContext *s = avctx->priv_data;
300+ ThreadFrame frame = { .f = data };
301+ const uint8_t *buf = avpkt->data;
302+ GetByteContext *gb = &s->gb;
303+ AVFrame *p = data;
304+ uint8_t *ptr, *ptr1, *ptr2;
305+ int ret;
306+
307+ if (avpkt->size < img_info[0].start)
308+ return AVERROR_INVALIDDATA;
309+
310+ if (!memcmp("PCD_OPA", buf, 7)) {
311+ s->thumbnails = AV_RL16(buf + 10);
312+ av_log(avctx, AV_LOG_WARNING, "this is a thumbnails file, "
313+ "reading first thumbnail only\n");
314+ } else if (avpkt->size < 786432) {
315+ return AVERROR_INVALIDDATA;
316+ } else if (memcmp("PCD_IPI", buf + 0x800, 7)) {
317+ return AVERROR_INVALIDDATA;
318+ }
319+
320+ s->orientation = s->thumbnails ? buf[12] & 3 : buf[0x48] & 3;
321+
322+ if (s->thumbnails)
323+ s->resolution = 0;
324+ else if (avpkt->size <= 788480)
325+ s->resolution = 2;
326+ else
327+ s->resolution = av_clip(4 - s->lowres, 0, 4);
328+
329+ avctx->width = img_info[s->resolution].width;
330+ avctx->height = img_info[s->resolution].height;
331+
332+ if ((ret = ff_thread_get_buffer(avctx, &frame, 0)) < 0)
333+ return ret;
334+
335+ p->pict_type = AV_PICTURE_TYPE_I;
336+ p->key_frame = 1;
337+
338+ bytestream2_init(gb, avpkt->data, avpkt->size);
339+
340+ if (s->resolution < 3) {
341+ ptr = p->data[0];
342+ ptr1 = p->data[1];
343+ ptr2 = p->data[2];
344+
345+ if (s->thumbnails)
346+ bytestream2_seek(gb, 10240, SEEK_SET);
347+ else
348+ bytestream2_seek(gb, img_info[s->resolution].start, SEEK_SET);
349+
350+ for (int y = 0; y < avctx->height; y += 2) {
351+ bytestream2_get_buffer(gb, ptr, avctx->width);
352+ ptr += p->linesize[0];
353+
354+ bytestream2_get_buffer(gb, ptr, avctx->width);
355+ ptr += p->linesize[0];
356+
357+ bytestream2_get_buffer(gb, ptr1, avctx->width >> 1);
358+ ptr1 += p->linesize[1];
359+
360+ bytestream2_get_buffer(gb, ptr2, avctx->width >> 1);
361+ ptr2 += p->linesize[2];
362+ }
363+ } else {
364+ s->streampos = 0;
365+ ptr = p->data[0];
366+ ptr1 = p->data[1];
367+ ptr2 = p->data[2];
368+
369+ interp_lowres(s, p, img_info[2].width, img_info[2].height);
370+
371+ interp_lines(ptr1, p->linesize[1], img_info[2].width, img_info[2].height);
372+ interp_lines(ptr2, p->linesize[2], img_info[2].width, img_info[2].height);
373+
374+ if (s->resolution == 4) {
375+ interp_pixels(ptr1, p->linesize[1], img_info[3].width, img_info[3].height);
376+ interp_lines (ptr1, p->linesize[1], img_info[3].width, img_info[3].height);
377+ interp_pixels(ptr2, p->linesize[2], img_info[3].width, img_info[3].height);
378+ interp_lines (ptr2, p->linesize[2], img_info[3].width, img_info[3].height);
379+ }
380+
381+ interp_lines(ptr, p->linesize[0], img_info[3].width, img_info[3].height);
382+
383+ s->streampos = 0xc2000;
384+ for (int n = 0; n < 3; n++) {
385+ if ((ret = read_hufftable(avctx, &s->vlc[n])) < 0)
386+ return ret;
387+ }
388+ s->streampos = (s->streampos + 2047) & ~0x3ff;
389+ if (decode_huff(avctx, p, s->resolution, 3) < 0)
390+ return AVERROR_INVALIDDATA;
391+
392+ if (s->resolution == 4) {
393+ interp_pixels(ptr, p->linesize[0], img_info[4].width, img_info[4].height);
394+ interp_lines (ptr, p->linesize[0], img_info[4].width, img_info[4].height);
395+
396+ for (int n = 0; n < 3; n++) {
397+ if ((ret = read_hufftable(avctx, &s->vlc[n])) < 0)
398+ return ret;
399+ }
400+ s->streampos = (s->streampos + 2047) & ~0x3ff;
401+ if (decode_huff(avctx, p, 4, 4) < 0)
402+ return AVERROR_INVALIDDATA;
403+ }
404+ }
405+
406+ {
407+ ptr1 = p->data[1];
408+ ptr2 = p->data[2];
409+
410+ for (int y = 0; y < avctx->height >> 1; y++) {
411+ for (int x = 0; x < avctx->width >> 1; x++) {
412+ ptr1[x] = av_clip_uint8(ptr1[x] - 28);
413+ ptr2[x] = av_clip_uint8(ptr2[x] - 9);
414+ }
415+
416+ ptr1 += p->linesize[1];
417+ ptr2 += p->linesize[2];
418+ }
419+ }
420+
421+ *got_frame = 1;
422+
423+ return 0;
424+}
425+
426+static av_cold int photocd_decode_init(AVCodecContext *avctx)
427+{
428+ avctx->pix_fmt = AV_PIX_FMT_YUV420P;
429+ avctx->colorspace = AVCOL_SPC_BT709;
430+ avctx->color_primaries = AVCOL_PRI_BT709;
431+ avctx->color_trc = AVCOL_TRC_IEC61966_2_1;
432+ avctx->color_range = AVCOL_RANGE_JPEG;
433+
434+ return 0;
435+}
436+
437+static av_cold int photocd_decode_close(AVCodecContext *avctx)
438+{
439+ PhotoCDContext *s = avctx->priv_data;
440+
441+ for (int i = 0; i < 3; i++)
442+ ff_free_vlc(&s->vlc[i]);
443+
444+ return 0;
445+}
446+
447+#define OFFSET(x) offsetof(PhotoCDContext, x)
448+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
449+
450+static const AVOption options[] = {
451+ { "lowres", "Lower the decoding resolution by a power of two",
452+ OFFSET(lowres), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 4, VD },
453+ { NULL },
454+};
455+
456+static const AVClass photocd_class = {
457+ .class_name = "photocd",
458+ .item_name = av_default_item_name,
459+ .option = options,
460+ .version = LIBAVUTIL_VERSION_INT,
461+};
462+
463+AVCodec ff_photocd_decoder = {
464+ .name = "photocd",
465+ .type = AVMEDIA_TYPE_VIDEO,
466+ .id = AV_CODEC_ID_PHOTOCD,
467+ .priv_data_size = sizeof(PhotoCDContext),
468+ .priv_class = &photocd_class,
469+ .init = photocd_decode_init,
470+ .close = photocd_decode_close,
471+ .decode = photocd_decode_frame,
472+ .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS,
473+ .long_name = NULL_IF_CONFIG_SMALL("Kodak Photo CD"),
474+};
libavcodec/version.h+1−1
@@ -28,7 +28,7 @@ · 27 unmodified lines
2828 #include "libavutil/version.h"
2929
3030 #define LIBAVCODEC_VERSION_MAJOR 58
31−#define LIBAVCODEC_VERSION_MINOR 104
31+#define LIBAVCODEC_VERSION_MINOR 105
3232 #define LIBAVCODEC_VERSION_MICRO 100
3333
3434 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
libavformat/img2.c+1−0
@@ -55,6 +55,7 @@ const IdStrMap ff_img_tags[] = { · 54 unmodified lines
5555 { AV_CODEC_ID_TIFF, "dng" },
5656 { AV_CODEC_ID_SGI, "sgi" },
5757 { AV_CODEC_ID_PTX, "ptx" },
58+ { AV_CODEC_ID_PHOTOCD, "pcd" },
5859 { AV_CODEC_ID_PCX, "pcx" },
5960 { AV_CODEC_ID_QDRAW, "pic" },
6061 { AV_CODEC_ID_QDRAW, "pct" },

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t