]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/smacker.c
avformat: Constify all muxer/demuxers
[ffmpeg] / libavformat / smacker.c
index b5c858aa9b1fcf6eafee8069bfd34bef388425d1..a96093b191dd0cb692e16ef4554291e9c99e168b 100644 (file)
 
 #include <inttypes.h>
 
-#include "libavutil/bswap.h"
 #include "libavutil/channel_layout.h"
 #include "libavutil/intreadwrite.h"
 #include "avformat.h"
+#include "avio_internal.h"
 #include "internal.h"
 
 #define SMACKER_PAL 0x01
@@ -43,42 +43,25 @@ enum SAudFlags {
 };
 
 typedef struct SmackerContext {
-    /* Smacker file header */
-    uint32_t magic;
-    uint32_t width, height;
     uint32_t frames;
-    int      pts_inc;
-    uint32_t flags;
-    uint32_t audio[7];
-    uint32_t treesize;
-    uint32_t mmap_size, mclr_size, full_size, type_size;
-    uint8_t  aflags[7];
-    uint32_t rates[7];
-    uint32_t pad;
     /* frame info */
     uint32_t *frm_size;
     uint8_t  *frm_flags;
     /* internal variables */
+    int64_t next_frame_pos;
     int cur_frame;
-    int is_ver4;
-    int64_t cur_pts;
+    int videoindex;
+    int indexes[7];
+    int duration_size[7];
     /* current frame for demuxing */
+    uint32_t frame_size;
+    int flags;
+    int next_audio_index;
+    int new_palette;
     uint8_t pal[768];
-    int indexes[7];
-    int videoindex;
-    uint8_t *bufs[7];
-    int buf_sizes[7];
-    int stream_id[7];
-    int curstream;
-    int64_t nextpos;
     int64_t aud_pts[7];
 } SmackerContext;
 
