| 1 | /* |
| 2 | * DXA demuxer |
| 3 | * Copyright (c) 2007 Konstantin Shishkov |
| 4 | * |
| 5 | * This file is part of FFmpeg. |
| 6 | * |
| 7 | * FFmpeg is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * FFmpeg is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with FFmpeg; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 20 | */ |
| 21 | |
| 22 | #include <inttypes.h> |
| 23 | |
| 24 | #include "libavutil/intreadwrite.h" |
| 25 | #include "avformat.h" |
| 26 | #include "internal.h" |
| 27 | #include "riff.h" |
| 28 | |
| 29 | #define DXA_EXTRA_SIZE 9 |
| 30 | |
| 31 | typedef struct{ |
| 32 | int frames; |
| 33 | int has_sound; |
| 34 | int bpc; |
| 35 | uint32_t bytes_left; |
| 36 | int64_t wavpos, vidpos; |
| 37 | int readvid; |
| 38 | }DXAContext; |
| 39 | |
| 40 | static int dxa_probe(AVProbeData *p) |
| 41 | { |
| 42 | int w, h; |
| 43 | if (p->buf_size < 15) |
| 44 | return 0; |
| 45 | w = AV_RB16(p->buf + 11); |
| 46 | h = AV_RB16(p->buf + 13); |
| 47 | /* check file header */ |
| 48 | if (p->buf[0] == 'D' && p->buf[1] == 'E' && |
| 49 | p->buf[2] == 'X' && p->buf[3] == 'A' && |
| 50 | w && w <= 2048 && h && h <= 2048) |
| 51 | return AVPROBE_SCORE_MAX; |
| 52 | else |
| 53 | return 0; |
| 54 | } |
| 55 | |
| 56 | static int dxa_read_header(AVFormatContext *s) |
| 57 | { |
| 58 | AVIOContext *pb = s->pb; |
| 59 | DXAContext *c = s->priv_data; |
| 60 | AVStream *st, *ast; |
| 61 | uint32_t tag; |
| 62 | int32_t fps; |
| 63 | int w, h; |
| 64 | int num, den; |
| 65 | int flags; |
| 66 | int ret; |
| 67 | |
| 68 | tag = avio_rl32(pb); |
| 69 | if (tag != MKTAG('D', 'E', 'X', 'A')) |
| 70 | return AVERROR_INVALIDDATA; |
| 71 | flags = avio_r8(pb); |
| 72 | c->frames = avio_rb16(pb); |
| 73 | if(!c->frames){ |
| 74 | av_log(s, AV_LOG_ERROR, "File contains no frames ???\n"); |
| 75 | return AVERROR_INVALIDDATA; |
| 76 | } |
| 77 | |
| 78 | fps = avio_rb32(pb); |
| 79 | if(fps > 0){ |
| 80 | den = 1000; |
| 81 | num = fps; |
| 82 | }else if (fps < 0){ |
| 83 | den = 100000; |
| 84 | num = -fps; |
| 85 | }else{ |
| 86 | den = 10; |
| 87 | num = 1; |
| 88 | } |
| 89 | w = avio_rb16(pb); |
| 90 | h = avio_rb16(pb); |
| 91 | c->has_sound = 0; |
| 92 | |
| 93 | st = avformat_new_stream(s, NULL); |
| 94 | if (!st) |
| 95 | return AVERROR(ENOMEM); |
| 96 | |
| 97 | // Parse WAV data header |
| 98 | if(avio_rl32(pb) == MKTAG('W', 'A', 'V', 'E')){ |
| 99 | uint32_t size, fsize; |
| 100 | c->has_sound = 1; |
| 101 | size = avio_rb32(pb); |
| 102 | c->vidpos = avio_tell(pb) + size; |
| 103 | avio_skip(pb, 16); |
| 104 | fsize = avio_rl32(pb); |
| 105 | |
| 106 | ast = avformat_new_stream(s, NULL); |
| 107 | if (!ast) |
| 108 | return AVERROR(ENOMEM); |
| 109 | ret = ff_get_wav_header(pb, ast->codec, fsize); |
| 110 | if (ret < 0) |
| 111 | return ret; |
| 112 | if (ast->codec->sample_rate > 0) |
| 113 | avpriv_set_pts_info(ast, 64, 1, ast->codec->sample_rate); |
| 114 | // find 'data' chunk |
| 115 | while(avio_tell(pb) < c->vidpos && !avio_feof(pb)){ |
| 116 | tag = avio_rl32(pb); |
| 117 | fsize = avio_rl32(pb); |
| 118 | if(tag == MKTAG('d', 'a', 't', 'a')) break; |
| 119 | avio_skip(pb, fsize); |
| 120 | } |
| 121 | c->bpc = (fsize + c->frames - 1) / c->frames; |
| 122 | if(ast->codec->block_align) |
| 123 | c->bpc = ((c->bpc + ast->codec->block_align - 1) / ast->codec->block_align) * ast->codec->block_align; |
| 124 | c->bytes_left = fsize; |
| 125 | c->wavpos = avio_tell(pb); |
| 126 | avio_seek(pb, c->vidpos, SEEK_SET); |
| 127 | } |
| 128 | |
| 129 | /* now we are ready: build format streams */ |
| 130 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
| 131 | st->codec->codec_id = AV_CODEC_ID_DXA; |
| 132 | st->codec->width = w; |
| 133 | st->codec->height = h; |
| 134 | av_reduce(&den, &num, den, num, (1UL<<31)-1); |
| 135 | avpriv_set_pts_info(st, 33, num, den); |
| 136 | /* flags & 0x80 means that image is interlaced, |
| 137 | * flags & 0x40 means that image has double height |
| 138 | * either way set true height |
| 139 | */ |
| 140 | if(flags & 0xC0){ |
| 141 | st->codec->height >>= 1; |
| 142 | } |
| 143 | c->readvid = !c->has_sound; |
| 144 | c->vidpos = avio_tell(pb); |
| 145 | s->start_time = 0; |
| 146 | s->duration = (int64_t)c->frames * AV_TIME_BASE * num / den; |
| 147 | av_log(s, AV_LOG_DEBUG, "%d frame(s)\n",c->frames); |
| 148 | |
| 149 | return 0; |
| 150 | } |
| 151 | |
| 152 | static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) |
| 153 | { |
| 154 | DXAContext *c = s->priv_data; |
| 155 | int ret; |
| 156 | uint32_t size; |
| 157 | uint8_t buf[DXA_EXTRA_SIZE], pal[768+4]; |
| 158 | int pal_size = 0; |
| 159 | |
| 160 | if(!c->readvid && c->has_sound && c->bytes_left){ |
| 161 | c->readvid = 1; |
| 162 | avio_seek(s->pb, c->wavpos, SEEK_SET); |
| 163 | size = FFMIN(c->bytes_left, c->bpc); |
| 164 | ret = av_get_packet(s->pb, pkt, size); |
| 165 | pkt->stream_index = 1; |
| 166 | if(ret != size) |
| 167 | return AVERROR(EIO); |
| 168 | c->bytes_left -= size; |
| 169 | c->wavpos = avio_tell(s->pb); |
| 170 | return 0; |
| 171 | } |
| 172 | avio_seek(s->pb, c->vidpos, SEEK_SET); |
| 173 | while(!avio_feof(s->pb) && c->frames){ |
| 174 | if ((ret = avio_read(s->pb, buf, 4)) != 4) { |
| 175 | av_log(s, AV_LOG_ERROR, "failed reading chunk type\n"); |
| 176 | return ret < 0 ? ret : AVERROR_INVALIDDATA; |
| 177 | } |
| 178 | switch(AV_RL32(buf)){ |
| 179 | case MKTAG('N', 'U', 'L', 'L'): |
| 180 | if(av_new_packet(pkt, 4 + pal_size) < 0) |
| 181 | return AVERROR(ENOMEM); |
| 182 | pkt->stream_index = 0; |
| 183 | if(pal_size) memcpy(pkt->data, pal, pal_size); |
| 184 | memcpy(pkt->data + pal_size, buf, 4); |
| 185 | c->frames--; |
| 186 | c->vidpos = avio_tell(s->pb); |
| 187 | c->readvid = 0; |
| 188 | return 0; |
| 189 | case MKTAG('C', 'M', 'A', 'P'): |
| 190 | pal_size = 768+4; |
| 191 | memcpy(pal, buf, 4); |
| 192 | avio_read(s->pb, pal + 4, 768); |
| 193 | break; |
| 194 | case MKTAG('F', 'R', 'A', 'M'): |
| 195 | if ((ret = avio_read(s->pb, buf + 4, DXA_EXTRA_SIZE - 4)) != DXA_EXTRA_SIZE - 4) { |
| 196 | av_log(s, AV_LOG_ERROR, "failed reading dxa_extra\n"); |
| 197 | return ret < 0 ? ret : AVERROR_INVALIDDATA; |
| 198 | } |
| 199 | size = AV_RB32(buf + 5); |
| 200 | if(size > 0xFFFFFF){ |
| 201 | av_log(s, AV_LOG_ERROR, "Frame size is too big: %"PRIu32"\n", |
| 202 | size); |
| 203 | return AVERROR_INVALIDDATA; |
| 204 | } |
| 205 | if(av_new_packet(pkt, size + DXA_EXTRA_SIZE + pal_size) < 0) |
| 206 | return AVERROR(ENOMEM); |
| 207 | memcpy(pkt->data + pal_size, buf, DXA_EXTRA_SIZE); |
| 208 | ret = avio_read(s->pb, pkt->data + DXA_EXTRA_SIZE + pal_size, size); |
| 209 | if(ret != size){ |
| 210 | av_free_packet(pkt); |
| 211 | return AVERROR(EIO); |
| 212 | } |
| 213 | if(pal_size) memcpy(pkt->data, pal, pal_size); |
| 214 | pkt->stream_index = 0; |
| 215 | c->frames--; |
| 216 | c->vidpos = avio_tell(s->pb); |
| 217 | c->readvid = 0; |
| 218 | return 0; |
| 219 | default: |
| 220 | av_log(s, AV_LOG_ERROR, "Unknown tag %c%c%c%c\n", buf[0], buf[1], buf[2], buf[3]); |
| 221 | return AVERROR_INVALIDDATA; |
| 222 | } |
| 223 | } |
| 224 | return AVERROR_EOF; |
| 225 | } |
| 226 | |
| 227 | AVInputFormat ff_dxa_demuxer = { |
| 228 | .name = "dxa", |
| 229 | .long_name = NULL_IF_CONFIG_SMALL("DXA"), |
| 230 | .priv_data_size = sizeof(DXAContext), |
| 231 | .read_probe = dxa_probe, |
| 232 | .read_header = dxa_read_header, |
| 233 | .read_packet = dxa_read_packet, |
| 234 | }; |