Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Cryo Interactive Entertainment HNM4 video decoder
  3.  *
  4.  * Copyright (c) 2012 David Kment
  5.  *
  6.  * This file is part of FFmpeg.
  7.  *
  8.  * FFmpeg is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU Lesser General Public
  10.  * License as published by the Free Software Foundation; either
  11.  * version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  * FFmpeg is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  * Lesser General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Lesser General Public
  19.  * License along with FFmpeg; if not, write to the Free Software
  20.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21.  */
  22.  
  23. #include <string.h>
  24.  
  25. #include "libavutil/imgutils.h"
  26. #include "libavutil/internal.h"
  27. #include "libavutil/intreadwrite.h"
  28. #include "libavutil/mem.h"
  29. #include "avcodec.h"
  30. #include "bytestream.h"
  31. #include "internal.h"
  32.  
  33. #define HNM4_CHUNK_ID_PL 19536
  34. #define HNM4_CHUNK_ID_IZ 23113
  35. #define HNM4_CHUNK_ID_IU 21833
  36. #define HNM4_CHUNK_ID_SD 17491
  37.  
  38. typedef struct Hnm4VideoContext {
  39.     uint8_t version;
  40.     int width;
  41.     int height;
  42.     uint8_t *current;
  43.     uint8_t *previous;
  44.     uint8_t *buffer1;
  45.     uint8_t *buffer2;
  46.     uint8_t *processed;
  47.     uint32_t palette[256];
  48. } Hnm4VideoContext;
  49.  
  50. static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits)
  51. {
  52.     int ret;
  53.  
  54.     if (!*bits) {
  55.         *bitbuf = bytestream2_get_le32(gb);
  56.         *bits = 32;
  57.     }
  58.  
  59.     ret = *bitbuf >> 31;
  60.     *bitbuf <<= 1;
  61.     (*bits)--;
  62.  
  63.     return ret;
  64. }
  65.  
  66. static void unpack_intraframe(AVCodecContext *avctx, uint8_t *src,
  67.                               uint32_t size)
  68. {
  69.     Hnm4VideoContext *hnm = avctx->priv_data;
  70.     GetByteContext gb;
  71.     uint32_t bitbuf = 0, writeoffset = 0, count = 0;
  72.     uint16_t word;
  73.     int32_t offset;
  74.     int bits = 0;
  75.  
  76.     bytestream2_init(&gb, src, size);
  77.  
  78.     while (bytestream2_tell(&gb) < size) {
  79.         if (getbit(&gb, &bitbuf, &bits)) {
  80.             if (writeoffset >= hnm->width * hnm->height) {
  81.                 av_log(avctx, AV_LOG_ERROR,
  82.                        "Attempting to write out of bounds\n");
  83.                 break;
  84.             }
  85.             hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
  86.         } else {
  87.             if (getbit(&gb, &bitbuf, &bits)) {
  88.                 word   = bytestream2_get_le16(&gb);
  89.                 count  = word & 0x07;
  90.                 offset = (word >> 3) - 0x2000;
  91.                 if (!count)
  92.                     count = bytestream2_get_byte(&gb);
  93.                 if (!count)
  94.                     return;
  95.             } else {
  96.                 count  = getbit(&gb, &bitbuf, &bits) * 2;
  97.                 count += getbit(&gb, &bitbuf, &bits);
  98.                 offset = bytestream2_get_byte(&gb) - 0x0100;
  99.             }
  100.             count  += 2;
  101.             offset += writeoffset;
  102.             if (offset < 0 || offset + count >= hnm->width * hnm->height) {
  103.                 av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
  104.                 break;
  105.             } else if (writeoffset + count >= hnm->width * hnm->height) {
  106.                 av_log(avctx, AV_LOG_ERROR,
  107.                        "Attempting to write out of bounds\n");
  108.                 break;
  109.             }
  110.             while (count--) {
  111.                 hnm->current[writeoffset++] = hnm->current[offset++];
  112.             }
  113.         }
  114.     }
  115. }
  116.  
  117. static void postprocess_current_frame(AVCodecContext *avctx)
  118. {
  119.     Hnm4VideoContext *hnm = avctx->priv_data;
  120.     uint32_t x, y, src_x, src_y;
  121.  
  122.     for (y = 0; y < hnm->height; y++) {
  123.         src_y = y - (y % 2);
  124.         src_x = src_y * hnm->width + (y % 2);
  125.         for (x = 0; x < hnm->width; x++) {
  126.             hnm->processed[(y * hnm->width) + x] = hnm->current[src_x];
  127.             src_x += 2;
  128.         }
  129.     }
  130. }
  131.  
  132. static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
  133. {
  134.     Hnm4VideoContext *hnm = avctx->priv_data;
  135.     uint8_t *src = hnm->processed;
  136.     uint8_t *dst = frame->data[0];
  137.     int y;
  138.  
  139.     for (y = 0; y < hnm->height; y++) {
  140.         memcpy(dst, src, hnm->width);
  141.         src += hnm->width;
  142.         dst += frame->linesize[0];
  143.     }
  144. }
  145.  
  146. static void decode_interframe_v4(AVCodecContext *avctx, uint8_t *src, uint32_t size)
  147. {
  148.     Hnm4VideoContext *hnm = avctx->priv_data;
  149.     GetByteContext gb;
  150.     uint32_t writeoffset = 0;
  151.     int count, left, offset;
  152.     uint8_t tag, previous, backline, backward, swap;
  153.  
  154.     bytestream2_init(&gb, src, size);
  155.  
  156.     while (bytestream2_tell(&gb) < size) {
  157.         count = bytestream2_peek_byte(&gb) & 0x1F;
  158.         if (count == 0) {
  159.             tag = bytestream2_get_byte(&gb) & 0xE0;
  160.             tag = tag >> 5;
  161.  
  162.             if (tag == 0) {
  163.                 if (writeoffset + 2 > hnm->width * hnm->height) {
  164.                     av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
  165.                     break;
  166.                 }
  167.                 hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
  168.                 hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
  169.             } else if (tag == 1) {
  170.                 writeoffset += bytestream2_get_byte(&gb) * 2;
  171.             } else if (tag == 2) {
  172.                 count = bytestream2_get_le16(&gb);
  173.                 count *= 2;
  174.                 writeoffset += count;
  175.             } else if (tag == 3) {
  176.                 count = bytestream2_get_byte(&gb) * 2;
  177.                 if (writeoffset + count > hnm->width * hnm->height) {
  178.                     av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
  179.                     break;
  180.                 }
  181.                 while (count > 0) {
  182.                     hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
  183.                     count--;
  184.                 }
  185.                 bytestream2_skip(&gb, 1);
  186.             } else {
  187.                 break;
  188.             }
  189.             if (writeoffset > hnm->width * hnm->height) {
  190.                 av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
  191.                 break;
  192.             }
  193.         } else {
  194.             previous = bytestream2_peek_byte(&gb) & 0x20;
  195.             backline = bytestream2_peek_byte(&gb) & 0x40;
  196.             backward = bytestream2_peek_byte(&gb) & 0x80;
  197.             bytestream2_skip(&gb, 1);
  198.             swap   = bytestream2_peek_byte(&gb) & 0x01;
  199.             offset = bytestream2_get_le16(&gb);
  200.             offset = (offset >> 1) & 0x7FFF;
  201.             offset = writeoffset + (offset * 2) - 0x8000;
  202.  
  203.             left = count;
  204.  
  205.             if (!backward && offset + 2*count > hnm->width * hnm->height) {
  206.                 av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
  207.                 break;
  208.             } else if (backward && offset + 1 >= hnm->width * hnm->height) {
  209.                 av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
  210.                 break;
  211.             } else if (writeoffset + 2*count > hnm->width * hnm->height) {
  212.                 av_log(avctx, AV_LOG_ERROR,
  213.                        "Attempting to write out of bounds\n");
  214.                 break;
  215.             }
  216.             if(backward) {
  217.                 if (offset < (!!backline)*(2 * hnm->width - 1) + 2*(left-1)) {
  218.                     av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
  219.                     break;
  220.                 }
  221.             } else {
  222.                 if (offset < (!!backline)*(2 * hnm->width - 1)) {
  223.                     av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
  224.                     break;
  225.                 }
  226.             }
  227.  
  228.             if (previous) {
  229.                 while (left > 0) {
  230.                     if (backline) {
  231.                         hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
  232.                         hnm->current[writeoffset++] = hnm->previous[offset++];
  233.                         offset++;
  234.                     } else {
  235.                         hnm->current[writeoffset++] = hnm->previous[offset++];
  236.                         hnm->current[writeoffset++] = hnm->previous[offset++];
  237.                     }
  238.                     if (backward)
  239.                         offset -= 4;
  240.                     left--;
  241.                 }
  242.             } else {
  243.                 while (left > 0) {
  244.                     if (backline) {
  245.                         hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
  246.                         hnm->current[writeoffset++] = hnm->current[offset++];
  247.                         offset++;
  248.                     } else {
  249.                         hnm->current[writeoffset++] = hnm->current[offset++];
  250.                         hnm->current[writeoffset++] = hnm->current[offset++];
  251.                     }
  252.                     if (backward)
  253.                         offset -= 4;
  254.                     left--;
  255.                 }
  256.             }
  257.  
  258.             if (swap) {
  259.                 left         = count;
  260.                 writeoffset -= count * 2;
  261.                 while (left > 0) {
  262.                     swap = hnm->current[writeoffset];
  263.                     hnm->current[writeoffset] = hnm->current[writeoffset + 1];
  264.                     hnm->current[writeoffset + 1] = swap;
  265.                     left--;
  266.                     writeoffset += 2;
  267.                 }
  268.             }
  269.         }
  270.     }
  271. }
  272.  
  273. static void decode_interframe_v4a(AVCodecContext *avctx, uint8_t *src,
  274.                                   uint32_t size)
  275. {
  276.     Hnm4VideoContext *hnm = avctx->priv_data;
  277.     GetByteContext gb;
  278.     uint32_t writeoffset = 0, offset;
  279.     uint8_t tag, count, previous, delta;
  280.  
  281.     bytestream2_init(&gb, src, size);
  282.  
  283.     while (bytestream2_tell(&gb) < size) {
  284.         count = bytestream2_peek_byte(&gb) & 0x3F;
  285.         if (count == 0) {
  286.             tag = bytestream2_get_byte(&gb) & 0xC0;
  287.             tag = tag >> 6;
  288.             if (tag == 0) {
  289.                 writeoffset += bytestream2_get_byte(&gb);
  290.             } else if (tag == 1) {
  291.                 if (writeoffset + hnm->width >= hnm->width * hnm->height) {
  292.                     av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
  293.                     break;
  294.                 }
  295.                 hnm->current[writeoffset]              = bytestream2_get_byte(&gb);
  296.                 hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
  297.                 writeoffset++;
  298.             } else if (tag == 2) {
  299.                 writeoffset += hnm->width;
  300.             } else if (tag == 3) {
  301.                 break;
  302.             }
  303.             if (writeoffset > hnm->width * hnm->height) {
  304.                 av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
  305.                 break;
  306.             }
  307.         } else {
  308.             delta    = bytestream2_peek_byte(&gb) & 0x80;
  309.             previous = bytestream2_peek_byte(&gb) & 0x40;
  310.             bytestream2_skip(&gb, 1);
  311.  
  312.             offset  = writeoffset;
  313.             offset += bytestream2_get_le16(&gb);
  314.  
  315.             if (delta) {
  316.                 if (offset < 0x10000) {
  317.                     av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
  318.                     break;
  319.                 }
  320.                 offset -= 0x10000;
  321.             }
  322.  
  323.             if (offset + hnm->width + count >= hnm->width * hnm->height) {
  324.                 av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
  325.                 break;
  326.             } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
  327.                 av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds\n");
  328.                 break;
  329.             }
  330.  
  331.             if (previous) {
  332.                 while (count > 0) {
  333.                     hnm->current[writeoffset]              = hnm->previous[offset];
  334.                     hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
  335.                     writeoffset++;
  336.                     offset++;
  337.                     count--;
  338.                 }
  339.             } else {
  340.                 while (count > 0) {
  341.                     hnm->current[writeoffset]              = hnm->current[offset];
  342.                     hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
  343.                     writeoffset++;
  344.                     offset++;
  345.                     count--;
  346.                 }
  347.             }
  348.         }
  349.     }
  350. }
  351.  
  352. static void hnm_update_palette(AVCodecContext *avctx, uint8_t *src,
  353.                                uint32_t size)
  354. {
  355.     Hnm4VideoContext *hnm = avctx->priv_data;
  356.     GetByteContext gb;
  357.     uint8_t start, writeoffset;
  358.     uint16_t count;
  359.     int eight_bit_colors;
  360.  
  361.     eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
  362.  
  363.     // skip first 8 bytes
  364.     bytestream2_init(&gb, src + 8, size - 8);
  365.  
  366.     while (bytestream2_tell(&gb) < size - 8) {
  367.         start = bytestream2_get_byte(&gb);
  368.         count = bytestream2_get_byte(&gb);
  369.         if (start == 255 && count == 255)
  370.             break;
  371.         if (count == 0)
  372.             count = 256;
  373.         writeoffset = start;
  374.         while (count > 0) {
  375.             hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
  376.             if (!eight_bit_colors)
  377.                 hnm->palette[writeoffset] <<= 2;
  378.             count--;
  379.             writeoffset++;
  380.         }
  381.     }
  382. }
  383.  
  384. static void hnm_flip_buffers(Hnm4VideoContext *hnm)
  385. {
  386.     uint8_t *temp;
  387.  
  388.     temp          = hnm->current;
  389.     hnm->current  = hnm->previous;
  390.     hnm->previous = temp;
  391. }
  392.  
  393. static int hnm_decode_frame(AVCodecContext *avctx, void *data,
  394.                             int *got_frame, AVPacket *avpkt)
  395. {
  396.     AVFrame *frame = data;
  397.     Hnm4VideoContext *hnm = avctx->priv_data;
  398.     int ret;
  399.     uint16_t chunk_id;
  400.  
  401.     if (avpkt->size < 8) {
  402.         av_log(avctx, AV_LOG_ERROR, "packet too small\n");
  403.         return AVERROR_INVALIDDATA;
  404.     }
  405.  
  406.     chunk_id = AV_RL16(avpkt->data + 4);
  407.  
  408.     if (chunk_id == HNM4_CHUNK_ID_PL) {
  409.         hnm_update_palette(avctx, avpkt->data, avpkt->size);
  410.     } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
  411.         if (avpkt->size < 12) {
  412.             av_log(avctx, AV_LOG_ERROR, "packet too small\n");
  413.             return AVERROR_INVALIDDATA;
  414.         }
  415.         if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
  416.             return ret;
  417.  
  418.         unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
  419.         memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
  420.         if (hnm->version == 0x4a)
  421.             memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
  422.         else
  423.             postprocess_current_frame(avctx);
  424.         copy_processed_frame(avctx, frame);
  425.         frame->pict_type = AV_PICTURE_TYPE_I;
  426.         frame->key_frame = 1;
  427.         memcpy(frame->data[1], hnm->palette, 256 * 4);
  428.         *got_frame = 1;
  429.     } else if (chunk_id == HNM4_CHUNK_ID_IU) {
  430.         if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
  431.             return ret;
  432.  
  433.         if (hnm->version == 0x4a) {
  434.             decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
  435.             memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
  436.         } else {
  437.             decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
  438.             postprocess_current_frame(avctx);
  439.         }
  440.         copy_processed_frame(avctx, frame);
  441.         frame->pict_type = AV_PICTURE_TYPE_P;
  442.         frame->key_frame = 0;
  443.         memcpy(frame->data[1], hnm->palette, 256 * 4);
  444.         *got_frame = 1;
  445.         hnm_flip_buffers(hnm);
  446.     } else {
  447.         av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
  448.         return AVERROR_INVALIDDATA;
  449.     }
  450.  
  451.     return avpkt->size;
  452. }
  453.  
  454. static av_cold int hnm_decode_init(AVCodecContext *avctx)
  455. {
  456.     Hnm4VideoContext *hnm = avctx->priv_data;
  457.     int ret;
  458.  
  459.     if (avctx->extradata_size < 1) {
  460.         av_log(avctx, AV_LOG_ERROR,
  461.                "Extradata missing, decoder requires version number\n");
  462.         return AVERROR_INVALIDDATA;
  463.     }
  464.  
  465.     ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
  466.     if (ret < 0)
  467.         return ret;
  468.  
  469.     hnm->version   = avctx->extradata[0];
  470.     avctx->pix_fmt = AV_PIX_FMT_PAL8;
  471.     hnm->width     = avctx->width;
  472.     hnm->height    = avctx->height;
  473.     hnm->buffer1   = av_mallocz(avctx->width * avctx->height);
  474.     hnm->buffer2   = av_mallocz(avctx->width * avctx->height);
  475.     hnm->processed = av_mallocz(avctx->width * avctx->height);
  476.  
  477.     if (   !hnm->buffer1 || !hnm->buffer2 || !hnm->processed
  478.         || avctx->width * avctx->height == 0
  479.         || avctx->height % 2) {
  480.         av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n");
  481.         av_freep(&hnm->buffer1);
  482.         av_freep(&hnm->buffer2);
  483.         av_freep(&hnm->processed);
  484.         return AVERROR(ENOMEM);
  485.     }
  486.  
  487.     hnm->current  = hnm->buffer1;
  488.     hnm->previous = hnm->buffer2;
  489.  
  490.     return 0;
  491. }
  492.  
  493. static av_cold int hnm_decode_end(AVCodecContext *avctx)
  494. {
  495.     Hnm4VideoContext *hnm = avctx->priv_data;
  496.  
  497.     av_freep(&hnm->buffer1);
  498.     av_freep(&hnm->buffer2);
  499.     av_freep(&hnm->processed);
  500.  
  501.     return 0;
  502. }
  503.  
  504. AVCodec ff_hnm4_video_decoder = {
  505.     .name           = "hnm4video",
  506.     .long_name      = NULL_IF_CONFIG_SMALL("HNM 4 video"),
  507.     .type           = AVMEDIA_TYPE_VIDEO,
  508.     .id             = AV_CODEC_ID_HNM4_VIDEO,
  509.     .priv_data_size = sizeof(Hnm4VideoContext),
  510.     .init           = hnm_decode_init,
  511.     .close          = hnm_decode_end,
  512.     .decode         = hnm_decode_frame,
  513.     .capabilities   = AV_CODEC_CAP_DR1,
  514. };
  515.