mirror of https://github.com/FFmpeg/FFmpeg.git
patch by Anssi Hannula, anssi.hannula gmail com Originally committed as revision 8643 to svn://svn.ffmpeg.org/ffmpeg/trunkpull/126/head
parent
fe0372296a
commit
9a0ddd09e7
10 changed files with 466 additions and 0 deletions
@ -0,0 +1,253 @@ |
||||
/*
|
||||
* Interplay C93 video decoder |
||||
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> |
||||
* |
||||
* 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 "avcodec.h" |
||||
#include "bytestream.h" |
||||
|
||||
typedef struct { |
||||
AVFrame pictures[2]; |
||||
int currentpic; |
||||
} C93DecoderContext; |
||||
|
||||
typedef enum { |
||||
C93_8X8_FROM_PREV = 0x02, |
||||
C93_4X4_FROM_PREV = 0x06, |
||||
C93_4X4_FROM_CURR = 0x07, |
||||
C93_8X8_2COLOR = 0x08, |
||||
C93_4X4_2COLOR = 0x0A, |
||||
C93_4X4_4COLOR_GRP = 0x0B, |
||||
C93_4X4_4COLOR = 0x0D, |
||||
C93_NOOP = 0x0E, |
||||
C93_8X8_INTRA = 0x0F, |
||||
} C93BlockType; |
||||
|
||||
#define WIDTH 320 |
||||
#define HEIGHT 192 |
||||
|
||||
#define C93_HAS_PALETTE 0x01 |
||||
#define C93_FIRST_FRAME 0x02 |
||||
|
||||
static int c93_decode_init(AVCodecContext *avctx) |
||||
{ |
||||
avctx->pix_fmt = PIX_FMT_PAL8; |
||||
return 0; |
||||
} |
||||
|
||||
static int c93_decode_end(AVCodecContext *avctx) |
||||
{ |
||||
C93DecoderContext * const c93 = avctx->priv_data; |
||||
|
||||
if (c93->pictures[0].data[0]) |
||||
avctx->release_buffer(avctx, &c93->pictures[0]); |
||||
if (c93->pictures[1].data[0]) |
||||
avctx->release_buffer(avctx, &c93->pictures[1]); |
||||
return 0; |
||||
} |
||||
|
||||
static inline int c93_copy_block(AVCodecContext *avctx, uint8_t *to, |
||||
uint8_t *from, int offset, int height, int stride) |
||||
{ |
||||
int i; |
||||
int width = height; |
||||
int from_x = offset % WIDTH; |
||||
int from_y = offset / WIDTH; |
||||
int overflow = from_x + width - WIDTH; |
||||
|
||||
if (!from) { |
||||
/* silently ignoring predictive blocks in first frame */ |
||||
return 0; |
||||
} |
||||
|
||||
if (from_y + height > HEIGHT) { |
||||
av_log(avctx, AV_LOG_ERROR, "invalid offset %d during C93 decoding\n", |
||||
offset); |
||||
return -1; |
||||
} |
||||
|
||||
if (overflow > 0) { |
||||
width -= overflow; |
||||
for (i = 0; i < height; i++) { |
||||
memcpy(&to[i*stride+width], &from[(from_y+i)*stride], overflow); |
||||
} |
||||
} |
||||
|
||||
for (i = 0; i < height; i++) { |
||||
memcpy(&to[i*stride], &from[(from_y+i)*stride+from_x], width); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static inline void c93_draw_n_color(uint8_t *out, int stride, int width, |
||||
int height, int bpp, uint8_t cols[4], uint8_t grps[4], uint32_t col) |
||||
{ |
||||
int x, y; |
||||
for (y = 0; y < height; y++) { |
||||
if (grps) |
||||
cols[0] = grps[3 * (y >> 1)]; |
||||
for (x = 0; x < width; x++) { |
||||
if (grps) |
||||
cols[1]= grps[(x >> 1) + 1]; |
||||
out[x + y*stride] = cols[col & ((1 << bpp) - 1)]; |
||||
col >>= bpp; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static int c93_decode_frame(AVCodecContext *avctx, void *data, |
||||
int *data_size, uint8_t * buf, int buf_size) |
||||
{ |
||||
C93DecoderContext * const c93 = avctx->priv_data; |
||||
AVFrame * const newpic = &c93->pictures[c93->currentpic]; |
||||
AVFrame * const oldpic = &c93->pictures[c93->currentpic^1]; |
||||
AVFrame *picture = data; |
||||
uint8_t *out; |
||||
int stride, i, x, y; |
||||
C93BlockType bt = 0; |
||||
|
||||
c93->currentpic ^= 1; |
||||
|
||||
newpic->reference = 1; |
||||
newpic->buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | |
||||
FF_BUFFER_HINTS_REUSABLE | FF_BUFFER_HINTS_READABLE; |
||||
if (avctx->reget_buffer(avctx, newpic)) { |
||||
av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); |
||||
return -1; |
||||
} |
||||
|
||||
stride = newpic->linesize[0]; |
||||
|
||||
if (buf[0] & C93_FIRST_FRAME) { |
||||
newpic->pict_type = FF_I_TYPE; |
||||
newpic->key_frame = 1; |
||||
} else { |
||||
newpic->pict_type = FF_P_TYPE; |
||||
newpic->key_frame = 0; |
||||
} |
||||
|
||||
if (*buf++ & C93_HAS_PALETTE) { |
||||
uint32_t *palette = (uint32_t *) newpic->data[1]; |
||||
uint8_t *palbuf = buf + buf_size - 768 - 1; |
||||
for (i = 0; i < 256; i++) { |
||||
palette[i] = bytestream_get_be24(&palbuf); |
||||
} |
||||
} else { |
||||
if (oldpic->data[1]) |
||||
memcpy(newpic->data[1], oldpic->data[1], 256 * 4); |
||||
} |
||||
|
||||
for (y = 0; y < HEIGHT; y += 8) { |
||||
out = newpic->data[0] + y * stride; |
||||
for (x = 0; x < WIDTH; x += 8) { |
||||
uint8_t *copy_from = oldpic->data[0]; |
||||
unsigned int offset, j; |
||||
uint8_t cols[4], grps[4]; |
||||
|
||||
if (!bt) |
||||
bt = *buf++; |
||||
|
||||
switch (bt & 0x0F) { |
||||
case C93_8X8_FROM_PREV: |
||||
offset = bytestream_get_le16(&buf); |
||||
if (c93_copy_block(avctx, out, copy_from, offset, 8, stride)) |
||||
return -1; |
||||
break; |
||||
|
||||
case C93_4X4_FROM_CURR: |
||||
copy_from = newpic->data[0]; |
||||
case C93_4X4_FROM_PREV: |
||||
for (j = 0; j < 8; j += 4) { |
||||
for (i = 0; i < 8; i += 4) { |
||||
offset = bytestream_get_le16(&buf); |
||||
if (c93_copy_block(avctx, &out[j*stride+i], |
||||
copy_from, offset, 4, stride)) |
||||
return -1; |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case C93_8X8_2COLOR: |
||||
bytestream_get_buffer(&buf, cols, 2); |
||||
for (i = 0; i < 8; i++) { |
||||
c93_draw_n_color(out + i*stride, stride, 8, 1, 1, cols, |
||||
NULL, *buf++); |
||||
} |
||||
|
||||
break; |
||||
|
||||
case C93_4X4_2COLOR: |
||||
case C93_4X4_4COLOR: |
||||
case C93_4X4_4COLOR_GRP: |
||||
for (j = 0; j < 8; j += 4) { |
||||
for (i = 0; i < 8; i += 4) { |
||||
if ((bt & 0x0F) == C93_4X4_2COLOR) { |
||||
bytestream_get_buffer(&buf, cols, 2); |
||||
c93_draw_n_color(out + i + j*stride, stride, 4, 4, |
||||
1, cols, NULL, bytestream_get_le16(&buf)); |
||||
} else if ((bt & 0x0F) == C93_4X4_4COLOR) { |
||||
bytestream_get_buffer(&buf, cols, 4); |
||||
c93_draw_n_color(out + i + j*stride, stride, 4, 4, |
||||
2, cols, NULL, bytestream_get_le32(&buf)); |
||||
} else { |
||||
bytestream_get_buffer(&buf, grps, 4); |
||||
c93_draw_n_color(out + i + j*stride, stride, 4, 4, |
||||
1, cols, grps, bytestream_get_le16(&buf)); |
||||
} |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case C93_NOOP: |
||||
break; |
||||
|
||||
case C93_8X8_INTRA: |
||||
for (j = 0; j < 8; j++) |
||||
bytestream_get_buffer(&buf, out + j*stride, 8); |
||||
break; |
||||
|
||||
default: |
||||
av_log(avctx, AV_LOG_ERROR, "unexpected type %x at %dx%d\n", |
||||
bt & 0x0F, x, y); |
||||
return -1; |
||||
} |
||||
bt >>= 4; |
||||
out += 8; |
||||
} |
||||
} |
||||
|
||||
*picture = *newpic; |
||||
*data_size = sizeof(AVFrame); |
||||
|
||||
return buf_size; |
||||
} |
||||
|
||||
AVCodec c93_decoder = { |
||||
"c93", |
||||
CODEC_TYPE_VIDEO, |
||||
CODEC_ID_C93, |
||||
sizeof(C93DecoderContext), |
||||
c93_decode_init, |
||||
NULL, |
||||
c93_decode_end, |
||||
c93_decode_frame, |
||||
CODEC_CAP_DR1, |
||||
}; |
@ -0,0 +1,202 @@ |
||||
/*
|
||||
* Interplay C93 demuxer |
||||
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> |
||||
* |
||||
* 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 "avformat.h" |
||||
#include "voc.h" |
||||
|
||||
typedef struct { |
||||
uint16_t index; |
||||
uint8_t length; |
||||
uint8_t frames; |
||||
} C93BlockRecord; |
||||
|
||||
typedef struct { |
||||
voc_dec_context_t voc; |
||||
|
||||
C93BlockRecord block_records[512]; |
||||
int current_block; |
||||
|
||||
uint32_t frame_offsets[32]; |
||||
int current_frame; |
||||
int next_pkt_is_audio; |
||||
|
||||
AVStream *audio; |
||||
} C93DemuxContext; |
||||
|
||||
static int c93_probe(AVProbeData *p) |
||||
{ |
||||
if (p->buf_size < 13) |
||||
return 0; |
||||
|
||||
if (p->buf[0] == 0x01 && p->buf[1] == 0x00 && |
||||
p->buf[4] == 0x01 + p->buf[2] && |
||||
p->buf[8] == p->buf[4] + p->buf[6] && |
||||
p->buf[12] == p->buf[8] + p->buf[10]) |
||||
return AVPROBE_SCORE_MAX; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int c93_read_header(AVFormatContext *s, |
||||
AVFormatParameters *ap) |
||||
{ |
||||
AVStream *video; |
||||
ByteIOContext *pb = &s->pb; |
||||
C93DemuxContext *c93 = s->priv_data; |
||||
int i; |
||||
int framecount = 0; |
||||
|
||||
for (i = 0; i < 512; i++) { |
||||
c93->block_records[i].index = get_le16(pb); |
||||
c93->block_records[i].length = get_byte(pb); |
||||
c93->block_records[i].frames = get_byte(pb); |
||||
if (c93->block_records[i].frames > 32) { |
||||
av_log(s, AV_LOG_ERROR, "too many frames in block\n"); |
||||
return AVERROR_INVALIDDATA; |
||||
} |
||||
framecount += c93->block_records[i].frames; |
||||
} |
||||
|
||||
/* Audio streams are added if audio packets are found */ |
||||
s->ctx_flags |= AVFMTCTX_NOHEADER; |
||||
|
||||
video = av_new_stream(s, 0); |
||||
if (!video) |
||||
return AVERROR_NOMEM; |
||||
|
||||
video->codec->codec_type = CODEC_TYPE_VIDEO; |
||||
video->codec->codec_id = CODEC_ID_C93; |
||||
video->codec->width = 320; |
||||
video->codec->height = 192; |
||||
/* 4:3 320x200 with 8 empty lines */ |
||||
video->codec->sample_aspect_ratio = (AVRational) { 5, 6 }; |
||||
video->time_base = (AVRational) { 2, 25 }; |
||||
video->nb_frames = framecount; |
||||
video->duration = framecount; |
||||
video->start_time = 0; |
||||
|
||||
c93->current_block = 0; |
||||
c93->current_frame = 0; |
||||
c93->next_pkt_is_audio = 0; |
||||
return 0; |
||||
} |
||||
|
||||
#define C93_HAS_PALETTE 0x01 |
||||
#define C93_FIRST_FRAME 0x02 |
||||
|
||||
static int c93_read_packet(AVFormatContext *s, AVPacket *pkt) |
||||
{ |
||||
ByteIOContext *pb = &s->pb; |
||||
C93DemuxContext *c93 = s->priv_data; |
||||
C93BlockRecord *br = &c93->block_records[c93->current_block]; |
||||
int datasize; |
||||
int ret, i; |
||||
|
||||
if (c93->next_pkt_is_audio) { |
||||
c93->current_frame++; |
||||
c93->next_pkt_is_audio = 0; |
||||
datasize = get_le16(pb); |
||||
if (datasize > 42) { |
||||
if (!c93->audio) { |
||||
c93->audio = av_new_stream(s, 1); |
||||
if (!c93->audio) |
||||
return AVERROR_NOMEM; |
||||
c93->audio->codec->codec_type = CODEC_TYPE_AUDIO; |
||||
} |
||||
url_fskip(pb, 26); /* VOC header */ |
||||
ret = voc_get_packet(s, pkt, c93->audio, datasize - 26); |
||||
if (ret > 0) { |
||||
pkt->stream_index = 1; |
||||
pkt->flags |= PKT_FLAG_KEY; |
||||
return ret; |
||||
} |
||||
} |
||||
} |
||||
if (c93->current_frame >= br->frames) { |
||||
if (c93->current_block >= 511 || !br[1].length) |
||||
return AVERROR_IO; |
||||
br++; |
||||
c93->current_block++; |
||||
c93->current_frame = 0; |
||||
} |
||||
|
||||
if (c93->current_frame == 0) { |
||||
url_fseek(pb, br->index * 2048, SEEK_SET); |
||||
for (i = 0; i < 32; i++) { |
||||
c93->frame_offsets[i] = get_le32(pb); |
||||
} |
||||
} |
||||
|
||||
url_fseek(pb,br->index * 2048 + |
||||
c93->frame_offsets[c93->current_frame], SEEK_SET); |
||||
datasize = get_le16(pb); /* video frame size */ |
||||
|
||||
ret = av_new_packet(pkt, datasize + 768 + 1); |
||||
if (ret < 0) |
||||
return ret; |
||||
pkt->data[0] = 0; |
||||
pkt->size = datasize + 1; |
||||
|
||||
ret = get_buffer(pb, pkt->data + 1, datasize); |
||||
if (ret < datasize) { |
||||
ret = AVERROR_IO; |
||||
goto fail; |
||||
} |
||||
|
||||
datasize = get_le16(pb); /* palette size */ |
||||
if (datasize) { |
||||
if (datasize != 768) { |
||||
av_log(s, AV_LOG_ERROR, "invalid palette size %u\n", datasize); |
||||
ret = AVERROR_INVALIDDATA; |
||||
goto fail; |
||||
} |
||||
pkt->data[0] |= C93_HAS_PALETTE; |
||||
ret = get_buffer(pb, pkt->data + pkt->size, datasize); |
||||
if (ret < datasize) { |
||||
ret = AVERROR_IO; |
||||
goto fail; |
||||
} |
||||
pkt->size += 768; |
||||
} |
||||
pkt->stream_index = 0; |
||||
c93->next_pkt_is_audio = 1; |
||||
|
||||
/* only the first frame is guaranteed to not reference previous frames */ |
||||
if (c93->current_block == 0 && c93->current_frame == 0) { |
||||
pkt->flags |= PKT_FLAG_KEY; |
||||
pkt->data[0] |= C93_FIRST_FRAME; |
||||
} |
||||
return 0; |
||||
|
||||
fail: |
||||
av_free_packet(pkt); |
||||
return ret; |
||||
} |
||||
|
||||
AVInputFormat c93_demuxer = { |
||||
"c93", |
||||
"Interplay C93", |
||||
sizeof(C93DemuxContext), |
||||
c93_probe, |
||||
c93_read_header, |
||||
c93_read_packet, |
||||
}; |
Loading…
Reference in new issue