qt: playlist: use item title if available
[vlc.git] / modules / stream_filter / skiptags.c
blob71f25140670c47e0b08df8e979a4639a815ac596
1 /*****************************************************************************
2 * skiptags.c:
3 *****************************************************************************
4 * Copyright © 2005-2008 VLC authors and VideoLAN
5 * Copyright © 2016-2017 Rémi Denis-Courmont
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
12 * This program 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
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20 *****************************************************************************/
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <string.h>
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_stream.h>
33 #include <vlc_block.h>
35 #define MAX_TAGS 16
36 #define MAX_TAG_SIZE (1<<21)
38 struct skiptags_sys_t
40 uint64_t header_skip;
41 /* TODO? also discard trailer tags? */
42 block_t *p_tags;
45 static struct skiptags_sys_t * skiptags_sys_New(void)
47 struct skiptags_sys_t *sys = malloc(sizeof (*sys));
48 if(sys)
50 sys->header_skip = 0;
51 sys->p_tags = NULL;
53 return sys;
56 static void skiptags_sys_Delete(struct skiptags_sys_t *sys)
58 block_ChainRelease(sys->p_tags);
59 free(sys);
62 static uint_fast32_t SkipID3Tag(stream_t *s)
64 const uint8_t *peek;
66 /* Get 10-bytes ID3 header */
67 if (vlc_stream_Peek(s, &peek, 10) < 10)
68 return 0;
69 if (memcmp(peek, "ID3", 3))
70 return 0;
72 uint_fast8_t version = peek[3];
73 uint_fast8_t revision = peek[4];
74 bool has_footer = (peek[5] & 0x10) != 0;
75 uint_fast32_t size = 10u + (peek[6] << 21) + (peek[7] << 14)
76 + (peek[8] << 7) + peek[9] + (has_footer ? 10u : 0u);
78 /* Skip the entire tag */
79 msg_Dbg(s, "ID3v2.%"PRIuFAST8" revision %"PRIuFAST8" tag found, "
80 "skipping %"PRIuFAST32" bytes", version, revision, size);
82 return size;
85 static uint_fast32_t SkipAPETag(stream_t *s)
87 const uint8_t *peek;
89 /* Get 32-bytes APE header */
90 if (vlc_stream_Peek(s, &peek, 32) < 32)
91 return 0;
92 if (memcmp(peek, "APETAGEX", 8))
93 return 0;
95 uint_fast32_t version = GetDWLE(peek + 8);
96 if (version != 1000 && version != 2000)
97 return 0;
99 uint_fast32_t size = GetDWLE(peek + 12);
100 if (size > SSIZE_MAX - 32u)
101 return 0; /* impossibly long tag */
103 uint_fast32_t flags = GetDWLE(peek + 16);
104 if ((flags & (1u << 29)) == 0)
105 return 0;
107 if (flags & (1u << 30))
108 size += 32;
110 msg_Dbg(s, "AP2 v%"PRIuFAST32" tag found, "
111 "skipping %"PRIuFAST32" bytes", version / 1000, size);
112 return size;
115 static bool SkipTag(stream_t *s, uint_fast32_t (*skipper)(stream_t *),
116 block_t **pp_block, unsigned *pi_tags_count)
118 uint_fast64_t offset = vlc_stream_Tell(s);
119 uint_fast32_t size = skipper(s);
120 if(size> 0)
122 /* Skip the entire tag */
123 ssize_t read;
124 if(*pi_tags_count < MAX_TAGS && size <= MAX_TAG_SIZE)
126 *pp_block = vlc_stream_Block(s, size);
127 read = *pp_block ? (ssize_t)(*pp_block)->i_buffer : -1;
129 else
131 read = vlc_stream_Read(s, NULL, size);
134 if(read < (ssize_t)size)
136 block_ChainRelease(*pp_block);
137 *pp_block = NULL;
138 if (unlikely(read < 0))
139 { /* I/O error, try to restore offset. If it fails, screwed. */
140 if (vlc_stream_Seek(s, offset))
141 msg_Err(s, "seek failure");
142 return false;
145 else (*pi_tags_count)++;
147 return size != 0;
150 static ssize_t Read(stream_t *stream, void *buf, size_t buflen)
152 return vlc_stream_Read(stream->s, buf, buflen);
155 static int Seek(stream_t *stream, uint64_t offset)
157 const struct skiptags_sys_t *sys = stream->p_sys;
159 if (unlikely(offset + sys->header_skip < offset))
160 return -1;
161 return vlc_stream_Seek(stream->s, sys->header_skip + offset);
164 static int Control(stream_t *stream, int query, va_list args)
166 const struct skiptags_sys_t *sys = stream->p_sys;
168 /* In principles, we should return the meta-data embedded in the skipped
169 * tags in STREAM_GET_META. But the meta engine is devoted to that already.
171 switch (query)
173 case STREAM_GET_TAGS:
174 if (sys->p_tags == NULL)
175 break;
176 *va_arg(args, const block_t **) = sys->p_tags;
177 return VLC_SUCCESS;
179 case STREAM_GET_SIZE:
181 uint64_t size;
182 int ret = vlc_stream_GetSize(stream->s, &size);
183 if (ret == VLC_SUCCESS)
184 *va_arg(args, uint64_t *) = size - sys->header_skip;
185 return ret;
189 return vlc_stream_vaControl(stream->s, query, args);
192 static int Open(vlc_object_t *obj)
194 stream_t *stream = (stream_t *)obj;
195 stream_t *s = stream->s;
196 struct skiptags_sys_t *sys;
198 block_t *p_tags = NULL, *p_tag = NULL;
199 unsigned i_tagscount = 0;
201 while (SkipTag(s, SkipID3Tag, &p_tag, &i_tagscount)||
202 SkipTag(s, SkipAPETag, &p_tag, &i_tagscount))
204 if(p_tag)
206 p_tag->p_next = p_tags;
207 p_tags = p_tag;
208 p_tag = NULL;
212 uint_fast64_t offset = vlc_stream_Tell(s);
213 if (offset == 0 || !(sys = skiptags_sys_New()))
215 block_ChainRelease( p_tags );
216 return VLC_EGENERIC; /* nothing to do */
219 sys->header_skip = offset;
220 sys->p_tags = p_tags;
221 stream->p_sys = sys;
222 stream->pf_read = Read;
223 stream->pf_seek = Seek;
224 stream->pf_control = Control;
225 return VLC_SUCCESS;
228 static void Close(vlc_object_t *obj)
230 stream_t *stream = (stream_t *)obj;
231 struct skiptags_sys_t *sys = (struct skiptags_sys_t *) stream->p_sys;
233 skiptags_sys_Delete(sys);
236 vlc_module_begin()
237 set_category(CAT_INPUT)
238 set_subcategory(SUBCAT_INPUT_STREAM_FILTER)
239 set_capability("stream_filter", 330)
241 set_description(N_("APE/ID3 tags-skipping filter"))
242 set_callbacks(Open, Close)
243 vlc_module_end()