Libav 0.7.1
|
00001 /* 00002 * a64 muxer 00003 * Copyright (c) 2009 Tobias Bindhammer 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * Libav is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "libavcodec/avcodec.h" 00023 #include "libavcodec/a64enc.h" 00024 #include "libavcodec/bytestream.h" 00025 #include "avformat.h" 00026 00027 typedef struct A64MuxerContext { 00028 int interleaved; 00029 AVPacket prev_pkt; 00030 int prev_frame_count; 00031 } A64MuxerContext; 00032 00033 static int a64_write_header(struct AVFormatContext *s) 00034 { 00035 AVCodecContext *avctx = s->streams[0]->codec; 00036 A64MuxerContext *c = s->priv_data; 00037 uint8_t header[5] = { 00038 0x00, //load 00039 0x40, //address 00040 0x00, //mode 00041 0x00, //charset_lifetime (multi only) 00042 0x00 //fps in 50/fps; 00043 }; 00044 c->interleaved = 0; 00045 switch (avctx->codec->id) { 00046 case CODEC_ID_A64_MULTI: 00047 header[2] = 0x00; 00048 header[3] = AV_RB32(avctx->extradata+0); 00049 header[4] = 2; 00050 break; 00051 case CODEC_ID_A64_MULTI5: 00052 header[2] = 0x01; 00053 header[3] = AV_RB32(avctx->extradata+0); 00054 header[4] = 3; 00055 break; 00056 default: 00057 return AVERROR(EINVAL); 00058 break; 00059 } 00060 avio_write(s->pb, header, 2); 00061 c->prev_pkt.size = 0; 00062 c->prev_frame_count = 0; 00063 return 0; 00064 } 00065 00066 static int a64_write_packet(struct AVFormatContext *s, AVPacket *pkt) 00067 { 00068 AVCodecContext *avctx = s->streams[0]->codec; 00069 A64MuxerContext *c = s->priv_data; 00070 int i, j; 00071 int ch_chunksize; 00072 int lifetime; 00073 int frame_count; 00074 int charset_size; 00075 int frame_size; 00076 int num_frames; 00077 00078 /* fetch values from extradata */ 00079 switch (avctx->codec->id) { 00080 case CODEC_ID_A64_MULTI: 00081 case CODEC_ID_A64_MULTI5: 00082 if(c->interleaved) { 00083 /* Write interleaved, means we insert chunks of the future charset before each current frame. 00084 * Reason: if we load 1 charset + corresponding frames in one block on c64, we need to store 00085 * them first and then display frame by frame to keep in sync. Thus we would read and write 00086 * the data for colram from/to ram first and waste too much time. If we interleave and send the 00087 * charset beforehand, we assemble a new charset chunk by chunk, write current screen data to 00088 * screen-ram to be displayed and decode the colram directly to colram-location $d800 during 00089 * the overscan, while reading directly from source. 00090 * This is the only way so far, to achieve 25fps on c64 */ 00091 if(avctx->extradata) { 00092 /* fetch values from extradata */ 00093 lifetime = AV_RB32(avctx->extradata + 0); 00094 frame_count = AV_RB32(avctx->extradata + 4); 00095 charset_size = AV_RB32(avctx->extradata + 8); 00096 frame_size = AV_RB32(avctx->extradata + 12); 00097 00098 /* TODO: sanity checks? */ 00099 } else { 00100 av_log(avctx, AV_LOG_ERROR, "extradata not set\n"); 00101 return AVERROR(EINVAL); 00102 } 00103 00104 ch_chunksize=charset_size/lifetime; 00105 /* TODO: check if charset/size is % lifetime, but maybe check in codec */ 00106 00107 if(pkt->data) num_frames = lifetime; 00108 else num_frames = c->prev_frame_count; 00109 00110 for(i = 0; i < num_frames; i++) { 00111 if(pkt->data) { 00112 /* if available, put newest charset chunk into buffer */ 00113 avio_write(s->pb, pkt->data + ch_chunksize * i, ch_chunksize); 00114 } else { 00115 /* a bit ugly, but is there an alternative to put many zeros? */ 00116 for(j = 0; j < ch_chunksize; j++) avio_w8(s->pb, 0); 00117 } 00118 00119 if(c->prev_pkt.data) { 00120 /* put frame (screen + colram) from last packet into buffer */ 00121 avio_write(s->pb, c->prev_pkt.data + charset_size + frame_size * i, frame_size); 00122 } else { 00123 /* a bit ugly, but is there an alternative to put many zeros? */ 00124 for(j = 0; j < frame_size; j++) avio_w8(s->pb, 0); 00125 } 00126 } 00127 00128 /* backup current packet for next turn */ 00129 if(pkt->data) { 00130 /* no backup packet yet? create one! */ 00131 if(!c->prev_pkt.data) av_new_packet(&c->prev_pkt, pkt->size); 00132 /* we have a packet and data is big enough, reuse it */ 00133 if(c->prev_pkt.data && c->prev_pkt.size >= pkt->size) { 00134 memcpy(c->prev_pkt.data, pkt->data, pkt->size); 00135 c->prev_pkt.size = pkt->size; 00136 } else { 00137 av_log(avctx, AV_LOG_ERROR, "Too less memory for prev_pkt.\n"); 00138 return AVERROR(ENOMEM); 00139 } 00140 } 00141 00142 c->prev_frame_count = frame_count; 00143 break; 00144 } 00145 default: 00146 /* Write things as is. Nice for self-contained frames from non-multicolor modes or if played 00147 * directly from ram and not from a streaming device (rrnet/mmc) */ 00148 if(pkt) avio_write(s->pb, pkt->data, pkt->size); 00149 break; 00150 } 00151 00152 avio_flush(s->pb); 00153 return 0; 00154 } 00155 00156 static int a64_write_trailer(struct AVFormatContext *s) 00157 { 00158 A64MuxerContext *c = s->priv_data; 00159 AVPacket pkt = {0}; 00160 /* need to flush last packet? */ 00161 if(c->interleaved) a64_write_packet(s, &pkt); 00162 /* discard backed up packet */ 00163 if(c->prev_pkt.data) av_destruct_packet(&c->prev_pkt); 00164 return 0; 00165 } 00166 00167 AVOutputFormat ff_a64_muxer = { 00168 .name = "a64", 00169 .long_name = NULL_IF_CONFIG_SMALL("a64 - video for Commodore 64"), 00170 .mime_type = NULL, 00171 .extensions = "a64, A64", 00172 .priv_data_size = sizeof (A64Context), 00173 .video_codec = CODEC_ID_A64_MULTI, 00174 a64_write_header, 00175 a64_write_packet, 00176 a64_write_trailer 00177 };