FFmpeg/libavformat/a64.c
Michael Niedermayer 976a8b2179 Merge remote-tracking branch 'qatar/master'
* qatar/master: (40 commits)
  H.264: template left MB handling
  H.264: faster fill_decode_caches
  H.264: faster write_back_*
  H.264: faster fill_filter_caches
  H.264: make filter_mb_fast support the case of unavailable top mb
  Do not include log.h in avutil.h
  Do not include pixfmt.h in avutil.h
  Do not include rational.h in avutil.h
  Do not include mathematics.h in avutil.h
  Do not include intfloat_readwrite.h in avutil.h
  Remove return statements following infinite loops without break
  RTSP: Doxygen comment cleanup
  doxygen: Escape '\' in Doxygen documentation.
  md5: cosmetics
  md5: use AV_WL32 to write result
  md5: add fate test
  md5: include correct headers
  md5: fix test program
  doxygen: Drop array size declarations from Doxygen parameter names.
  doxygen: Fix parameter names to match the function prototypes.
  ...

Conflicts:
	libavcodec/x86/dsputil_mmx.c
	libavformat/flvenc.c
	libavformat/oggenc.c
	libavformat/wtv.c

Merged-by: Michael Niedermayer <michaelni@gmx.at>
2011-07-04 00:45:21 +02:00

177 lines
6.3 KiB
C

/*
* a64 muxer
* Copyright (c) 2009 Tobias Bindhammer
*
* 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 "libavcodec/avcodec.h"
#include "libavcodec/a64enc.h"
#include "libavcodec/bytestream.h"
#include "avformat.h"
typedef struct A64MuxerContext {
int interleaved;
AVPacket prev_pkt;
int prev_frame_count;
} A64MuxerContext;
static int a64_write_header(struct AVFormatContext *s)
{
AVCodecContext *avctx = s->streams[0]->codec;
A64MuxerContext *c = s->priv_data;
uint8_t header[5] = {
0x00, //load
0x40, //address
0x00, //mode
0x00, //charset_lifetime (multi only)
0x00 //fps in 50/fps;
};
c->interleaved = 0;
switch (avctx->codec->id) {
case CODEC_ID_A64_MULTI:
header[2] = 0x00;
header[3] = AV_RB32(avctx->extradata+0);
header[4] = 2;
break;
case CODEC_ID_A64_MULTI5:
header[2] = 0x01;
header[3] = AV_RB32(avctx->extradata+0);
header[4] = 3;
break;
default:
return AVERROR(EINVAL);
}
avio_write(s->pb, header, 2);
c->prev_pkt.size = 0;
c->prev_frame_count = 0;
return 0;
}
static int a64_write_packet(struct AVFormatContext *s, AVPacket *pkt)
{
AVCodecContext *avctx = s->streams[0]->codec;
A64MuxerContext *c = s->priv_data;
int i, j;
int ch_chunksize;
int lifetime;
int frame_count;
int charset_size;
int frame_size;
int num_frames;
/* fetch values from extradata */
switch (avctx->codec->id) {
case CODEC_ID_A64_MULTI:
case CODEC_ID_A64_MULTI5:
if(c->interleaved) {
/* Write interleaved, means we insert chunks of the future charset before each current frame.
* Reason: if we load 1 charset + corresponding frames in one block on c64, we need to store
* them first and then display frame by frame to keep in sync. Thus we would read and write
* the data for colram from/to ram first and waste too much time. If we interleave and send the
* charset beforehand, we assemble a new charset chunk by chunk, write current screen data to
* screen-ram to be displayed and decode the colram directly to colram-location $d800 during
* the overscan, while reading directly from source.
* This is the only way so far, to achieve 25fps on c64 */
if(avctx->extradata) {
/* fetch values from extradata */
lifetime = AV_RB32(avctx->extradata + 0);
frame_count = AV_RB32(avctx->extradata + 4);
charset_size = AV_RB32(avctx->extradata + 8);
frame_size = AV_RB32(avctx->extradata + 12);
/* TODO: sanity checks? */
} else {
av_log(avctx, AV_LOG_ERROR, "extradata not set\n");
return AVERROR(EINVAL);
}
ch_chunksize=charset_size/lifetime;
/* TODO: check if charset/size is % lifetime, but maybe check in codec */
if(pkt->data) num_frames = lifetime;
else num_frames = c->prev_frame_count;
for(i = 0; i < num_frames; i++) {
if(pkt->data) {
/* if available, put newest charset chunk into buffer */
avio_write(s->pb, pkt->data + ch_chunksize * i, ch_chunksize);
} else {
/* a bit ugly, but is there an alternative to put many zeros? */
for(j = 0; j < ch_chunksize; j++) avio_w8(s->pb, 0);
}
if(c->prev_pkt.data) {
/* put frame (screen + colram) from last packet into buffer */
avio_write(s->pb, c->prev_pkt.data + charset_size + frame_size * i, frame_size);
} else {
/* a bit ugly, but is there an alternative to put many zeros? */
for(j = 0; j < frame_size; j++) avio_w8(s->pb, 0);
}
}
/* backup current packet for next turn */
if(pkt->data) {
/* no backup packet yet? create one! */
if(!c->prev_pkt.data) av_new_packet(&c->prev_pkt, pkt->size);
/* we have a packet and data is big enough, reuse it */
if(c->prev_pkt.data && c->prev_pkt.size >= pkt->size) {
memcpy(c->prev_pkt.data, pkt->data, pkt->size);
c->prev_pkt.size = pkt->size;
} else {
av_log(avctx, AV_LOG_ERROR, "Too less memory for prev_pkt.\n");
return AVERROR(ENOMEM);
}
}
c->prev_frame_count = frame_count;
break;
}
default:
/* Write things as is. Nice for self-contained frames from non-multicolor modes or if played
* directly from ram and not from a streaming device (rrnet/mmc) */
if(pkt) avio_write(s->pb, pkt->data, pkt->size);
break;
}
avio_flush(s->pb);
return 0;
}
static int a64_write_trailer(struct AVFormatContext *s)
{
A64MuxerContext *c = s->priv_data;
AVPacket pkt = {0};
/* need to flush last packet? */
if(c->interleaved) a64_write_packet(s, &pkt);
/* discard backed up packet */
if(c->prev_pkt.data) av_destruct_packet(&c->prev_pkt);
return 0;
}
AVOutputFormat ff_a64_muxer = {
.name = "a64",
.long_name = NULL_IF_CONFIG_SMALL("a64 - video for Commodore 64"),
.mime_type = NULL,
.extensions = "a64, A64",
.priv_data_size = sizeof (A64Context),
.video_codec = CODEC_ID_A64_MULTI,
a64_write_header,
a64_write_packet,
a64_write_trailer
};