From 4d681269e0966446b21a324c15c5e5e6cef7c378 Mon Sep 17 00:00:00 2001 From: Paul B Mahol Date: Wed, 28 Jun 2017 19:24:29 +0200 Subject: [PATCH] avcodec/gdv: add decompression for 2 and 5 method Signed-off-by: Paul B Mahol --- libavcodec/gdv.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++ libavformat/gdv.c | 44 +++++++++++++++++-- 2 files changed, 148 insertions(+), 3 deletions(-) diff --git a/libavcodec/gdv.c b/libavcodec/gdv.c index 275af9a351..b324e4f26e 100644 --- a/libavcodec/gdv.c +++ b/libavcodec/gdv.c @@ -38,6 +38,11 @@ typedef struct GDVContext { unsigned scale_h, scale_v; } GDVContext; +typedef struct Bits8 { + uint8_t queue; + uint8_t fill; +} Bits8; + typedef struct Bits32 { uint32_t queue; uint8_t fill; @@ -124,6 +129,21 @@ static void rescale(GDVContext *gdv, uint8_t *dst, int w, int h, int scale_v, in gdv->scale_h = scale_h; } +static int read_bits2(Bits8 *bits, GetByteContext *gb) +{ + int res; + + if (bits->fill == 0) { + bits->queue |= bytestream2_get_byte(gb); + bits->fill = 8; + } + res = bits->queue >> 6; + bits->queue <<= 2; + bits->fill -= 2; + + return res; +} + static void fill_bits32(Bits32 *bits, GetByteContext *gb) { bits->queue = bytestream2_get_le32(gb); @@ -173,6 +193,87 @@ static void lz_copy(PutByteContext *pb, GetByteContext *g2, int offset, unsigned } } +static int decompress_2(AVCodecContext *avctx) +{ + GDVContext *gdv = avctx->priv_data; + GetByteContext *gb = &gdv->gb; + GetByteContext *g2 = &gdv->g2; + PutByteContext *pb = &gdv->pb; + Bits8 bits = { 0 }; + int c, i; + + bytestream2_init(g2, gdv->frame, gdv->frame_size); + bytestream2_skip_p(pb, PREAMBLE_SIZE); + + for (c = 0; c < 256; c++) { + for (i = 0; i < 16; i++) { + gdv->frame[c * 16 + i] = c; + } + } + + while (bytestream2_get_bytes_left_p(pb) > 0 && bytestream2_get_bytes_left(gb) > 0) { + int tag = read_bits2(&bits, gb); + if (tag == 0) { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + } else if (tag == 1) { + int b = bytestream2_get_byte(gb); + int len = (b & 0xF) + 3; + int top = (b >> 4) & 0xF; + int off = (bytestream2_get_byte(gb) << 4) + top - 4096; + lz_copy(pb, g2, off, len); + } else if (tag == 2) { + int len = (bytestream2_get_byte(gb)) + 2; + bytestream2_skip_p(pb, len); + } else { + break; + } + } + return 0; +} + +static int decompress_5(AVCodecContext *avctx, unsigned skip) +{ + GDVContext *gdv = avctx->priv_data; + GetByteContext *gb = &gdv->gb; + GetByteContext *g2 = &gdv->g2; + PutByteContext *pb = &gdv->pb; + Bits8 bits = { 0 }; + + bytestream2_init(g2, gdv->frame, gdv->frame_size); + bytestream2_skip_p(pb, skip + PREAMBLE_SIZE); + + while (bytestream2_get_bytes_left_p(pb) > 0 && bytestream2_get_bytes_left(gb) > 0) { + int tag = read_bits2(&bits, gb); + if (tag == 0) { + bytestream2_put_byte(pb, bytestream2_get_byte(gb)); + } else if (tag == 1) { + int b = bytestream2_get_byte(gb); + int len = (b & 0xF) + 3; + int top = b >> 4; + int off = (bytestream2_get_byte(gb) << 4) + top - 4096; + lz_copy(pb, g2, off, len); + } else if (tag == 2) { + int len; + int b = bytestream2_get_byte(gb); + if (b == 0) { + break; + } + if (b != 0xFF) { + len = b; + } else { + len = bytestream2_get_le16(gb); + } + bytestream2_skip_p(pb, len + 1); + } else { + int b = bytestream2_get_byte(gb); + int len = (b & 0x3) + 2; + int off = -(b >> 2) - 1; + lz_copy(pb, g2, off, len); + } + } + return 0; +} + static int decompress_68(AVCodecContext *avctx, unsigned skip, unsigned use8) { GDVContext *gdv = avctx->priv_data; @@ -333,8 +434,14 @@ static int gdv_decode_frame(AVCodecContext *avctx, void *data, gdv->pal[i] = 0xFFU << 24 | r << 18 | g << 10 | b << 2; } break; + case 2: + ret = decompress_2(avctx); + break; case 3: break; + case 5: + ret = decompress_5(avctx, flags >> 8); + break; case 6: ret = decompress_68(avctx, flags >> 8, 0); break; diff --git a/libavformat/gdv.c b/libavformat/gdv.c index 87bba2fdbd..90692bd61c 100644 --- a/libavformat/gdv.c +++ b/libavformat/gdv.c @@ -42,14 +42,40 @@ static int gdv_read_probe(AVProbeData *p) return 0; } +struct { + uint16_t id; + uint16_t width; + uint16_t height; +} FixedSize[] = { + { 0, 320, 200}, + { 1, 640, 200}, + { 2, 320, 167}, + { 3, 320, 180}, + { 4, 320, 400}, + { 5, 320, 170}, + { 6, 160, 85}, + { 7, 160, 83}, + { 8, 160, 90}, + { 9, 280, 128}, + {10, 320, 240}, + {11, 320, 201}, + {16, 640, 400}, + {17, 640, 200}, + {18, 640, 180}, + {19, 640, 167}, + {20, 640, 170}, + {21, 320, 240} +}; + static int gdv_read_header(AVFormatContext *ctx) { GDVContext *gdv = ctx->priv_data; AVIOContext *pb = ctx->pb; AVStream *vst, *ast; - unsigned fps, snd_flags, vid_depth; + unsigned fps, snd_flags, vid_depth, size_id; - avio_skip(pb, 6); + avio_skip(pb, 4); + size_id = avio_rl16(pb); vst = avformat_new_stream(ctx, 0); if (!vst) @@ -91,6 +117,18 @@ static int gdv_read_header(AVFormatContext *ctx) vst->codecpar->width = avio_rl16(pb); vst->codecpar->height = avio_rl16(pb); + if (vst->codecpar->width == 0 || vst->codecpar->height == 0) { + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(FixedSize) - 1; i++) { + if (FixedSize[i].id == size_id) + break; + } + + vst->codecpar->width = FixedSize[i].width; + vst->codecpar->height = FixedSize[i].height; + } + avpriv_set_pts_info(vst, 64, 1, fps); if (vid_depth & 1) { @@ -100,7 +138,7 @@ static int gdv_read_header(AVFormatContext *ctx) unsigned r = avio_r8(pb); unsigned g = avio_r8(pb); unsigned b = avio_r8(pb); - gdv->pal[i] = 0xFF << 24 | r << 18 | g << 10 | b << 2; + gdv->pal[i] = 0xFFU << 24 | r << 18 | g << 10 | b << 2; } }