@ -24,9 +24,16 @@
# 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
@ -34,6 +41,7 @@ static int a64_write_header(struct AVFormatContext *s)
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 ;
@ -50,16 +58,99 @@ static int a64_write_header(struct AVFormatContext *s)
break ;
}
put_buffer ( 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 )
{
put_buffer ( s - > pb , pkt - > data , pkt - > size ) ;
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 25f ps 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 */
put_buffer ( 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 + + ) put_byte ( s - > pb , 0 ) ;
}
if ( c - > prev_pkt . data ) {
/* put frame (screen + colram) from last packet into buffer */
put_buffer ( 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 + + ) put_byte ( s - > pb , 0 ) ;
}
}
/* backup current packet for next turn */
if ( pkt - > data ) {
av_new_packet ( & c - > prev_pkt , pkt - > size ) ;
memcpy ( c - > prev_pkt . data , pkt - > data , pkt - > size ) ;
}
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 ) put_buffer ( s - > pb , pkt - > data , pkt - > size ) ;
break ;
}
put_flush_packet ( s - > pb ) ;
return 0 ;
}
static int a64_write_trailer ( struct AVFormatContext * s )
{
A64MuxerContext * c = s - > priv_data ;
AVPacket pkt ;
/* need to flush last packet? */
if ( c - > interleaved ) a64_write_packet ( s , & pkt ) ;
return 0 ;
}
AVOutputFormat a64_muxer = {
. name = " a64 " ,
. long_name = NULL_IF_CONFIG_SMALL ( " a64 - video for Commodore 64 " ) ,
@ -69,4 +160,5 @@ AVOutputFormat a64_muxer = {
. video_codec = CODEC_ID_A64_MULTI ,
a64_write_header ,
a64_write_packet ,
a64_write_trailer
} ;