00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "libavutil/channel_layout.h"
00023 #include "libavutil/intreadwrite.h"
00024 #include "libavutil/parseutils.h"
00025 #include "libavutil/opt.h"
00026 #include "avformat.h"
00027 #include "internal.h"
00028
00029 #define CDXL_HEADER_SIZE 32
00030
00031 typedef struct CDXLDemuxContext {
00032 AVClass *class;
00033 int sample_rate;
00034 char *framerate;
00035 AVRational fps;
00036 int read_chunk;
00037 uint8_t header[CDXL_HEADER_SIZE];
00038 int video_stream_index;
00039 int audio_stream_index;
00040 } CDXLDemuxContext;
00041
00042 static int cdxl_read_probe(AVProbeData *p)
00043 {
00044 int score = AVPROBE_SCORE_MAX / 2 + 10;
00045
00046 if (p->buf_size < CDXL_HEADER_SIZE)
00047 return 0;
00048
00049
00050 if (AV_RN64(&p->buf[24]) || AV_RN16(&p->buf[10]))
00051 return 0;
00052
00053
00054 if (p->buf[0] != 1)
00055 return 0;
00056
00057
00058 if (AV_RB16(&p->buf[20]) > 512)
00059 return 0;
00060
00061
00062 if (p->buf[18] || !p->buf[19])
00063 return 0;
00064
00065
00066 if (!AV_RN16(&p->buf[14]) || !AV_RN16(&p->buf[16]))
00067 return 0;
00068
00069
00070 if (AV_RB32(&p->buf[2]) < AV_RB16(&p->buf[22]) + AV_RB16(&p->buf[20]) + CDXL_HEADER_SIZE)
00071 return 0;
00072
00073
00074 if (AV_RN32(&p->buf[6]))
00075 score /= 2;
00076
00077
00078 if (AV_RB16(&p->buf[12]) != 1)
00079 score /= 2;
00080
00081 return score;
00082 }
00083
00084 static int cdxl_read_header(AVFormatContext *s)
00085 {
00086 CDXLDemuxContext *cdxl = s->priv_data;
00087 int ret;
00088
00089 if (cdxl->framerate && (ret = av_parse_video_rate(&cdxl->fps, cdxl->framerate)) < 0) {
00090 av_log(s, AV_LOG_ERROR,
00091 "Could not parse framerate: %s.\n", cdxl->framerate);
00092 return ret;
00093 }
00094
00095 cdxl->read_chunk = 0;
00096 cdxl->video_stream_index = -1;
00097 cdxl->audio_stream_index = -1;
00098
00099 s->ctx_flags |= AVFMTCTX_NOHEADER;
00100
00101 return 0;
00102 }
00103
00104 static int cdxl_read_packet(AVFormatContext *s, AVPacket *pkt)
00105 {
00106 CDXLDemuxContext *cdxl = s->priv_data;
00107 AVIOContext *pb = s->pb;
00108 uint32_t current_size, video_size, image_size;
00109 uint16_t audio_size, palette_size, width, height;
00110 int64_t pos;
00111 int ret;
00112
00113 if (url_feof(pb))
00114 return AVERROR_EOF;
00115
00116 pos = avio_tell(pb);
00117 if (!cdxl->read_chunk &&
00118 avio_read(pb, cdxl->header, CDXL_HEADER_SIZE) != CDXL_HEADER_SIZE)
00119 return AVERROR_EOF;
00120 if (cdxl->header[0] != 1) {
00121 av_log(s, AV_LOG_ERROR, "non-standard cdxl file\n");
00122 return AVERROR_INVALIDDATA;
00123 }
00124
00125 current_size = AV_RB32(&cdxl->header[2]);
00126 width = AV_RB16(&cdxl->header[14]);
00127 height = AV_RB16(&cdxl->header[16]);
00128 palette_size = AV_RB16(&cdxl->header[20]);
00129 audio_size = AV_RB16(&cdxl->header[22]);
00130 image_size = FFALIGN(width, 16) * height * cdxl->header[19] / 8;
00131 video_size = palette_size + image_size;
00132
00133 if (palette_size > 512)
00134 return AVERROR_INVALIDDATA;
00135 if (current_size < (uint64_t)audio_size + video_size + CDXL_HEADER_SIZE)
00136 return AVERROR_INVALIDDATA;
00137
00138 if (cdxl->read_chunk && audio_size) {
00139 if (cdxl->audio_stream_index == -1) {
00140 AVStream *st = avformat_new_stream(s, NULL);
00141 if (!st)
00142 return AVERROR(ENOMEM);
00143
00144 st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
00145 st->codec->codec_tag = 0;
00146 st->codec->codec_id = AV_CODEC_ID_PCM_S8;
00147 if (cdxl->header[1] & 0x10) {
00148 st->codec->channels = 2;
00149 st->codec->channel_layout = AV_CH_LAYOUT_STEREO;
00150 } else {
00151 st->codec->channels = 1;
00152 st->codec->channel_layout = AV_CH_LAYOUT_MONO;
00153 }
00154 st->codec->sample_rate = cdxl->sample_rate;
00155 st->start_time = 0;
00156 cdxl->audio_stream_index = st->index;
00157 avpriv_set_pts_info(st, 64, 1, cdxl->sample_rate);
00158 }
00159
00160 ret = av_get_packet(pb, pkt, audio_size);
00161 if (ret < 0)
00162 return ret;
00163 pkt->stream_index = cdxl->audio_stream_index;
00164 pkt->pos = pos;
00165 pkt->duration = audio_size;
00166 cdxl->read_chunk = 0;
00167 } else {
00168 if (cdxl->video_stream_index == -1) {
00169 AVStream *st = avformat_new_stream(s, NULL);
00170 if (!st)
00171 return AVERROR(ENOMEM);
00172
00173 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00174 st->codec->codec_tag = 0;
00175 st->codec->codec_id = AV_CODEC_ID_CDXL;
00176 st->codec->width = width;
00177 st->codec->height = height;
00178 st->start_time = 0;
00179 cdxl->video_stream_index = st->index;
00180 if (cdxl->framerate)
00181 avpriv_set_pts_info(st, 64, cdxl->fps.den, cdxl->fps.num);
00182 else
00183 avpriv_set_pts_info(st, 64, 1, cdxl->sample_rate);
00184 }
00185
00186 if (av_new_packet(pkt, video_size + CDXL_HEADER_SIZE) < 0)
00187 return AVERROR(ENOMEM);
00188 memcpy(pkt->data, cdxl->header, CDXL_HEADER_SIZE);
00189 ret = avio_read(pb, pkt->data + CDXL_HEADER_SIZE, video_size);
00190 if (ret < 0) {
00191 av_free_packet(pkt);
00192 return ret;
00193 }
00194 av_shrink_packet(pkt, CDXL_HEADER_SIZE + ret);
00195 pkt->stream_index = cdxl->video_stream_index;
00196 pkt->flags |= AV_PKT_FLAG_KEY;
00197 pkt->pos = pos;
00198 pkt->duration = cdxl->framerate ? 1 : audio_size ? audio_size : 220;
00199 cdxl->read_chunk = audio_size;
00200 }
00201
00202 if (!cdxl->read_chunk)
00203 avio_skip(pb, current_size - audio_size - video_size - CDXL_HEADER_SIZE);
00204 return ret;
00205 }
00206
00207 #define OFFSET(x) offsetof(CDXLDemuxContext, x)
00208 static const AVOption cdxl_options[] = {
00209 { "sample_rate", "", OFFSET(sample_rate), AV_OPT_TYPE_INT, { .i64 = 11025 }, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
00210 { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_DECODING_PARAM },
00211 { NULL },
00212 };
00213
00214 static const AVClass cdxl_demuxer_class = {
00215 .class_name = "CDXL demuxer",
00216 .item_name = av_default_item_name,
00217 .option = cdxl_options,
00218 .version = LIBAVUTIL_VERSION_INT,
00219 };
00220
00221 AVInputFormat ff_cdxl_demuxer = {
00222 .name = "cdxl",
00223 .long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"),
00224 .priv_data_size = sizeof(CDXLDemuxContext),
00225 .read_probe = cdxl_read_probe,
00226 .read_header = cdxl_read_header,
00227 .read_packet = cdxl_read_packet,
00228 .extensions = "cdxl,xl",
00229 .flags = AVFMT_GENERIC_INDEX,
00230 .priv_class = &cdxl_demuxer_class,
00231 };