Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /* This file is part of Strigi Desktop Search |
---|---|
2 | * |
3 | * Copyright (C) 2009 Evgeny Egorochkin <phreedom.stdin@gmail.com> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public License |
16 | * along with this library; see the file COPYING.LIB. If not, write to |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #define STRIGI_IMPORT_API |
22 | #include <strigi/analyzerplugin.h> |
23 | #include <strigi/streamendanalyzer.h> |
24 | #include <strigi/analysisresult.h> |
25 | #include <strigi/fieldtypes.h> |
26 | #include <strigi/textutils.h> |
27 | #include <strigi/rdfnamespaces.h> |
28 | #include <xine.h> |
29 | #include <cstring> |
30 | #include <iostream> |
31 | using namespace Strigi; |
32 | using namespace std; |
33 | |
34 | class XineEndAnalyzerFactory; |
35 | |
36 | class STRIGI_PLUGIN_API XineEndAnalyzer : public StreamEndAnalyzer { |
37 | private: |
38 | AnalysisResult* result; |
39 | const XineEndAnalyzerFactory* factory; |
40 | xine_t *engine; |
41 | xine_audio_port_t *audiodrv; |
42 | xine_video_port_t *videodrv; |
43 | |
44 | public: |
45 | XineEndAnalyzer(const XineEndAnalyzerFactory* f) :factory(f) { |
46 | if ((engine = xine_new())) { |
47 | xine_init(engine); |
48 | |
49 | audiodrv = xine_open_audio_driver(engine, NULL, NULL); |
50 | videodrv = xine_open_video_driver(engine, NULL, XINE_VISUAL_TYPE_NONE, NULL); |
51 | |
52 | if (!audiodrv || !videodrv) |
53 | xine_exit (engine); |
54 | } |
55 | } |
56 | |
57 | ~XineEndAnalyzer() { |
58 | if (engine) { |
59 | if (audiodrv) |
60 | xine_close_audio_driver(engine, audiodrv); |
61 | if (videodrv) |
62 | xine_close_video_driver(engine, videodrv); |
63 | xine_exit(engine); |
64 | } |
65 | } |
66 | |
67 | const char* name() const { |
68 | return "XineEndAnalyzer"; |
69 | } |
70 | bool checkHeader(const char* header, int32_t headersize) const; |
71 | signed char analyze(AnalysisResult& idx, ::InputStream* in); |
72 | }; |
73 | |
74 | class STRIGI_PLUGIN_API XineEndAnalyzerFactory : public StreamEndAnalyzerFactory { |
75 | friend class XineEndAnalyzer; |
76 | private: |
77 | StreamEndAnalyzer* newInstance() const { |
78 | return new XineEndAnalyzer(this); |
79 | } |
80 | const char* name() const { |
81 | return "XineEndAnalyzer"; |
82 | } |
83 | void registerFields(FieldRegister& ); |
84 | |
85 | const RegisteredField* durationProperty; |
86 | const RegisteredField* widthProperty; |
87 | const RegisteredField* heightProperty; |
88 | const RegisteredField* frameRateProperty; |
89 | const RegisteredField* codecProperty; |
90 | const RegisteredField* bitrateProperty; |
91 | const RegisteredField* channelsProperty; |
92 | const RegisteredField* samplerateProperty; |
93 | const RegisteredField* titleProperty; |
94 | const RegisteredField* commentProperty; |
95 | const RegisteredField* typeProperty; |
96 | }; |
97 | |
98 | const string |
99 | videoClassName = |
100 | NFO "Video", |
101 | audioClassName = |
102 | NFO "Audio", |
103 | musicPieceClassName = |
104 | NMM_DRAFT "MusicPiece", |
105 | |
106 | titlePropertyName = |
107 | NIE "title", |
108 | commentPropertyName = |
109 | NIE "comment", |
110 | |
111 | sampleratePropertyName = |
112 | NFO "sampleRate", |
113 | codecPropertyName = |
114 | NFO "codec", |
115 | channelsPropertyName = |
116 | NFO "channels", |
117 | bitratePropertyName = |
118 | NFO "averageBitrate", |
119 | durationPropertyName = |
120 | NFO "duration", |
121 | widthPropertyName = |
122 | NFO "width", |
123 | heightPropertyName = |
124 | NFO "height", |
125 | frameRatePropertyName = |
126 | NFO "frameRate"; |
127 | |
128 | void |
129 | XineEndAnalyzerFactory::registerFields(FieldRegister& r) { |
130 | durationProperty = r.registerField(durationPropertyName); |
131 | widthProperty = r.registerField(widthPropertyName); |
132 | heightProperty = r.registerField(heightPropertyName); |
133 | frameRateProperty = r.registerField(frameRatePropertyName); |
134 | codecProperty = r.registerField(codecPropertyName); |
135 | bitrateProperty = r.registerField(bitratePropertyName); |
136 | typeProperty = r.typeField; |
137 | channelsProperty = r.registerField(channelsPropertyName); |
138 | samplerateProperty = r.registerField(sampleratePropertyName); |
139 | titleProperty = r.registerField(titlePropertyName); |
140 | commentProperty = r.registerField(commentPropertyName); |
141 | } |
142 | |
143 | //Have to detect here all supported formats because there's no way to pass this to xine |
144 | bool |
145 | XineEndAnalyzer::checkHeader(const char* header, int32_t headersize) const { |
146 | return headersize>=12 && ( |
147 | !strncmp(header, "FLV", 3) // FLV |
148 | || readLittleEndianUInt32(header) == 0x75b22630 // ASF/WMA/WMV |
149 | || (!strncmp(header, "RIFF", 4) && !strncmp(header+8, "AVI ", 4)) // AVI |
150 | || readLittleEndianUInt32(header) == 0xa3df451a // Matroska/MKV/MKA |
151 | || !strncmp(header+4, "ftyp3gp", 7) // 3GPP |
152 | || !strncmp(header+4, "ftypisom", 8) || !strncmp(header+4, "ftypmp42", 8) // MOV |
153 | || !strncmp(header+4, "ftypMSNV", 8) || !strncmp(header+4, "ftypM4", 6) // MOV |
154 | || (!strncmp(header, "OggS", 4) && strncmp(header+29, "vorbis", 6)) // Any ogg apart from Vorbis, which is handled by an internal analyzer |
155 | || readLittleEndianUInt32(header) == 0x10ff3f47 // MPG |
156 | || readLittleEndianUInt32(header) == 0xb3010000 // MPG |
157 | || readLittleEndianUInt32(header) == 0xba010000 // MPG |
158 | ); |
159 | } |
160 | |
161 | /* |
162 | Left unused: |
163 | XINE_STREAM_INFO_SEEKABLE |
164 | XINE_STREAM_INFO_VIDEO_RATIO |
165 | XINE_STREAM_INFO_VIDEO_CHANNELS |
166 | XINE_STREAM_INFO_VIDEO_STREAMS |
167 | XINE_STREAM_INFO_VIDEO_FOURCC |
168 | XINE_STREAM_INFO_VIDEO_HANDLED |
169 | XINE_STREAM_INFO_AUDIO_BITS |
170 | XINE_STREAM_INFO_AUDIO_FOURCC |
171 | XINE_STREAM_INFO_AUDIO_HANDLED |
172 | XINE_STREAM_INFO_HAS_CHAPTERS |
173 | XINE_STREAM_INFO_IGNORE_VIDEO |
174 | XINE_STREAM_INFO_IGNORE_AUDIO |
175 | XINE_STREAM_INFO_IGNORE_SPU |
176 | XINE_STREAM_INFO_VIDEO_HAS_STILL |
177 | XINE_STREAM_INFO_MAX_AUDIO_CHANNEL |
178 | XINE_STREAM_INFO_MAX_SPU_CHANNEL |
179 | XINE_STREAM_INFO_AUDIO_MODE |
180 | XINE_STREAM_INFO_SKIPPED_FRAMES |
181 | XINE_STREAM_INFO_DISCARDED_FRAMES |
182 | XINE_STREAM_INFO_VIDEO_AFD |
183 | XINE_STREAM_INFO_DVD_TITLE_NUMBER |
184 | XINE_STREAM_INFO_DVD_TITLE_COUNT |
185 | XINE_STREAM_INFO_DVD_CHAPTER_NUMBER |
186 | XINE_STREAM_INFO_DVD_CHAPTER_COUNT |
187 | XINE_STREAM_INFO_DVD_ANGLE_NUMBER |
188 | XINE_STREAM_INFO_DVD_ANGLE_COUNT |
189 | |
190 | XINE_META_INFO_ARTIST |
191 | XINE_META_INFO_GENRE |
192 | XINE_META_INFO_ALBUM |
193 | XINE_META_INFO_YEAR |
194 | XINE_META_INFO_SYSTEMLAYER |
195 | XINE_META_INFO_INPUT_PLUGIN |
196 | XINE_META_INFO_CDINDEX_DISCID |
197 | XINE_META_INFO_TRACK_NUMBER |
198 | */ |
199 | |
200 | signed char |
201 | XineEndAnalyzer::analyze(AnalysisResult& ar, ::InputStream* in) { |
202 | |
203 | xine_stream_t *stream; |
204 | |
205 | int posstream, postime, lengthtime; |
206 | |
207 | string filename; |
208 | |
209 | if ((ar.depth()==0) && (ar.path().substr(0,7) == "file://")) |
210 | filename = ar.path().substr(7); |
211 | else |
212 | filename = ar.path(); |
213 | |
214 | if (!(stream = xine_stream_new(engine, audiodrv, videodrv))) |
215 | return -1; |
216 | |
217 | if (!xine_open(stream, filename.c_str())) { |
218 | xine_dispose(stream); |
219 | return 0; |
220 | } |
221 | |
222 | bool audio = xine_get_stream_info(stream, XINE_STREAM_INFO_HAS_AUDIO); |
223 | |
224 | if (xine_get_pos_length(stream, &posstream, &postime, &lengthtime)) { |
225 | if (lengthtime > 0) |
226 | ar.addValue(factory->durationProperty, lengthtime / 1000); //duration in seconds |
227 | } |
228 | |
229 | int bitrate = xine_get_stream_info (stream, XINE_STREAM_INFO_BITRATE); |
230 | if (bitrate > 0) |
231 | ar.addValue(factory->bitrateProperty, bitrate); |
232 | |
233 | // video properties |
234 | |
235 | if ( xine_get_stream_info(stream, XINE_STREAM_INFO_HAS_VIDEO) ) { |
236 | ar.addValue(factory->typeProperty, videoClassName); |
237 | |
238 | int width = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_WIDTH); |
239 | int height = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_HEIGHT); |
240 | if (width > 0 && height > 0) { |
241 | ar.addValue(factory->widthProperty, width); |
242 | ar.addValue(factory->heightProperty, height); |
243 | } |
244 | |
245 | int duration = xine_get_stream_info(stream, XINE_STREAM_INFO_FRAME_DURATION); |
246 | if ( duration > 0) |
247 | ar.addValue(factory->frameRateProperty, 90000 / duration); |
248 | |
249 | const char *videocodec = xine_get_meta_info(stream, XINE_META_INFO_VIDEOCODEC); |
250 | if (videocodec) |
251 | ar.addValue(factory->codecProperty, videocodec, strlen(videocodec)); |
252 | |
253 | // Somehow bitrate always ends up being 0 :( |
254 | int bitrate = xine_get_stream_info (stream, XINE_STREAM_INFO_VIDEO_BITRATE); |
255 | if (bitrate > 0) |
256 | ar.addValue(factory->bitrateProperty, bitrate); |
257 | } else if (audio) { |
258 | ar.addValue(factory->typeProperty, musicPieceClassName); |
259 | ar.addValue(factory->typeProperty, audioClassName); |
260 | } |
261 | |
262 | // audio properties |
263 | |
264 | if (audio) { |
265 | // Somehow bitrate always ends up being 0 :( |
266 | int bitrate = xine_get_stream_info (stream, XINE_STREAM_INFO_AUDIO_BITRATE); |
267 | if (bitrate > 0) |
268 | ar.addValue(factory->bitrateProperty, bitrate); |
269 | |
270 | const char *audiocodec = xine_get_meta_info(stream, XINE_META_INFO_AUDIOCODEC); |
271 | if (audiocodec) |
272 | ar.addValue(factory->codecProperty, audiocodec, strlen(audiocodec)); |
273 | |
274 | int channels = xine_get_stream_info(stream, XINE_STREAM_INFO_AUDIO_CHANNELS); |
275 | if (channels>0) |
276 | ar.addValue(factory->channelsProperty, channels); |
277 | |
278 | int samplerate = xine_get_stream_info(stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE); |
279 | if (samplerate>0) |
280 | ar.addValue(factory->samplerateProperty, samplerate); |
281 | } |
282 | |
283 | // tags |
284 | |
285 | const char *title = xine_get_meta_info(stream, XINE_META_INFO_TITLE); |
286 | if (title) |
287 | ar.addValue(factory->titleProperty, title); |
288 | |
289 | const char *comment = xine_get_meta_info(stream, XINE_META_INFO_COMMENT); |
290 | if (comment) |
291 | ar.addValue(factory->commentProperty, comment); |
292 | |
293 | xine_dispose(stream); |
294 | return 0; |
295 | } |
296 | |
297 | /* |
298 | For plugins, we need to have a way to find out which plugins are defined in a |
299 | plugin. One instance of AnalyzerFactoryFactory per plugin profides this |
300 | information. |
301 | */ |
302 | class Factory : public AnalyzerFactoryFactory { |
303 | public: |
304 | list<StreamEndAnalyzerFactory*> |
305 | streamEndAnalyzerFactories() const { |
306 | list<StreamEndAnalyzerFactory*> af; |
307 | af.push_back(new XineEndAnalyzerFactory()); |
308 | return af; |
309 | } |
310 | }; |
311 | |
312 | /* |
313 | Register the AnalyzerFactoryFactory |
314 | */ |
315 | STRIGI_ANALYZER_FACTORY(Factory) |
316 |
Warning: That file was not part of the compilation database. It may have many parsing errors.