-typedef struct SmackerFrame {
-    int64_t pts;
-    int stream;
-} SmackerFrame;
-
 /* palette used in Smacker */
 static const uint8_t smk_pal[64] = {
     0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C,
@@ -108,199 +91,192 @@ static int smacker_read_header(AVFormatContext *s)
 {
     AVIOContext *pb = s->pb;
     SmackerContext *smk = s->priv_data;
-    AVStream *st, *ast[7];
-    int i, ret;
+    AVStream *st;
+    AVCodecParameters *par;
+    uint32_t magic, width, height, flags, treesize;
+    int i, ret, pts_inc;
     int tbase;
 
     /* read and check header */
-    smk->magic = avio_rl32(pb);
-    if (smk->magic != MKTAG('S', 'M', 'K', '2') && smk->magic != MKTAG('S', 'M', 'K', '4'))
+    magic  = avio_rl32(pb);
+    if (magic != MKTAG('S', 'M', 'K', '2') && magic != MKTAG('S', 'M', 'K', '4'))
         return AVERROR_INVALIDDATA;
-    smk->width = avio_rl32(pb);
-    smk->height = avio_rl32(pb);
+    width  = avio_rl32(pb);
+    height = avio_rl32(pb);
     smk->frames = avio_rl32(pb);
-    smk->pts_inc = (int32_t)avio_rl32(pb);
-    if (smk->pts_inc > INT_MAX / 100) {
-        av_log(s, AV_LOG_ERROR, "pts_inc %d is too large\n", smk->pts_inc);
+    pts_inc = avio_rl32(pb);
+    if (pts_inc > INT_MAX / 100 || pts_inc == INT_MIN) {
+        av_log(s, AV_LOG_ERROR, "pts_inc %d is invalid\n", pts_inc);
         return AVERROR_INVALIDDATA;
     }
 
-    smk->flags = avio_rl32(pb);
-    if(smk->flags & SMACKER_FLAG_RING_FRAME)
+    flags = avio_rl32(pb);
+    if (flags & SMACKER_FLAG_RING_FRAME)
         smk->frames++;
-    for(i = 0; i < 7; i++)
-        smk->audio[i] = avio_rl32(pb);
-    smk->treesize = avio_rl32(pb);
-
-    if(smk->treesize >= UINT_MAX/4){ // smk->treesize + 16 must not overflow (this check is probably redundant)
-        av_log(s, AV_LOG_ERROR, "treesize too large\n");
-        return AVERROR_INVALIDDATA;
-    }
-
-//FIXME remove extradata "rebuilding"
-    smk->mmap_size = avio_rl32(pb);
-    smk->mclr_size = avio_rl32(pb);
-    smk->full_size = avio_rl32(pb);
-    smk->type_size = avio_rl32(pb);
-    for(i = 0; i < 7; i++) {
-        smk->rates[i]  = avio_rl24(pb);
-        smk->aflags[i] = avio_r8(pb);
-    }
-    smk->pad = avio_rl32(pb);
-    /* setup data */
-    if(smk->frames > 0xFFFFFF) {
+    if (smk->frames > 0xFFFFFF) {
         av_log(s, AV_LOG_ERROR, "Too many frames: %"PRIu32"\n", smk->frames);
         return AVERROR_INVALIDDATA;
     }
-    smk->frm_size = av_malloc_array(smk->frames, sizeof(*smk->frm_size));
-    smk->frm_flags = av_malloc(smk->frames);
-    if (!smk->frm_size || !smk->frm_flags) {
-        av_freep(&smk->frm_size);
-        av_freep(&smk->frm_flags);
-        return AVERROR(ENOMEM);
-    }
 
-    smk->is_ver4 = (smk->magic != MKTAG('S', 'M', 'K', '2'));
+    avio_skip(pb, 28); /* Unused audio related data */
 
-    /* read frame info */
-    for(i = 0; i < smk->frames; i++) {
-        smk->frm_size[i] = avio_rl32(pb);
-    }
-    for(i = 0; i < smk->frames; i++) {
-        smk->frm_flags[i] = avio_r8(pb);
+    treesize = avio_rl32(pb);
+    if (treesize >= UINT_MAX/4) {
+        // treesize + 16 must not overflow (this check is probably redundant)
+        av_log(s, AV_LOG_ERROR, "treesize too large\n");
+        return AVERROR_INVALIDDATA;
     }
 
-    /* init video codec */
     st = avformat_new_stream(s, NULL);
     if (!st)
         return AVERROR(ENOMEM);
+
     smk->videoindex = st->index;
-    st->codecpar->width = smk->width;
-    st->codecpar->height = smk->height;
-    st->codecpar->format = AV_PIX_FMT_PAL8;
-    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
-    st->codecpar->codec_id = AV_CODEC_ID_SMACKVIDEO;
-    st->codecpar->codec_tag = smk->magic;
     /* Smacker uses 100000 as internal timebase */
-    if(smk->pts_inc < 0)
-        smk->pts_inc = -smk->pts_inc;
+    if (pts_inc < 0)
+        pts_inc = -pts_inc;
     else
-        smk->pts_inc *= 100;
+        pts_inc *= 100;
     tbase = 100000;
-    av_reduce(&tbase, &smk->pts_inc, tbase, smk->pts_inc, (1UL<<31)-1);
-    avpriv_set_pts_info(st, 33, smk->pts_inc, tbase);
+    av_reduce(&tbase, &pts_inc, tbase, pts_inc, (1UL << 31) - 1);
+    avpriv_set_pts_info(st, 33, pts_inc, tbase);
     st->duration = smk->frames;
+
+    /* init video codec */
+    par = st->codecpar;
+    par->width      = width;
+    par->height     = height;
+    par->format     = AV_PIX_FMT_PAL8;
+    par->codec_type = AVMEDIA_TYPE_VIDEO;
+    par->codec_id   = AV_CODEC_ID_SMACKVIDEO;
+    par->codec_tag  = magic;
+
+    if ((ret = ff_alloc_extradata(par, treesize + 16)) < 0) {
+        av_log(s, AV_LOG_ERROR,
+               "Cannot allocate %"PRIu32" bytes of extradata\n",
+               treesize + 16);
+        return ret;
+    }
+    if ((ret = ffio_read_size(pb, par->extradata, 16)) < 0)
+        return ret;
+
     /* handle possible audio streams */
-    for(i = 0; i < 7; i++) {
+    for (i = 0; i < 7; i++) {
+        uint32_t rate = avio_rl24(pb);
+        uint8_t aflag = avio_r8(pb);
+
         smk->indexes[i] = -1;
-        if (smk->rates[i]) {
-            ast[i] = avformat_new_stream(s, NULL);
-            if (!ast[i])
+
+        if (rate) {
+            AVStream *ast = avformat_new_stream(s, NULL);
+            AVCodecParameters *par;
+            if (!ast)
                 return AVERROR(ENOMEM);
-            smk->indexes[i] = ast[i]->index;
-            ast[i]->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
-            if (smk->aflags[i] & SMK_AUD_BINKAUD) {
-                ast[i]->codecpar->codec_id = AV_CODEC_ID_BINKAUDIO_RDFT;
-            } else if (smk->aflags[i] & SMK_AUD_USEDCT) {
-                ast[i]->codecpar->codec_id = AV_CODEC_ID_BINKAUDIO_DCT;
-            } else if (smk->aflags[i] & SMK_AUD_PACKED){
-                ast[i]->codecpar->codec_id = AV_CODEC_ID_SMACKAUDIO;
-                ast[i]->codecpar->codec_tag = MKTAG('S', 'M', 'K', 'A');
+
+            smk->indexes[i] = ast->index;
+            par = ast->codecpar;
+            par->codec_type = AVMEDIA_TYPE_AUDIO;
+            if (aflag & SMK_AUD_BINKAUD) {
+                par->codec_id  = AV_CODEC_ID_BINKAUDIO_RDFT;
+            } else if (aflag & SMK_AUD_USEDCT) {
+                par->codec_id  = AV_CODEC_ID_BINKAUDIO_DCT;
+            } else if (aflag & SMK_AUD_PACKED) {
+                par->codec_id  = AV_CODEC_ID_SMACKAUDIO;
+                par->codec_tag = MKTAG('S', 'M', 'K', 'A');
             } else {
-                ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_U8;
+                par->codec_id  = AV_CODEC_ID_PCM_U8;
             }
-            if (smk->aflags[i] & SMK_AUD_STEREO) {
-                ast[i]->codecpar->channels       = 2;
-                ast[i]->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
+            if (aflag & SMK_AUD_STEREO) {
+                par->channels       = 2;
+                par->channel_layout = AV_CH_LAYOUT_STEREO;
             } else {
-                ast[i]->codecpar->channels       = 1;
-                ast[i]->codecpar->channel_layout = AV_CH_LAYOUT_MONO;
+                par->channels       = 1;
+                par->channel_layout = AV_CH_LAYOUT_MONO;
             }
-            ast[i]->codecpar->sample_rate = smk->rates[i];
-            ast[i]->codecpar->bits_per_coded_sample = (smk->aflags[i] & SMK_AUD_16BITS) ? 16 : 8;
-            if(ast[i]->codecpar->bits_per_coded_sample == 16 && ast[i]->codecpar->codec_id == AV_CODEC_ID_PCM_U8)
-                ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE;
-            avpriv_set_pts_info(ast[i], 64, 1, ast[i]->codecpar->sample_rate
-                    * ast[i]->codecpar->channels * ast[i]->codecpar->bits_per_coded_sample / 8);
+            par->sample_rate = rate;
+            par->bits_per_coded_sample = (aflag & SMK_AUD_16BITS) ? 16 : 8;
+            if (par->bits_per_coded_sample == 16 &&
+                par->codec_id == AV_CODEC_ID_PCM_U8)
+                par->codec_id = AV_CODEC_ID_PCM_S16LE;
+            else
+                smk->duration_size[i] = 4;
+            avpriv_set_pts_info(ast, 64, 1, par->sample_rate * par->channels
+                                            * par->bits_per_coded_sample / 8);
         }
     }
 
+    avio_rl32(pb); /* padding */
 
-    /* load trees to extradata, they will be unpacked by decoder */
-    if(ff_alloc_extradata(st->codecpar, smk->treesize + 16)){
-        av_log(s, AV_LOG_ERROR,
-               "Cannot allocate %"PRIu32" bytes of extradata\n",
-               smk->treesize + 16);
-        av_freep(&smk->frm_size);
-        av_freep(&smk->frm_flags);
+    /* setup data */
+    st->priv_data  = av_malloc_array(smk->frames, sizeof(*smk->frm_size) +
+                                                  sizeof(*smk->frm_flags));
+    if (!st->priv_data)
         return AVERROR(ENOMEM);
+    smk->frm_size  = st->priv_data;
+    smk->frm_flags = (void*)(smk->frm_size + smk->frames);
+
+    /* read frame info */
+    for (i = 0; i < smk->frames; i++) {
+        smk->frm_size[i] = avio_rl32(pb);
     }
-    ret = avio_read(pb, st->codecpar->extradata + 16, st->codecpar->extradata_size - 16);
-    if(ret != st->codecpar->extradata_size - 16){
-        av_freep(&smk->frm_size);
-        av_freep(&smk->frm_flags);
-        return AVERROR(EIO);
+    if ((ret = ffio_read_size(pb, smk->frm_flags, smk->frames)) < 0 ||
+        /* load trees to extradata, they will be unpacked by decoder */
+        (ret = ffio_read_size(pb, par->extradata + 16,
+                              par->extradata_size - 16)) < 0) {
+        return ret;
     }
-    ((int32_t*)st->codecpar->extradata)[0] = av_le2ne32(smk->mmap_size);
-    ((int32_t*)st->codecpar->extradata)[1] = av_le2ne32(smk->mclr_size);
-    ((int32_t*)st->codecpar->extradata)[2] = av_le2ne32(smk->full_size);
-    ((int32_t*)st->codecpar->extradata)[3] = av_le2ne32(smk->type_size);
-
-    smk->curstream = -1;
-    smk->nextpos = avio_tell(pb);
 
     return 0;
 }
 
-
 static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     SmackerContext *smk = s->priv_data;
     int flags;
     int ret;
-    int i;
-    int frame_size = 0;
-    int palchange = 0;
 
     if (avio_feof(s->pb) || smk->cur_frame >= smk->frames)
         return AVERROR_EOF;
 
     /* if we demuxed all streams, pass another frame */
-    if(smk->curstream < 0) {
-        avio_seek(s->pb, smk->nextpos, 0);
-        frame_size = smk->frm_size[smk->cur_frame] & (~3);
+    if (!smk->next_audio_index) {
+        smk->frame_size = smk->frm_size[smk->cur_frame] & (~3);
+        smk->next_frame_pos = avio_tell(s->pb) + smk->frame_size;
         flags = smk->frm_flags[smk->cur_frame];
+        smk->flags = flags >> 1;
         /* handle palette change event */
-        if(flags & SMACKER_PAL){
+        if (flags & SMACKER_PAL) {
             int size, sz, t, off, j, pos;
             uint8_t *pal = smk->pal;
             uint8_t oldpal[768];
 
             memcpy(oldpal, pal, 768);
             size = avio_r8(s->pb);
-            size = size * 4 - 1;
-            if(size + 1 > frame_size)
-                return AVERROR_INVALIDDATA;
-            frame_size -= size;
-            frame_size--;
+            size = size * 4;
+            if (size > smk->frame_size) {
+                ret = AVERROR_INVALIDDATA;
+                goto next_frame;
+            }
+            smk->frame_size -= size--;
             sz = 0;
             pos = avio_tell(s->pb) + size;
-            while(sz < 256){
+            while (sz < 256) {
                 t = avio_r8(s->pb);
-                if(t & 0x80){ /* skip palette entries */
-                    sz += (t & 0x7F) + 1;
+                if (t & 0x80) { /* skip palette entries */
+                    sz  +=  (t & 0x7F) + 1;
                     pal += ((t & 0x7F) + 1) * 3;
-                } else if(t & 0x40){ /* copy with offset */
+                } else if (t & 0x40) { /* copy with offset */
                     off = avio_r8(s->pb);
                     j = (t & 0x3F) + 1;
                     if (off + j > 0x100) {
                         av_log(s, AV_LOG_ERROR,
                                "Invalid palette update, offset=%d length=%d extends beyond palette size\n",
                                off, j);
-                        return AVERROR_INVALIDDATA;
+                        ret = AVERROR_INVALIDDATA;
+                        goto next_frame;
                     }
                     off *= 3;
-                    while(j-- && sz < 256) {
+                    while (j-- && sz < 256) {
                         *pal++ = oldpal[off + 0];
                         *pal++ = oldpal[off + 1];
                         *pal++ = oldpal[off + 2];
@@ -315,87 +291,107 @@ static int smacker_read_packet(AVFormatContext *s, AVPacket *pkt)
                 }
             }
             avio_seek(s->pb, pos, 0);
-            palchange |= 1;
+            smk->new_palette = 1;
         }
-        flags >>= 1;
-        smk->curstream = -1;
-        /* if audio chunks are present, put them to stack and retrieve later */
-        for(i = 0; i < 7; i++) {
-            if(flags & 1) {
-                uint32_t size;
-                int err;
-
-                size = avio_rl32(s->pb) - 4;
-                if (!size || size + 4LL > frame_size) {
-                    av_log(s, AV_LOG_ERROR, "Invalid audio part size\n");
-                    return AVERROR_INVALIDDATA;
-                }
-                frame_size -= size;
-                frame_size -= 4;
-                smk->curstream++;
-                if ((err = av_reallocp(&smk->bufs[smk->curstream], size)) < 0) {
-                    smk->buf_sizes[smk->curstream] = 0;
-                    return err;
-                }
-                smk->buf_sizes[smk->curstream] = size;
-                ret = avio_read(s->pb, smk->bufs[smk->curstream], size);
-                if(ret != size)
-                    return AVERROR(EIO);
-                smk->stream_id[smk->curstream] = smk->indexes[i];
+    }
+
+    for (int i = smk->next_audio_index; i < 7; i++) {
+        if (smk->flags & (1 << i)) {
+            uint32_t size;
+
+            size = avio_rl32(s->pb);
+            if ((int)size < 4 + smk->duration_size[i] || size > smk->frame_size) {
+                av_log(s, AV_LOG_ERROR, "Invalid audio part size\n");
+                ret = AVERROR_INVALIDDATA;
+                goto next_frame;
             }
-            flags >>= 1;
+            smk->frame_size -= size;
+            size            -= 4;
+
+            if (smk->indexes[i] < 0 ||
+                s->streams[smk->indexes[i]]->discard >= AVDISCARD_ALL) {
+                smk->aud_pts[i] += smk->duration_size[i] ? avio_rl32(s->pb)
+                                                         : size;
+                avio_skip(s->pb, size - smk->duration_size[i]);
+                continue;
+            }
+            if ((ret = av_get_packet(s->pb, pkt, size)) != size) {
+                ret = ret < 0 ? ret : AVERROR_INVALIDDATA;
+                goto next_frame;
+            }
+            pkt->stream_index = smk->indexes[i];
+            pkt->pts          = smk->aud_pts[i];
+            pkt->duration     = smk->duration_size[i] ? AV_RL32(pkt->data)
+                                                      : size;
+            smk->aud_pts[i]  += pkt->duration;
+            smk->next_audio_index = i + 1;
+            return 0;
         }
-        if (frame_size < 0 || frame_size >= INT_MAX/2)
-            return AVERROR_INVALIDDATA;
-        if (av_new_packet(pkt, frame_size + 769))
-            return AVERROR(ENOMEM);
-        if(smk->frm_size[smk->cur_frame] & 1)
-            palchange |= 2;
-        pkt->data[0] = palchange;
-        memcpy(pkt->data + 1, smk->pal, 768);
-        ret = avio_read(s->pb, pkt->data + 769, frame_size);
-        if(ret != frame_size)
-            return AVERROR(EIO);
-        pkt->stream_index = smk->videoindex;
-        pkt->pts          = smk->cur_frame;
-        pkt->size = ret + 769;
-        smk->cur_frame++;
-        smk->nextpos = avio_tell(s->pb);
-    } else {
-        if (smk->stream_id[smk->curstream] < 0 || !smk->bufs[smk->curstream])
-            return AVERROR_INVALIDDATA;
-        if (av_new_packet(pkt, smk->buf_sizes[smk->curstream]))
-            return AVERROR(ENOMEM);
-        memcpy(pkt->data, smk->bufs[smk->curstream], smk->buf_sizes[smk->curstream]);
-        pkt->size = smk->buf_sizes[smk->curstream];
-        pkt->stream_index = smk->stream_id[smk->curstream];
-        pkt->pts = smk->aud_pts[smk->curstream];
-        smk->aud_pts[smk->curstream] += AV_RL32(pkt->data);
-        smk->curstream--;
     }
 
+    if (s->streams[smk->videoindex]->discard >= AVDISCARD_ALL) {
+        ret = FFERROR_REDO;
+        goto next_frame;
+    }
+    if (smk->frame_size >= INT_MAX/2) {
+        ret = AVERROR_INVALIDDATA;
+        goto next_frame;
+    }
+    if ((ret = av_new_packet(pkt, smk->frame_size + 769)) < 0)
+        goto next_frame;
+    flags = smk->new_palette;
+    if (smk->frm_size[smk->cur_frame] & 1)
+        flags |= 2;
+    pkt->data[0] = flags;
+    memcpy(pkt->data + 1, smk->pal, 768);
+    ret = ffio_read_size(s->pb, pkt->data + 769, smk->frame_size);
+    if (ret < 0)
+        goto next_frame;
+    pkt->stream_index = smk->videoindex;
+    pkt->pts          = smk->cur_frame;
+    smk->next_audio_index = 0;
+    smk->new_palette = 0;
+    smk->cur_frame++;
+
     return 0;
+next_frame:
+    avio_seek(s->pb, smk->next_frame_pos, SEEK_SET);
+    smk->next_audio_index = 0;
+    smk->cur_frame++;
+    return ret;
 }
 
-static int smacker_read_close(AVFormatContext *s)
+static int smacker_read_seek(AVFormatContext *s, int stream_index,
+                             int64_t timestamp, int flags)
 {
     SmackerContext *smk = s->priv_data;
-    int i;
+    int64_t ret;
+
+    /* only rewinding to start is supported */
+    if (timestamp != 0) {
+        av_log(s, AV_LOG_ERROR,
+               "Random seeks are not supported (can only seek to start).\n");
+        return AVERROR(EINVAL);
+    }
+
+    if ((ret = avio_seek(s->pb, s->internal->data_offset, SEEK_SET)) < 0)
+        return ret;
 
-    for(i = 0; i < 7; i++)
-        av_freep(&smk->bufs[i]);
-    av_freep(&smk->frm_size);
-    av_freep(&smk->frm_flags);
+    smk->cur_frame = 0;
+    smk->next_audio_index = 0;
+    smk->new_palette = 0;
+    memset(smk->pal, 0, sizeof(smk->pal));
+    memset(smk->aud_pts, 0, sizeof(smk->aud_pts));
 
     return 0;
 }
 
-AVInputFormat ff_smacker_demuxer = {
+const AVInputFormat ff_smacker_demuxer = {
     .name           = "smk",
     .long_name      = NULL_IF_CONFIG_SMALL("Smacker"),
     .priv_data_size = sizeof(SmackerContext),
     .read_probe     = smacker_probe,
     .read_header    = smacker_read_header,
     .read_packet    = smacker_read_packet,
-    .read_close     = smacker_read_close,
+    .read_seek      = smacker_read_seek,
 };