Parse the ASMRuleBook SDP line to dynamically create one new AVStream for
[ffmpeg.git] / libavformat / siff.c
1 /*
2  * Beam Software SIFF 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 "avformat.h"
23
24 enum SIFFTags{
25     TAG_SIFF = MKTAG('S', 'I', 'F', 'F'),
26     TAG_BODY = MKTAG('B', 'O', 'D', 'Y'),
27     TAG_VBHD = MKTAG('V', 'B', 'H', 'D'),
28     TAG_SHDR = MKTAG('S', 'H', 'D', 'R'),
29     TAG_VBV1 = MKTAG('V', 'B', 'V', '1'),
30     TAG_SOUN = MKTAG('S', 'O', 'U', 'N'),
31 };
32
33 enum VBFlags{
34     VB_HAS_GMC     = 0x01,
35     VB_HAS_AUDIO   = 0x04,
36     VB_HAS_VIDEO   = 0x08,
37     VB_HAS_PALETTE = 0x10,
38     VB_HAS_LENGTH  = 0x20
39 };
40
41 typedef struct SIFFContext{
42     int frames;
43     int cur_frame;
44     int rate;
45     int bits;
46     int block_align;
47
48     int has_video;
49     int has_audio;
50
51     int curstrm;
52     int pktsize;
53     int gmcsize;
54     int sndsize;
55
56     int flags;
57     uint8_t gmc[4];
58 }SIFFContext;
59
60 static int siff_probe(AVProbeData *p)
61 {
62     /* check file header */
63     if (AV_RL32(p->buf) == TAG_SIFF)
64         return AVPROBE_SCORE_MAX;
65     else
66         return 0;
67 }
68
69 static int create_audio_stream(AVFormatContext *s, SIFFContext *c)
70 {
71     AVStream *ast;
72     ast = av_new_stream(s, 0);
73     if (!ast)
74         return -1;
75     ast->codec->codec_type      = CODEC_TYPE_AUDIO;
76     ast->codec->codec_id        = CODEC_ID_PCM_U8;
77     ast->codec->channels        = 1;
78     ast->codec->bits_per_coded_sample = c->bits;
79     ast->codec->sample_rate     = c->rate;
80     ast->codec->frame_size      = c->block_align;
81     av_set_pts_info(ast, 16, 1, c->rate);
82     return 0;
83 }
84
85 static int siff_parse_vbv1(AVFormatContext *s, SIFFContext *c, ByteIOContext *pb)
86 {
87     AVStream *st;
88     int width, height;
89
90     if (get_le32(pb) != TAG_VBHD){
91         av_log(s, AV_LOG_ERROR, "Header chunk is missing\n");
92         return -1;
93     }
94     if(get_be32(pb) != 32){
95         av_log(s, AV_LOG_ERROR, "Header chunk size is incorrect\n");
96         return -1;
97     }
98     if(get_le16(pb) != 1){
99         av_log(s, AV_LOG_ERROR, "Incorrect header version\n");
100         return -1;
101     }
102     width = get_le16(pb);
103     height = get_le16(pb);
104     url_fskip(pb, 4);
105     c->frames = get_le16(pb);
106     if(!c->frames){
107         av_log(s, AV_LOG_ERROR, "File contains no frames ???\n");
108         return -1;
109     }
110     c->bits = get_le16(pb);
111     c->rate = get_le16(pb);
112     c->block_align = c->rate * (c->bits >> 3);
113
114     url_fskip(pb, 16); //zeroes
115
116     st = av_new_stream(s, 0);
117     if (!st)
118         return -1;
119     st->codec->codec_type = CODEC_TYPE_VIDEO;
120     st->codec->codec_id   = CODEC_ID_VB;
121     st->codec->codec_tag  = MKTAG('V', 'B', 'V', '1');
122     st->codec->width      = width;
123     st->codec->height     = height;
124     st->codec->pix_fmt    = PIX_FMT_PAL8;
125     av_set_pts_info(st, 16, 1, 12);
126
127     c->cur_frame = 0;
128     c->has_video = 1;
129     c->has_audio = !!c->rate;
130     c->curstrm = -1;
131     if (c->has_audio && create_audio_stream(s, c) < 0)
132         return -1;
133     return 0;
134 }
135
136 static int siff_parse_soun(AVFormatContext *s, SIFFContext *c, ByteIOContext *pb)
137 {
138     if (get_le32(pb) != TAG_SHDR){
139         av_log(s, AV_LOG_ERROR, "Header chunk is missing\n");
140         return -1;
141     }
142     if(get_be32(pb) != 8){
143         av_log(s, AV_LOG_ERROR, "Header chunk size is incorrect\n");
144         return -1;
145     }
146     url_fskip(pb, 4); //unknown value
147     c->rate = get_le16(pb);
148     c->bits = get_le16(pb);
149     c->block_align = c->rate * (c->bits >> 3);
150     return create_audio_stream(s, c);
151 }
152
153 static int siff_read_header(AVFormatContext *s, AVFormatParameters *ap)
154 {
155     ByteIOContext *pb = s->pb;
156     SIFFContext *c = s->priv_data;
157     uint32_t tag;
158
159     if (get_le32(pb) != TAG_SIFF)
160         return -1;
161     url_fskip(pb, 4); //ignore size
162     tag = get_le32(pb);
163
164     if (tag != TAG_VBV1 && tag != TAG_SOUN){
165         av_log(s, AV_LOG_ERROR, "Not a VBV file\n");
166         return -1;
167     }
168
169     if (tag == TAG_VBV1 && siff_parse_vbv1(s, c, pb) < 0)
170         return -1;
171     if (tag == TAG_SOUN && siff_parse_soun(s, c, pb) < 0)
172         return -1;
173     if (get_le32(pb) != MKTAG('B', 'O', 'D', 'Y')){
174         av_log(s, AV_LOG_ERROR, "'BODY' chunk is missing\n");
175         return -1;
176     }
177     url_fskip(pb, 4); //ignore size
178
179     return 0;
180 }
181
182 static int siff_read_packet(AVFormatContext *s, AVPacket *pkt)
183 {
184     SIFFContext *c = s->priv_data;
185     int size;
186
187     if (c->has_video){
188         if (c->cur_frame >= c->frames)
189             return AVERROR(EIO);
190         if (c->curstrm == -1){
191             c->pktsize = get_le32(s->pb) - 4;
192             c->flags = get_le16(s->pb);
193             c->gmcsize = (c->flags & VB_HAS_GMC) ? 4 : 0;
194             if (c->gmcsize)
195                 get_buffer(s->pb, c->gmc, c->gmcsize);
196             c->sndsize = (c->flags & VB_HAS_AUDIO) ? get_le32(s->pb): 0;
197             c->curstrm = !!(c->flags & VB_HAS_AUDIO);
198         }
199
200         if (!c->curstrm){
201             size = c->pktsize - c->sndsize;
202             if (av_new_packet(pkt, size) < 0)
203                 return AVERROR(ENOMEM);
204             AV_WL16(pkt->data, c->flags);
205             if (c->gmcsize)
206                 memcpy(pkt->data + 2, c->gmc, c->gmcsize);
207             get_buffer(s->pb, pkt->data + 2 + c->gmcsize, size - c->gmcsize - 2);
208             pkt->stream_index = 0;
209             c->curstrm = -1;
210         }else{
211             if (av_get_packet(s->pb, pkt, c->sndsize - 4) < 0)
212                 return AVERROR(EIO);
213             pkt->stream_index = 1;
214             c->curstrm = 0;
215         }
216         if(!c->cur_frame || c->curstrm)
217             pkt->flags |= PKT_FLAG_KEY;
218         if (c->curstrm == -1)
219             c->cur_frame++;
220     }else{
221         size = av_get_packet(s->pb, pkt, c->block_align);
222         if(size <= 0)
223             return AVERROR(EIO);
224     }
225     return pkt->size;
226 }
227
228 AVInputFormat siff_demuxer = {
229     "siff",
230     NULL_IF_CONFIG_SMALL("Beam Software SIFF"),
231     sizeof(SIFFContext),
232     siff_probe,
233     siff_read_header,
234     siff_read_packet,
235     .extensions = "vb,son"
236 };