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