1 /*****************************************************************************
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 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_stream.h>
33 #include <vlc_block.h>
36 #define MAX_TAG_SIZE (1<<17)
41 /* TODO? also discard trailer tags? */
45 static struct skiptags_sys_t
* skiptags_sys_New(void)
47 struct skiptags_sys_t
*sys
= malloc(sizeof (*sys
));
56 static void skiptags_sys_Delete(struct skiptags_sys_t
*sys
)
58 block_ChainRelease(sys
->p_tags
);
62 static uint_fast32_t SkipID3Tag(stream_t
*s
)
66 /* Get 10-bytes ID3 header */
67 if (vlc_stream_Peek(s
, &peek
, 10) < 10)
69 if (memcmp(peek
, "ID3", 3))
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
);
85 static uint_fast32_t SkipAPETag(stream_t
*s
)
89 /* Get 32-bytes APE header */
90 if (vlc_stream_Peek(s
, &peek
, 32) < 32)
92 if (memcmp(peek
, "APETAGEX", 8))
95 uint_fast32_t version
= GetDWLE(peek
+ 8);
96 if (version
!= 1000 && version
!= 2000)
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)
107 if (flags
& (1u << 30))
110 msg_Dbg(s
, "AP2 v%"PRIuFAST32
" tag found, "
111 "skipping %"PRIuFAST32
" bytes", version
/ 1000, 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
);
122 /* Skip the entire tag */
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;
131 read
= vlc_stream_Read(s
, NULL
, size
);
134 if(read
< (ssize_t
)size
)
136 block_ChainRelease(*pp_block
);
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");
145 else (*pi_tags_count
)++;
150 static ssize_t
Read(stream_t
*stream
, void *buf
, size_t buflen
)
152 return vlc_stream_Read(stream
->p_source
, buf
, buflen
);
155 static int ReadDir(stream_t
*stream
, input_item_node_t
*node
)
157 (void) stream
; (void) node
;
161 static int Seek(stream_t
*stream
, uint64_t offset
)
163 const struct skiptags_sys_t
*sys
= stream
->p_sys
;
165 if (unlikely(offset
+ sys
->header_skip
< offset
))
167 return vlc_stream_Seek(stream
->p_source
, sys
->header_skip
+ offset
);
170 static int Control(stream_t
*stream
, int query
, va_list args
)
172 const struct skiptags_sys_t
*sys
= stream
->p_sys
;
173 /* In principles, we should return the meta-data embedded in the skipped
174 * tags in STREAM_GET_META. But the meta engine is devoted to that already.
176 if( query
== STREAM_GET_TAGS
&& sys
->p_tags
)
178 *va_arg( args
, const block_t
** ) = sys
->p_tags
;
182 return vlc_stream_vaControl(stream
->p_source
, query
, args
);
185 static int Open(vlc_object_t
*obj
)
187 stream_t
*stream
= (stream_t
*)obj
;
188 stream_t
*s
= stream
->p_source
;
189 struct skiptags_sys_t
*sys
;
191 block_t
*p_tags
= NULL
, *p_tag
= NULL
;
192 unsigned i_tagscount
= 0;
194 while (SkipTag(s
, SkipID3Tag
, &p_tag
, &i_tagscount
)||
195 SkipTag(s
, SkipAPETag
, &p_tag
, &i_tagscount
))
199 p_tag
->p_next
= p_tags
;
205 uint_fast64_t offset
= vlc_stream_Tell(s
);
206 if (offset
== 0 || !(sys
= skiptags_sys_New()))
208 block_ChainRelease( p_tags
);
209 return VLC_EGENERIC
; /* nothing to do */
212 sys
->header_skip
= offset
;
213 sys
->p_tags
= p_tags
;
215 stream
->pf_read
= Read
;
216 stream
->pf_readdir
= ReadDir
;
217 stream
->pf_seek
= Seek
;
218 stream
->pf_control
= Control
;
222 static void Close(vlc_object_t
*obj
)
224 stream_t
*stream
= (stream_t
*)obj
;
225 struct skiptags_sys_t
*sys
= (struct skiptags_sys_t
*) stream
->p_sys
;
227 skiptags_sys_Delete(sys
);
231 set_category(CAT_INPUT
)
232 set_subcategory(SUBCAT_INPUT_STREAM_FILTER
)
233 set_capability("stream_filter", 30)
235 set_description(N_("APE/ID3 tags-skipping filter"))
236 set_callbacks(Open
, Close
)