Merge commit '9af7a8523a6bb517834ebed36093bdab11a8b38e'
* commit '9af7a8523a6bb517834ebed36093bdab11a8b38e': HNM4/HNM4A demuxer & video decoder Conflicts: Changelog doc/general.texi libavcodec/Makefile libavcodec/allcodecs.c libavcodec/avcodec.h libavcodec/version.h libavformat/Makefile libavformat/allformats.c libavformat/version.h Merged-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
		
						commit
						a2bfee36b7
					
				@ -3,6 +3,8 @@ releases are sorted from youngest to oldest.
 | 
			
		||||
 | 
			
		||||
version <next>
 | 
			
		||||
 | 
			
		||||
- HNM version 4 demuxer and video decoder
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
version 2.1:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -249,6 +249,8 @@ library:
 | 
			
		||||
@item GXF                       @tab X @tab X
 | 
			
		||||
    @tab General eXchange Format SMPTE 360M, used by Thomson Grass Valley
 | 
			
		||||
         playout servers.
 | 
			
		||||
@item HNM @tab   @tab X
 | 
			
		||||
    @tab Only version 4 supported, used in some games from Cryo Interactive
 | 
			
		||||
@item iCEDraw File              @tab   @tab X
 | 
			
		||||
@item ICO                       @tab X @tab X
 | 
			
		||||
    @tab Microsoft Windows ICO
 | 
			
		||||
@ -607,6 +609,7 @@ following image formats are supported:
 | 
			
		||||
@item H.263+ / H.263-1998 / H.263 version 2  @tab  X  @tab  X
 | 
			
		||||
@item H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10  @tab  E  @tab  X
 | 
			
		||||
    @tab encoding supported through external library libx264
 | 
			
		||||
@item HNM version 4          @tab     @tab  X
 | 
			
		||||
@item HuffYUV                @tab  X  @tab  X
 | 
			
		||||
@item HuffYUV FFmpeg variant @tab  X  @tab  X
 | 
			
		||||
@item IBM Ultimotion         @tab     @tab  X
 | 
			
		||||
 | 
			
		||||
@ -236,7 +236,7 @@ OBJS-$(CONFIG_H264_VDA_DECODER)        += vda_h264_dec.o
 | 
			
		||||
OBJS-$(CONFIG_HEVC_DECODER)            += hevc.o hevc_mvs.o hevc_ps.o hevc_sei.o \
 | 
			
		||||
                                          hevc_cabac.o hevc_refs.o hevcpred.o    \
 | 
			
		||||
                                          hevcdsp.o hevc_filter.o cabac.o
 | 
			
		||||
 | 
			
		||||
OBJS-$(CONFIG_HNM4_VIDEO_DECODER)      += hnm4video.o
 | 
			
		||||
OBJS-$(CONFIG_HUFFYUV_DECODER)         += huffyuv.o huffyuvdec.o
 | 
			
		||||
OBJS-$(CONFIG_HUFFYUV_ENCODER)         += huffyuv.o huffyuvenc.o
 | 
			
		||||
OBJS-$(CONFIG_IAC_DECODER)             += imc.o
 | 
			
		||||
 | 
			
		||||
@ -165,6 +165,7 @@ void avcodec_register_all(void)
 | 
			
		||||
    REGISTER_DECODER(H264_VDA,          h264_vda);
 | 
			
		||||
    REGISTER_DECODER(H264_VDPAU,        h264_vdpau);
 | 
			
		||||
    REGISTER_DECODER(HEVC,              hevc);
 | 
			
		||||
    REGISTER_DECODER(HNM4_VIDEO,        hnm4_video);
 | 
			
		||||
    REGISTER_ENCDEC (HUFFYUV,           huffyuv);
 | 
			
		||||
    REGISTER_DECODER(IDCIN,             idcin);
 | 
			
		||||
    REGISTER_DECODER(IFF_BYTERUN1,      iff_byterun1);
 | 
			
		||||
 | 
			
		||||
@ -276,6 +276,7 @@ enum AVCodecID {
 | 
			
		||||
    AV_CODEC_ID_ESCAPE130_DEPRECATED,
 | 
			
		||||
    AV_CODEC_ID_G2M_DEPRECATED,
 | 
			
		||||
    AV_CODEC_ID_WEBP_DEPRECATED,
 | 
			
		||||
    AV_CODEC_ID_HNM4_VIDEO,
 | 
			
		||||
 | 
			
		||||
    AV_CODEC_ID_BRENDER_PIX= MKBETAG('B','P','I','X'),
 | 
			
		||||
    AV_CODEC_ID_Y41P       = MKBETAG('Y','4','1','P'),
 | 
			
		||||
 | 
			
		||||
@ -1394,6 +1394,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
 | 
			
		||||
        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY |
 | 
			
		||||
                     AV_CODEC_PROP_LOSSLESS,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        .id        = AV_CODEC_ID_HNM4_VIDEO,
 | 
			
		||||
        .type      = AVMEDIA_TYPE_VIDEO,
 | 
			
		||||
        .name      = "hnm4video",
 | 
			
		||||
        .long_name = NULL_IF_CONFIG_SMALL("HNM 4 video"),
 | 
			
		||||
        .props     = AV_CODEC_PROP_LOSSY,
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /* various PCM "codecs" */
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										459
									
								
								libavcodec/hnm4video.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								libavcodec/hnm4video.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,459 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Cryo Interactive Entertainment HNM4 video decoder
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2012 David Kment
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of FFmpeg.
 | 
			
		||||
 *
 | 
			
		||||
 * FFmpeg is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * FFmpeg is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with FFmpeg; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include "libavutil/internal.h"
 | 
			
		||||
#include "libavutil/intreadwrite.h"
 | 
			
		||||
#include "libavutil/mem.h"
 | 
			
		||||
#include "avcodec.h"
 | 
			
		||||
#include "bytestream.h"
 | 
			
		||||
#include "internal.h"
 | 
			
		||||
 | 
			
		||||
#define HNM4_CHUNK_ID_PL 19536
 | 
			
		||||
#define HNM4_CHUNK_ID_IZ 23113
 | 
			
		||||
#define HNM4_CHUNK_ID_IU 21833
 | 
			
		||||
#define HNM4_CHUNK_ID_SD 17491
 | 
			
		||||
 | 
			
		||||
typedef struct Hnm4VideoContext {
 | 
			
		||||
    uint8_t version;
 | 
			
		||||
    uint16_t width;
 | 
			
		||||
    uint16_t height;
 | 
			
		||||
    uint8_t *current;
 | 
			
		||||
    uint8_t *previous;
 | 
			
		||||
    uint8_t *buffer1;
 | 
			
		||||
    uint8_t *buffer2;
 | 
			
		||||
    uint8_t *processed;
 | 
			
		||||
    uint32_t palette[256];
 | 
			
		||||
} Hnm4VideoContext;
 | 
			
		||||
 | 
			
		||||
static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits)
 | 
			
		||||
{
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (!*bits) {
 | 
			
		||||
        *bitbuf = bytestream2_get_le32(gb);
 | 
			
		||||
        *bits = 32;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = *bitbuf >> 31;
 | 
			
		||||
    *bitbuf <<= 1;
 | 
			
		||||
    (*bits)--;
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void unpack_intraframe(AVCodecContext *avctx, uint8_t *src,
 | 
			
		||||
                              uint32_t size)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
    GetByteContext gb;
 | 
			
		||||
    uint32_t bitbuf = 0, writeoffset = 0, count = 0;
 | 
			
		||||
    uint16_t word;
 | 
			
		||||
    int32_t offset;
 | 
			
		||||
    int bits = 0;
 | 
			
		||||
 | 
			
		||||
    bytestream2_init(&gb, src, size);
 | 
			
		||||
 | 
			
		||||
    while (bytestream2_tell(&gb) < size) {
 | 
			
		||||
        if (getbit(&gb, &bitbuf, &bits)) {
 | 
			
		||||
            if (writeoffset >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR,
 | 
			
		||||
                       "Attempting to write out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (getbit(&gb, &bitbuf, &bits)) {
 | 
			
		||||
                word   = bytestream2_get_le16(&gb);
 | 
			
		||||
                count  = word & 0x07;
 | 
			
		||||
                offset = (word >> 3) - 0x2000;
 | 
			
		||||
                if (!count)
 | 
			
		||||
                    count = bytestream2_get_byte(&gb);
 | 
			
		||||
                if (!count)
 | 
			
		||||
                    return;
 | 
			
		||||
            } else {
 | 
			
		||||
                count  = getbit(&gb, &bitbuf, &bits) * 2;
 | 
			
		||||
                count += getbit(&gb, &bitbuf, &bits);
 | 
			
		||||
                offset = bytestream2_get_byte(&gb) - 0x0100;
 | 
			
		||||
            }
 | 
			
		||||
            count  += 2;
 | 
			
		||||
            offset += writeoffset;
 | 
			
		||||
            if (offset < 0 || offset + count >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (writeoffset + count >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR,
 | 
			
		||||
                       "Attempting to write out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            while (count--) {
 | 
			
		||||
                hnm->current[writeoffset++] = hnm->current[offset++];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void postprocess_current_frame(AVCodecContext *avctx)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
    uint32_t x, y, src_x, src_y;
 | 
			
		||||
 | 
			
		||||
    for (y = 0; y < hnm->height; y++) {
 | 
			
		||||
        src_y = y - (y % 2);
 | 
			
		||||
        src_x = src_y * hnm->width + (y % 2);
 | 
			
		||||
        for (x = 0; x < hnm->width; x++) {
 | 
			
		||||
            hnm->processed[(y * hnm->width) + x] = hnm->current[src_x];
 | 
			
		||||
            src_x += 2;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
    uint8_t *src = hnm->processed;
 | 
			
		||||
    uint8_t *dst = frame->data[0];
 | 
			
		||||
    int y;
 | 
			
		||||
 | 
			
		||||
    for (y = 0; y < hnm->height; y++) {
 | 
			
		||||
        memcpy(dst, src, hnm->width);
 | 
			
		||||
        src += hnm->width;
 | 
			
		||||
        dst += frame->linesize[0];
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void decode_interframe_v4(AVCodecContext *avctx, uint8_t *src, uint32_t size)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
    GetByteContext gb;
 | 
			
		||||
    uint32_t writeoffset = 0, count, left, offset;
 | 
			
		||||
    uint8_t tag, previous, backline, backward, swap;
 | 
			
		||||
 | 
			
		||||
    bytestream2_init(&gb, src, size);
 | 
			
		||||
 | 
			
		||||
    while (bytestream2_tell(&gb) < size) {
 | 
			
		||||
        count = bytestream2_peek_byte(&gb) & 0x1F;
 | 
			
		||||
        if (count == 0) {
 | 
			
		||||
            tag = bytestream2_get_byte(&gb) & 0xE0;
 | 
			
		||||
            tag = tag >> 5;
 | 
			
		||||
            if (tag == 0) {
 | 
			
		||||
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
 | 
			
		||||
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
 | 
			
		||||
            } else if (tag == 1) {
 | 
			
		||||
                writeoffset += bytestream2_get_byte(&gb) * 2;
 | 
			
		||||
            } else if (tag == 2) {
 | 
			
		||||
                count = bytestream2_get_le16(&gb);
 | 
			
		||||
                count *= 2;
 | 
			
		||||
                writeoffset += count;
 | 
			
		||||
            } else if (tag == 3) {
 | 
			
		||||
                count = bytestream2_get_byte(&gb) * 2;
 | 
			
		||||
                while (count > 0) {
 | 
			
		||||
                    hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
 | 
			
		||||
                    count--;
 | 
			
		||||
                }
 | 
			
		||||
                bytestream2_skip(&gb, 1);
 | 
			
		||||
            } else {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            previous = bytestream2_peek_byte(&gb) & 0x20;
 | 
			
		||||
            backline = bytestream2_peek_byte(&gb) & 0x40;
 | 
			
		||||
            backward = bytestream2_peek_byte(&gb) & 0x80;
 | 
			
		||||
            bytestream2_skip(&gb, 1);
 | 
			
		||||
            swap   = bytestream2_peek_byte(&gb) & 0x01;
 | 
			
		||||
            offset = bytestream2_get_le16(&gb);
 | 
			
		||||
            offset = (offset >> 1) & 0x7FFF;
 | 
			
		||||
            offset = writeoffset + (offset * 2) - 0x8000;
 | 
			
		||||
 | 
			
		||||
            left = count;
 | 
			
		||||
 | 
			
		||||
            if (!backward && offset + count >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (backward && offset >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (writeoffset + count >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR,
 | 
			
		||||
                       "Attempting to write out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (previous) {
 | 
			
		||||
                while (left > 0) {
 | 
			
		||||
                    if (backline) {
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->previous[offset++];
 | 
			
		||||
                        offset++;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->previous[offset++];
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->previous[offset++];
 | 
			
		||||
                    }
 | 
			
		||||
                    if (backward)
 | 
			
		||||
                        offset -= 4;
 | 
			
		||||
                    left--;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                while (left > 0) {
 | 
			
		||||
                    if (backline) {
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->current[offset++];
 | 
			
		||||
                        offset++;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->current[offset++];
 | 
			
		||||
                        hnm->current[writeoffset++] = hnm->current[offset++];
 | 
			
		||||
                    }
 | 
			
		||||
                    if (backward)
 | 
			
		||||
                        offset -= 4;
 | 
			
		||||
                    left--;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (swap) {
 | 
			
		||||
                left         = count;
 | 
			
		||||
                writeoffset -= count * 2;
 | 
			
		||||
                while (left > 0) {
 | 
			
		||||
                    swap = hnm->current[writeoffset];
 | 
			
		||||
                    hnm->current[writeoffset] = hnm->current[writeoffset + 1];
 | 
			
		||||
                    hnm->current[writeoffset + 1] = swap;
 | 
			
		||||
                    left--;
 | 
			
		||||
                    writeoffset += 2;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void decode_interframe_v4a(AVCodecContext *avctx, uint8_t *src,
 | 
			
		||||
                                  uint32_t size)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
    GetByteContext gb;
 | 
			
		||||
    uint32_t writeoffset = 0, offset;
 | 
			
		||||
    uint8_t tag, count, previous, delta;
 | 
			
		||||
 | 
			
		||||
    bytestream2_init(&gb, src, size);
 | 
			
		||||
 | 
			
		||||
    while (bytestream2_tell(&gb) < size) {
 | 
			
		||||
        count = bytestream2_peek_byte(&gb) & 0x3F;
 | 
			
		||||
        if (count == 0) {
 | 
			
		||||
            tag = bytestream2_get_byte(&gb) & 0xC0;
 | 
			
		||||
            tag = tag >> 6;
 | 
			
		||||
            if (tag == 0) {
 | 
			
		||||
                writeoffset += bytestream2_get_byte(&gb);
 | 
			
		||||
            } else if (tag == 1) {
 | 
			
		||||
                hnm->current[writeoffset]              = bytestream2_get_byte(&gb);
 | 
			
		||||
                hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
 | 
			
		||||
                writeoffset++;
 | 
			
		||||
            } else if (tag == 2) {
 | 
			
		||||
                writeoffset += hnm->width;
 | 
			
		||||
            } else if (tag == 3) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            delta    = bytestream2_peek_byte(&gb) & 0x80;
 | 
			
		||||
            previous = bytestream2_peek_byte(&gb) & 0x40;
 | 
			
		||||
            bytestream2_skip(&gb, 1);
 | 
			
		||||
 | 
			
		||||
            offset  = writeoffset;
 | 
			
		||||
            offset += bytestream2_get_le16(&gb);
 | 
			
		||||
 | 
			
		||||
            if (delta)
 | 
			
		||||
                offset -= 0x10000;
 | 
			
		||||
 | 
			
		||||
            if (offset + hnm->width + count >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
 | 
			
		||||
                av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds");
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (previous) {
 | 
			
		||||
                while (count > 0) {
 | 
			
		||||
                    hnm->current[writeoffset]              = hnm->previous[offset];
 | 
			
		||||
                    hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
 | 
			
		||||
                    writeoffset++;
 | 
			
		||||
                    offset++;
 | 
			
		||||
                    count--;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                while (count > 0) {
 | 
			
		||||
                    hnm->current[writeoffset]              = hnm->current[offset];
 | 
			
		||||
                    hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
 | 
			
		||||
                    writeoffset++;
 | 
			
		||||
                    offset++;
 | 
			
		||||
                    count--;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hnm_update_palette(AVCodecContext *avctx, uint8_t *src,
 | 
			
		||||
                               uint32_t size)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
    GetByteContext gb;
 | 
			
		||||
    uint8_t start, writeoffset;
 | 
			
		||||
    uint16_t count;
 | 
			
		||||
    int eight_bit_colors;
 | 
			
		||||
 | 
			
		||||
    eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
 | 
			
		||||
 | 
			
		||||
    // skip first 8 bytes
 | 
			
		||||
    bytestream2_init(&gb, src + 8, size - 8);
 | 
			
		||||
 | 
			
		||||
    while (bytestream2_tell(&gb) < size - 8) {
 | 
			
		||||
        start = bytestream2_get_byte(&gb);
 | 
			
		||||
        count = bytestream2_get_byte(&gb);
 | 
			
		||||
        if (start == 255 && count == 255)
 | 
			
		||||
            break;
 | 
			
		||||
        if (count == 0)
 | 
			
		||||
            count = 256;
 | 
			
		||||
        writeoffset = start;
 | 
			
		||||
        while (count > 0) {
 | 
			
		||||
            hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
 | 
			
		||||
            if (!eight_bit_colors)
 | 
			
		||||
                hnm->palette[writeoffset] <<= 2;
 | 
			
		||||
            count--;
 | 
			
		||||
            writeoffset++;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hnm_flip_buffers(Hnm4VideoContext *hnm)
 | 
			
		||||
{
 | 
			
		||||
    uint8_t *temp;
 | 
			
		||||
 | 
			
		||||
    temp          = hnm->current;
 | 
			
		||||
    hnm->current  = hnm->previous;
 | 
			
		||||
    hnm->previous = temp;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hnm_decode_frame(AVCodecContext *avctx, void *data,
 | 
			
		||||
                            int *got_frame, AVPacket *avpkt)
 | 
			
		||||
{
 | 
			
		||||
    AVFrame *frame = data;
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
    int ret;
 | 
			
		||||
    uint16_t chunk_id;
 | 
			
		||||
 | 
			
		||||
    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) {
 | 
			
		||||
        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    chunk_id = AV_RL16(avpkt->data + 4);
 | 
			
		||||
 | 
			
		||||
    if (chunk_id == HNM4_CHUNK_ID_PL) {
 | 
			
		||||
        hnm_update_palette(avctx, avpkt->data, avpkt->size);
 | 
			
		||||
        frame->palette_has_changed = 1;
 | 
			
		||||
    } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
 | 
			
		||||
        unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
 | 
			
		||||
        memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
 | 
			
		||||
        if (hnm->version == 0x4a)
 | 
			
		||||
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
 | 
			
		||||
        else
 | 
			
		||||
            postprocess_current_frame(avctx);
 | 
			
		||||
        copy_processed_frame(avctx, frame);
 | 
			
		||||
        frame->pict_type = AV_PICTURE_TYPE_I;
 | 
			
		||||
        frame->key_frame = 1;
 | 
			
		||||
        memcpy(frame->data[1], hnm->palette, 256 * 4);
 | 
			
		||||
        *got_frame = 1;
 | 
			
		||||
    } else if (chunk_id == HNM4_CHUNK_ID_IU) {
 | 
			
		||||
        if (hnm->version == 0x4a) {
 | 
			
		||||
            decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
 | 
			
		||||
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
 | 
			
		||||
        } else {
 | 
			
		||||
            decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
 | 
			
		||||
            postprocess_current_frame(avctx);
 | 
			
		||||
        }
 | 
			
		||||
        copy_processed_frame(avctx, frame);
 | 
			
		||||
        frame->pict_type = AV_PICTURE_TYPE_P;
 | 
			
		||||
        frame->key_frame = 0;
 | 
			
		||||
        memcpy(frame->data[1], hnm->palette, 256 * 4);
 | 
			
		||||
        *got_frame = 1;
 | 
			
		||||
        hnm_flip_buffers(hnm);
 | 
			
		||||
    } else {
 | 
			
		||||
        av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
 | 
			
		||||
        return AVERROR_INVALIDDATA;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return avpkt->size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static av_cold int hnm_decode_init(AVCodecContext *avctx)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
 | 
			
		||||
    if (avctx->extradata_size < 1) {
 | 
			
		||||
        av_log(avctx, AV_LOG_ERROR,
 | 
			
		||||
               "Extradata missing, decoder requires version number\n");
 | 
			
		||||
        return AVERROR_INVALIDDATA;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hnm->version   = avctx->extradata[0];
 | 
			
		||||
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
 | 
			
		||||
    hnm->width     = avctx->width;
 | 
			
		||||
    hnm->height    = avctx->height;
 | 
			
		||||
    hnm->buffer1   = av_mallocz(avctx->width * avctx->height);
 | 
			
		||||
    hnm->buffer2   = av_mallocz(avctx->width * avctx->height);
 | 
			
		||||
    hnm->processed = av_mallocz(avctx->width * avctx->height);
 | 
			
		||||
 | 
			
		||||
    if (!hnm->buffer1 || !hnm->buffer2 || !hnm->processed) {
 | 
			
		||||
        av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n");
 | 
			
		||||
        av_freep(&hnm->buffer1);
 | 
			
		||||
        av_freep(&hnm->buffer2);
 | 
			
		||||
        av_freep(&hnm->processed);
 | 
			
		||||
        return AVERROR(ENOMEM);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hnm->current  = hnm->buffer1;
 | 
			
		||||
    hnm->previous = hnm->buffer2;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static av_cold int hnm_decode_end(AVCodecContext *avctx)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4VideoContext *hnm = avctx->priv_data;
 | 
			
		||||
 | 
			
		||||
    av_freep(&hnm->buffer1);
 | 
			
		||||
    av_freep(&hnm->buffer2);
 | 
			
		||||
    av_freep(&hnm->processed);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AVCodec ff_hnm4_video_decoder = {
 | 
			
		||||
    .name           = "hnm4video",
 | 
			
		||||
    .long_name      = NULL_IF_CONFIG_SMALL("HNM 4 video"),
 | 
			
		||||
    .type           = AVMEDIA_TYPE_VIDEO,
 | 
			
		||||
    .id             = AV_CODEC_ID_HNM4_VIDEO,
 | 
			
		||||
    .priv_data_size = sizeof(Hnm4VideoContext),
 | 
			
		||||
    .init           = hnm_decode_init,
 | 
			
		||||
    .close          = hnm_decode_end,
 | 
			
		||||
    .decode         = hnm_decode_frame,
 | 
			
		||||
    .capabilities   = CODEC_CAP_DR1,
 | 
			
		||||
};
 | 
			
		||||
@ -29,7 +29,7 @@
 | 
			
		||||
#include "libavutil/avutil.h"
 | 
			
		||||
 | 
			
		||||
#define LIBAVCODEC_VERSION_MAJOR 55
 | 
			
		||||
#define LIBAVCODEC_VERSION_MINOR  39
 | 
			
		||||
#define LIBAVCODEC_VERSION_MINOR  40
 | 
			
		||||
#define LIBAVCODEC_VERSION_MICRO 100
 | 
			
		||||
 | 
			
		||||
#define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
 | 
			
		||||
 | 
			
		||||
@ -169,6 +169,7 @@ OBJS-$(CONFIG_H264_MUXER)                += rawenc.o
 | 
			
		||||
OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
 | 
			
		||||
OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
 | 
			
		||||
OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o
 | 
			
		||||
OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
 | 
			
		||||
OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
 | 
			
		||||
OBJS-$(CONFIG_ICO_MUXER)                 += icoenc.o
 | 
			
		||||
OBJS-$(CONFIG_IDCIN_DEMUXER)             += idcin.o
 | 
			
		||||
 | 
			
		||||
@ -136,6 +136,7 @@ void av_register_all(void)
 | 
			
		||||
    REGISTER_MUXDEMUX(H264,             h264);
 | 
			
		||||
    REGISTER_DEMUXER (HEVC,             hevc);
 | 
			
		||||
    REGISTER_MUXDEMUX(HLS,              hls);
 | 
			
		||||
    REGISTER_DEMUXER (HNM,              hnm);
 | 
			
		||||
    REGISTER_MUXDEMUX(ICO,              ico);
 | 
			
		||||
    REGISTER_DEMUXER (IDCIN,            idcin);
 | 
			
		||||
    REGISTER_DEMUXER (IDF,              idf);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										204
									
								
								libavformat/hnm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								libavformat/hnm.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,204 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Cryo Interactive Entertainment HNM4 demuxer
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2012 David Kment
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of FFmpeg .
 | 
			
		||||
 *
 | 
			
		||||
 * FFmpeg  is free software; you can redistribute it and/or
 | 
			
		||||
 * modify it under the terms of the GNU Lesser General Public
 | 
			
		||||
 * License as published by the Free Software Foundation; either
 | 
			
		||||
 * version 2.1 of the License, or (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * FFmpeg  is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
			
		||||
 * Lesser General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Lesser General Public
 | 
			
		||||
 * License along with FFmpeg ; if not, write to the Free Software
 | 
			
		||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "libavutil/intreadwrite.h"
 | 
			
		||||
#include "avformat.h"
 | 
			
		||||
#include "internal.h"
 | 
			
		||||
 | 
			
		||||
#define HNM4_TAG MKTAG('H', 'N', 'M', '4')
 | 
			
		||||
 | 
			
		||||
#define HNM4_SAMPLE_RATE 22050
 | 
			
		||||
#define HNM4_FRAME_FPS 24
 | 
			
		||||
 | 
			
		||||
#define HNM4_CHUNK_ID_PL 19536
 | 
			
		||||
#define HNM4_CHUNK_ID_IZ 23113
 | 
			
		||||
#define HNM4_CHUNK_ID_IU 21833
 | 
			
		||||
#define HNM4_CHUNK_ID_SD 17491
 | 
			
		||||
 | 
			
		||||
typedef struct Hnm4DemuxContext {
 | 
			
		||||
    uint8_t version;
 | 
			
		||||
    uint16_t width;
 | 
			
		||||
    uint16_t height;
 | 
			
		||||
    uint32_t filesize;
 | 
			
		||||
    uint32_t frames;
 | 
			
		||||
    uint32_t taboffset;
 | 
			
		||||
    uint16_t bits;
 | 
			
		||||
    uint16_t channels;
 | 
			
		||||
    uint32_t framesize;
 | 
			
		||||
    uint32_t currentframe;
 | 
			
		||||
    int64_t pts;
 | 
			
		||||
    uint32_t superchunk_remaining;
 | 
			
		||||
    AVPacket vpkt;
 | 
			
		||||
} Hnm4DemuxContext;
 | 
			
		||||
 | 
			
		||||
static int hnm_probe(AVProbeData *p)
 | 
			
		||||
{
 | 
			
		||||
    if (p->buf_size < 4)
 | 
			
		||||
        return 0;
 | 
			
		||||
 | 
			
		||||
    // check for HNM4 header.
 | 
			
		||||
    // currently only HNM v4/v4A is supported
 | 
			
		||||
    if (AV_RL32(&p->buf[0]) == HNM4_TAG)
 | 
			
		||||
        return AVPROBE_SCORE_MAX;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hnm_read_header(AVFormatContext *s)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4DemuxContext *hnm = s->priv_data;
 | 
			
		||||
    AVIOContext *pb = s->pb;
 | 
			
		||||
    AVStream *vst;
 | 
			
		||||
 | 
			
		||||
    /* default context members */
 | 
			
		||||
    hnm->pts = 0;
 | 
			
		||||
    av_init_packet(&hnm->vpkt);
 | 
			
		||||
    hnm->vpkt.data = NULL;
 | 
			
		||||
    hnm->vpkt.size = 0;
 | 
			
		||||
 | 
			
		||||
    hnm->superchunk_remaining = 0;
 | 
			
		||||
 | 
			
		||||
    avio_skip(pb, 8);
 | 
			
		||||
    hnm->width     = avio_rl16(pb);
 | 
			
		||||
    hnm->height    = avio_rl16(pb);
 | 
			
		||||
    hnm->filesize  = avio_rl32(pb);
 | 
			
		||||
    hnm->frames    = avio_rl32(pb);
 | 
			
		||||
    hnm->taboffset = avio_rl32(pb);
 | 
			
		||||
    hnm->bits      = avio_rl16(pb);
 | 
			
		||||
    hnm->channels  = avio_rl16(pb);
 | 
			
		||||
    hnm->framesize = avio_rl32(pb);
 | 
			
		||||
    avio_skip(pb, 32);
 | 
			
		||||
 | 
			
		||||
    hnm->currentframe = 0;
 | 
			
		||||
 | 
			
		||||
    if (hnm->width  < 320 || hnm->width  > 640 ||
 | 
			
		||||
        hnm->height < 150 || hnm->height > 480) {
 | 
			
		||||
        av_log(s, AV_LOG_ERROR,
 | 
			
		||||
               "invalid resolution: %ux%u\n", hnm->width, hnm->height);
 | 
			
		||||
        return AVERROR_INVALIDDATA;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: find a better way to detect HNM4A
 | 
			
		||||
    if (hnm->width == 640)
 | 
			
		||||
        hnm->version = 0x4a;
 | 
			
		||||
    else
 | 
			
		||||
        hnm->version = 0x40;
 | 
			
		||||
 | 
			
		||||
    if (!(vst = avformat_new_stream(s, NULL)))
 | 
			
		||||
        return AVERROR(ENOMEM);
 | 
			
		||||
 | 
			
		||||
    vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
 | 
			
		||||
    vst->codec->codec_id   = AV_CODEC_ID_HNM4_VIDEO;
 | 
			
		||||
    vst->codec->codec_tag  = 0;
 | 
			
		||||
    vst->codec->width      = hnm->width;
 | 
			
		||||
    vst->codec->height     = hnm->height;
 | 
			
		||||
    vst->codec->extradata  = av_mallocz(1);
 | 
			
		||||
 | 
			
		||||
    vst->codec->extradata_size = 1;
 | 
			
		||||
    memcpy(vst->codec->extradata, &hnm->version, 1);
 | 
			
		||||
 | 
			
		||||
    vst->start_time = 0;
 | 
			
		||||
 | 
			
		||||
    avpriv_set_pts_info(vst, 33, 1, HNM4_FRAME_FPS);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4DemuxContext *hnm = s->priv_data;
 | 
			
		||||
    AVIOContext *pb = s->pb;
 | 
			
		||||
    int ret = 0;
 | 
			
		||||
 | 
			
		||||
    uint32_t superchunk_size, chunk_size;
 | 
			
		||||
    uint16_t chunk_id;
 | 
			
		||||
 | 
			
		||||
    if (hnm->currentframe == hnm->frames || pb->eof_reached)
 | 
			
		||||
        return AVERROR_EOF;
 | 
			
		||||
 | 
			
		||||
    if (hnm->superchunk_remaining == 0) {
 | 
			
		||||
        /* parse next superchunk */
 | 
			
		||||
        superchunk_size = avio_rl24(pb);
 | 
			
		||||
        avio_skip(pb, 1);
 | 
			
		||||
 | 
			
		||||
        hnm->superchunk_remaining = superchunk_size - 4;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    chunk_size = avio_rl24(pb);
 | 
			
		||||
    avio_skip(pb, 1);
 | 
			
		||||
    chunk_id = avio_rl16(pb);
 | 
			
		||||
    avio_skip(pb, 2);
 | 
			
		||||
 | 
			
		||||
    if (chunk_size > hnm->superchunk_remaining) {
 | 
			
		||||
        av_log(s, AV_LOG_ERROR, "invalid chunk size: %u, offset: %u\n",
 | 
			
		||||
               chunk_size, (int) avio_tell(pb));
 | 
			
		||||
        avio_skip(pb, hnm->superchunk_remaining - 8);
 | 
			
		||||
        hnm->superchunk_remaining = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (chunk_id) {
 | 
			
		||||
    case HNM4_CHUNK_ID_PL:
 | 
			
		||||
    case HNM4_CHUNK_ID_IZ:
 | 
			
		||||
    case HNM4_CHUNK_ID_IU:
 | 
			
		||||
        avio_seek(pb, -8, SEEK_CUR);
 | 
			
		||||
        ret += av_get_packet(pb, pkt, chunk_size);
 | 
			
		||||
        hnm->superchunk_remaining -= chunk_size;
 | 
			
		||||
        if (chunk_id == HNM4_CHUNK_ID_IZ || chunk_id == HNM4_CHUNK_ID_IU)
 | 
			
		||||
            hnm->currentframe++;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    case HNM4_CHUNK_ID_SD:
 | 
			
		||||
        avio_skip(pb, chunk_size - 8);
 | 
			
		||||
        hnm->superchunk_remaining -= chunk_size;
 | 
			
		||||
        break;
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
        av_log(s, AV_LOG_WARNING, "unknown chunk found: %d, offset: %d\n",
 | 
			
		||||
               chunk_id, (int) avio_tell(pb));
 | 
			
		||||
        avio_skip(pb, chunk_size - 8);
 | 
			
		||||
        hnm->superchunk_remaining -= chunk_size;
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int hnm_read_close(AVFormatContext *s)
 | 
			
		||||
{
 | 
			
		||||
    Hnm4DemuxContext *hnm = s->priv_data;
 | 
			
		||||
 | 
			
		||||
    if (hnm->vpkt.size > 0)
 | 
			
		||||
        av_free_packet(&hnm->vpkt);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
AVInputFormat ff_hnm_demuxer = {
 | 
			
		||||
    .name           = "hnm",
 | 
			
		||||
    .long_name      = NULL_IF_CONFIG_SMALL("Cryo HNM v4"),
 | 
			
		||||
    .priv_data_size = sizeof(Hnm4DemuxContext),
 | 
			
		||||
    .read_probe     = hnm_probe,
 | 
			
		||||
    .read_header    = hnm_read_header,
 | 
			
		||||
    .read_packet    = hnm_read_packet,
 | 
			
		||||
    .read_close     = hnm_read_close,
 | 
			
		||||
    .flags          = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH | AVFMT_NOBINSEARCH
 | 
			
		||||
};
 | 
			
		||||
@ -30,8 +30,8 @@
 | 
			
		||||
#include "libavutil/avutil.h"
 | 
			
		||||
 | 
			
		||||
#define LIBAVFORMAT_VERSION_MAJOR 55
 | 
			
		||||
#define LIBAVFORMAT_VERSION_MINOR 19
 | 
			
		||||
#define LIBAVFORMAT_VERSION_MICRO 104
 | 
			
		||||
#define LIBAVFORMAT_VERSION_MINOR 20
 | 
			
		||||
#define LIBAVFORMAT_VERSION_MICRO 100
 | 
			
		||||
 | 
			
		||||
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
 | 
			
		||||
                                               LIBAVFORMAT_VERSION_MINOR, \
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user