Commit ee72a78658c8a559a7ed0c7f4ee9274b77d27929

sync with strigi svn trunk revision 1092572
  
1616set(LIBSTREAMANALYZER_SOVERSION 0.7)
1717option(BUILD_UTILS "build luceneindexer, xmlindexer, rdfindexer, ontoprint utilities" ON)
1818option(BUILD_DEEPTOOLS "build deep find and deepgrep tools" ON)
19option(ENABLE_XINE
20 "enable Xine support. This adds support for a larger number of media formats(highly unstable)." OFF)
21option(ENABLE_FFMPEG
22 "enable FFMPEG support. This adds support for a larger number of media formats(testing)." ON)
1923option(ENABLE_EXIV2
2024 "enable exiv2 support. This allows you to index EXIF/IPTC metadata." ON)
2125set(CLUCENE_MIN_VERSION "0.9.21")
  
1# - Try to find FFMPEG
2# Once done this will define
3#
4# FFMPEG_FOUND - system has FFMPEG
5# FFMPEG_INCLUDE_DIRS - the FFMPEG include directory
6# FFMPEG_LIBRARIES - Link these to use FFMPEG
7# FFMPEG_DEFINITIONS - Compiler switches required for using FFMPEG
8#
9# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
10#
11# Redistribution and use is allowed according to the terms of the New
12# BSD license.
13# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
14#
15
16
17if (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIRS)
18 # in cache already
19 set(FFMPEG_FOUND TRUE)
20else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIRS)
21 # use pkg-config to get the directories and then use these values
22 # in the FIND_PATH() and FIND_LIBRARY() calls
23 if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
24 include(UsePkgConfig)
25 pkgconfig(libavcodec _AVCODEC_INCLUDEDIR _AVCODEC_LIBDIR _AVCODEC_LDFLAGS _AVCODEC_CFLAGS)
26 pkgconfig(libavformat _AVFORMAT_INCLUDEDIR _AVFORMAT_LIBDIR _AVFORMAT_LDFLAGS _AVFORMAT_CFLAGS)
27 pkgconfig(libavutil _AVUTIL_INCLUDEDIR _AVUTIL_LIBDIR _AVUTIL_LDFLAGS _AVUTIL_CFLAGS)
28# pkgconfig(libpostproc _POSTPROC_INCLUDEDIR _POSTPROC_LIBDIR _POSTPROC_LDFLAGS _POSTPROC_CFLAGS)
29 pkgconfig(libswscale _SWSCALE_INCLUDEDIR _SWSCALE_LIBDIR _SWSCALE_LDFLAGS _SWSCALE_CFLAGS)
30 else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
31 message("Doing find_package(PkgConfig)")
32 find_package(PkgConfig)
33 if (PKG_CONFIG_FOUND)
34 pkg_check_modules(_AVCODEC libavcodec)
35 pkg_check_modules(_AVFORMAT libavformat)
36 pkg_check_modules(_AVUTIL libavutil)
37# pkg_check_modules(_POSTPROC libpostproc)
38 pkg_check_modules(_SWSCALE libswscale)
39 endif (PKG_CONFIG_FOUND)
40 endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4)
41
42 find_path(AVCODEC_INCLUDE_DIR
43 NAMES
44 avcodec.h
45 PATHS
46 ${_AVCODEC_INCLUDEDIR}
47 /usr/include
48 /usr/local/include
49 /opt/local/include
50 /sw/include
51 PATH_SUFFIXES
52 libavcodec
53 ffmpeg
54 NO_DEFAULT_PATH
55 )
56
57 message("AVCODEC_INCLUDE_DIR = ${AVCODEC_INCLUDE_DIR}")
58
59 mark_as_advanced(AVCODEC_INCLUDE_DIR)
60
61 find_path(AVUTIL_INCLUDE_DIR
62 NAMES
63 avutil.h
64 PATHS
65 ${_AVUTIL_INCLUDEDIR}
66 /usr/include
67 /usr/local/include
68 /opt/local/include
69 /sw/include
70 PATH_SUFFIXES
71 libavutil
72 ffmpeg
73 NO_DEFAULT_PATH
74 )
75 mark_as_advanced(AVUTIL_INCLUDE_DIR)
76
77 find_path(AVFORMAT_INCLUDE_DIR
78 NAMES
79 avformat.h
80 PATHS
81 ${_AVFORMAT_INCLUDEDIR}
82 /usr/include
83 /usr/local/include
84 /opt/local/include
85 /sw/include
86 PATH_SUFFIXES
87 libavformat
88 ffmpeg
89 NO_DEFAULT_PATH
90 )
91 mark_as_advanced(AVFORMAT_INCLUDE_DIR)
92
93 if (FALSE)
94 find_path(POSTPROC_INCLUDE_DIR
95 NAMES
96 postprocess.h
97 PATHS
98 ${_POSTPROC_INCLUDEDIR}
99 /usr/include/postproc
100 /usr/local/include/postproc
101 /opt/local/include/postproc
102 /sw/include
103 PATH_SUFFIXES
104 libpostproc
105 ffmpeg
106 )
107 mark_as_advanced(POSTPROC_INCLUDE_DIR)
108 endif(FALSE)
109
110 find_path(SWSCALE_INCLUDE_DIR
111 NAMES
112 swscale.h
113 PATHS
114 ${_SWSCALE_INCLUDEDIR}
115 /usr/include
116 /usr/local/include
117 /opt/local/include
118 /sw/include
119 PATH_SUFFIXES
120 libswscale
121 ffmpeg
122 NO_DEFAULT_PATH
123 )
124 mark_as_advanced(SWSCALE_INCLUDE_DIR)
125
126 find_library(AVCODEC_LIBRARY
127 NAMES
128 avcodec
129 PATHS
130 ${_FFMPEG_LIBDIR}
131 /usr/lib
132 /usr/local/lib
133 /opt/local/lib
134 /sw/lib
135 NO_DEFAULT_PATH
136 )
137 mark_as_advanced(AVCODEC_LIBRARY)
138
139 find_library(AVUTIL_LIBRARY
140 NAMES
141 avutil
142 PATHS
143 ${_FFMPEG_LIBDIR}
144 /usr/lib
145 /usr/local/lib
146 /opt/local/lib
147 /sw/lib
148 NO_DEFAULT_PATH
149 )
150 mark_as_advanced(AVUTIL_LIBRARY)
151
152 find_library(AVFORMAT_LIBRARY
153 NAMES
154 avformat
155 PATHS
156 ${_FFMPEG_LIBDIR}
157 /usr/lib
158 /usr/local/lib
159 /opt/local/lib
160 /sw/lib
161 NO_DEFAULT_PATH
162 )
163 mark_as_advanced(AVFORMAT_LIBRARY)
164
165 if (FALSE)
166 find_library(POSTPROC_LIBRARY
167 NAMES
168 postproc
169 PATHS
170 ${_FFMPEG_LIBDIR}
171 /usr/lib
172 /usr/local/lib
173 /opt/local/lib
174 /sw/lib
175 NO_DEFAULT_PATH
176 )
177 mark_as_advanced(POSTPROC_LIBRARY)
178 endif(FALSE)
179
180 find_library(SWSCALE_LIBRARY
181 NAMES
182 swscale
183 PATHS
184 ${_FFMPEG_LIBDIR}
185 /usr/lib
186 /usr/local/lib
187 /opt/local/lib
188 /sw/lib
189 NO_DEFAULT_PATH
190 )
191 mark_as_advanced(SWSCALE_LIBRARY)
192
193 if (AVCODEC_LIBRARY)
194 set(AVCODEC_FOUND TRUE)
195 endif (AVCODEC_LIBRARY)
196 if (AVUTIL_LIBRARY)
197 set(AVUTIL_FOUND TRUE)
198 endif (AVUTIL_LIBRARY)
199 if (AVFORMAT_LIBRARY)
200 set(AVFORMAT_FOUND TRUE)
201 endif (AVFORMAT_LIBRARY)
202
203 if (POSTPROC_LIBRARY)
204 set(POSTPROC_FOUND TRUE)
205 endif (POSTPROC_LIBRARY)
206 if (SWSCALE_LIBRARY)
207 set(SWSCALE_FOUND TRUE)
208 endif (SWSCALE_LIBRARY)
209
210 set(FFMPEG_INCLUDE_DIRS
211 ${_AVCODEC_INCLUDEDIR}
212 ${AVCODEC_INCLUDE_DIR}
213 ${AVFORMAT_INCLUDE_DIR}
214 ${AVUTIL_INCLUDE_DIR}
215 ${POSTPROC_INCLUDE_DIR}
216 ${SWSCALE_INCLUDE_DIR}
217 )
218
219 if (AVCODEC_FOUND)
220 set(FFMPEG_LIBRARIES
221 ${FFMPEG_LIBRARIES}
222 ${AVCODEC_LIBRARY}
223 )
224 endif (AVCODEC_FOUND)
225 if (AVUTIL_FOUND)
226 set(FFMPEG_LIBRARIES
227 ${FFMPEG_LIBRARIES}
228 ${AVUTIL_LIBRARY}
229 )
230 endif (AVUTIL_FOUND)
231 if (AVFORMAT_FOUND)
232 set(FFMPEG_LIBRARIES
233 ${FFMPEG_LIBRARIES}
234 ${AVFORMAT_LIBRARY}
235 )
236 endif (AVFORMAT_FOUND)
237 if (POSTPROC_FOUND)
238 set(FFMPEG_LIBRARIES
239 ${FFMPEG_LIBRARIES}
240 ${POSTPROC_LIBRARY}
241 )
242 endif (POSTPROC_FOUND)
243 if (SWSCALE_FOUND)
244 set(FFMPEG_LIBRARIES
245 ${FFMPEG_LIBRARIES}
246 ${SWSCALE_LIBRARY}
247 )
248 endif (SWSCALE_FOUND)
249
250 if (FFMPEG_INCLUDE_DIRS AND FFMPEG_LIBRARIES)
251 set(FFMPEG_FOUND TRUE)
252 endif (FFMPEG_INCLUDE_DIRS AND FFMPEG_LIBRARIES)
253
254 if (FFMPEG_FOUND)
255 if (NOT FFMPEG_FIND_QUIETLY)
256 message(STATUS "Found FFMPEG: ${FFMPEG_LIBRARIES}")
257 endif (NOT FFMPEG_FIND_QUIETLY)
258 else (FFMPEG_FOUND)
259 if (FFMPEG_FIND_REQUIRED)
260 message(FATAL_ERROR "Could not find FFMPEG")
261 endif (FFMPEG_FIND_REQUIRED)
262 endif (FFMPEG_FOUND)
263
264 # show the FFMPEG_INCLUDE_DIRS and FFMPEG_LIBRARIES variables only in the advanced view
265 mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES)
266
267endif (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIRS)
  
1# - Try to find the XINE library
2# Once done this will define
3#
4# XINE_FOUND - system has the XINE library
5# XINE_VERSION - XINE version
6# XINE_BUGFIX_VERSION - the XINE bugfix version
7# XINE_INCLUDE_DIR - the XINE include directory
8# XINE_LIBRARY - The libraries needed to use XINE
9# XINE_XCB_FOUND - libxine can use XCB for video output
10
11# Copyright (c) 2008 Helio Chissini de Castro, <helio@kde.org>
12# Copyright (c) 2006,2007 Laurent Montel, <montel@kde.org>
13# Copyright (c) 2006, Matthias Kretz, <kretz@kde.org>
14#
15# Redistribution and use is allowed according to the terms of the BSD license.
16# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
17
18if (XINE_INCLUDE_DIR AND XINE_LIBRARY)
19 # Already in cache, be silent
20 set(Xine_FIND_QUIETLY TRUE)
21endif (XINE_INCLUDE_DIR AND XINE_LIBRARY)
22
23IF (NOT WIN32)
24 FIND_PACKAGE(PkgConfig)
25 PKG_CHECK_MODULES(PKG_XINE libxine)
26ENDIF (NOT WIN32)
27
28FIND_PATH(XINE_INCLUDE_DIR NAMES xine.h
29 PATHS ${PKG_XINE_INCLUDE_DIRS} )
30
31FIND_LIBRARY(XINE_LIBRARY NAMES xine
32 PATHS ${PKG_XINE_LIBRARY_DIRS} )
33
34if (XINE_INCLUDE_DIR AND XINE_LIBRARY)
35 set(XINE_FOUND TRUE)
36 string(REGEX REPLACE "[0-9].[0-9]." "" XINE_BUGFIX_VERSION ${PKG_XINE_VERSION})
37 set(XINE_VERSION ${PKG_XINE_VERSION})
38endif (XINE_INCLUDE_DIR AND XINE_LIBRARY)
39
40
41if( XINE_FOUND )
42 INCLUDE(CheckCSourceCompiles)
43 SET(CMAKE_REQUIRED_INCLUDES ${XINE_INCLUDE_DIR})
44 SET(CMAKE_REQUIRED_LIBRARIES ${XINE_LIBRARY})
45 CHECK_C_SOURCE_COMPILES("#include <xine.h>\nint main()\n{\n xine_open_video_driver(xine_new(), \"auto\", XINE_VISUAL_TYPE_XCB, NULL);\n return 0;\n}\n" XINE_XCB_FOUND)
46endif(XINE_FOUND)
47
48if (XINE_FOUND)
49 if (NOT Xine_FIND_QUIETLY)
50 message(STATUS "Found XINE: ${XINE_LIBRARY}")
51 endif (NOT Xine_FIND_QUIETLY)
52endif (XINE_FOUND)
53
54MARK_AS_ADVANCED(XINE_INCLUDE_DIR XINE_LIBRARY)
  
9393 AnalysisResult analysisresult(path, mtime, *manager.indexWriter(),
9494 analyzer, "");
9595 if (realfile) {
96 FileInputStream file(path.c_str());
97 return analysisresult.index(&file);
96 InputStream* file = FileInputStream::open(path.c_str());
97 int r = analysisresult.index(file);
98 delete file;
99 return r;
98100 } else {
99101 return analysisresult.index(0);
100102 }
119119 AnalysisResult analysisresult(filepath, s.st_mtime,
120120 indexWriter, *analyzer, parentpath);
121121 if (S_ISREG(s.st_mode)) {
122 FileInputStream file(filepath.c_str());
123 analysisresult.index(&file);
122 InputStream* file = FileInputStream::open(filepath.c_str());
123 analysisresult.index(file);
124 delete file;
124125 } else {
125126 analysisresult.index(0);
126127 }
194194 AnalysisResult analysisresult(i->first, i->second.st_mtime,
195195 *manager.indexWriter(), *analyzer, path);
196196 if (S_ISREG(i->second.st_mode)) {
197 FileInputStream file(i->first.c_str());
198 analysisresult.index(&file);
197 InputStream* file = FileInputStream::open(i->first.c_str());
198 analysisresult.index(file);
199 delete file;
199200 } else {
200201 analysisresult.index(0);
201202 }
  
3838 heightField = reg.registerField(
3939 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
4040 colorDepthField = reg.registerField(
41 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
41 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
4242 rdftypeField = reg.typeField;
4343
4444 addField(typeField);
  
5757 NMM_DRAFT "setNumber"),
5858 discCountPropertyName(
5959 NMM_DRAFT "setCount"),
60
60
6161 musicClassName(
6262 NMM_DRAFT "MusicPiece"),
6363 audioClassName(
279279
280280 ICONV_CONST char *input = (char *)data;
281281 iconv(conv, &input, &len, &result, &reslen);
282
282
283283 return string(out,capacity-reslen);
284284}
285285
302302 trackNumberField = r.registerField(NMM_DRAFT "trackNumber");
303303 durationField = r.registerField(NFO "duration");
304304 typeField = r.typeField;
305
305
306306 bitrateField = r.registerField(NFO "averageBitrate");
307307 samplerateField = r.registerField(NFO "sampleRate");
308308 codecField = r.registerField(NFO "codec");
321321int32_t readAsyncSize(const unsigned char* b) {
322322 return (((int32_t)b[0])<<21) + (((int32_t)b[1])<<14)
323323 + (((int32_t)b[2])<<7) + ((int32_t)b[3]);
324}
324}
325325
326326int32_t
327327readSize(const unsigned char* b, bool async) {
352352ID3EndAnalyzer::analyze(Strigi::AnalysisResult& indexable, Strigi::InputStream* in) {
353353 if(!in)
354354 return -1;
355
355
356356 bool found_title = false, found_artist = false,
357357 found_album = false, found_comment = false,
358358 found_year = false, found_track = false,
359359 found_genre = false, found_tag = false;
360360 string albumUri;
361361 char albumArtNum = '\0';
362
362
363363 // read 10 byte header
364364 const char* buf;
365365 int32_t nread = in->read(buf, 10, 10);
427427 if (enc == 0 || enc == 3) {
428428 value = string(p+11, strnlen(p+11, size-1));
429429 } else {
430 value = conv.convert(p+11,size-1); // FIXME: add similar workaround
430 value = conv.convert(p+11,size-1); // FIXME: add similar workaround
431431 }
432432
433433 if (!value.empty()) {
517517 ostringstream outs;
518518 outs << dcount;
519519 addStatement(indexable, albumUri, discCountPropertyName, outs.str());
520 }
520 }
521521 }
522522 }
523523 }
530530 if (((unsigned char)buf[0] == 0xff) && (((unsigned char)buf[1]&0xfe) == 0xfa)
531531 && ((bitrateindex = ((unsigned char)buf[2]>>4)) != 0xf)
532532 && ((samplerateindex = (((unsigned char)buf[2]>>2)&3)) != 3 )) { // is this MP3?
533
533
534534 indexable.addValue(factory->typeField, audioClassName);
535535 // FIXME: no support for VBR :(
536536 // ideas: compare bitrate from the frame with stream size/duration from ID3 tags
537537 // check several consecutive frames to see if bitrate is different
538538 // in neither case you can be sure to properly detected VBR :(
539 indexable.addValue(factory->bitrateField, bitrate[bitrateindex]);
539 indexable.addValue(factory->bitrateField, bitrate[bitrateindex]);
540540 indexable.addValue(factory->samplerateField, samplerate[samplerateindex]);
541541 indexable.addValue(factory->codecField, "MP3");
542542 indexable.addValue(factory->channelsField, ((buf[3]>>6) == 3 ? 1:2 ) );
543543 }
544
544
545545 // Parse ID3v1 tag
546546
547547 int64_t insize;
552552 if (nskip == in->skip(nskip))
553553 if (in->read(buf, 128, 128)==128)
554554 if (!strncmp("TAG", buf, 3)) {
555
555
556556 found_tag = true;
557
557
558558 if (!found_title && buf[3])
559559 indexable.addValue(factory->titleField, string(buf+3, strnlen(buf+3, 30)));
560560 if (!found_artist && buf[33])
566566 if (!found_comment && buf[97])
567567 indexable.addValue(factory->commentField, string(buf+97, strnlen(buf+97, 30)));
568568 if (!found_track && !buf[125] && buf[126]) {
569 ostringstream out;
570 out << (int)(buf[126]);
571 indexable.addValue(factory->trackNumberField, out.str());
569 indexable.addValue(factory->trackNumberField, (int)(buf[126]));
572570 }
573571 if (!found_genre && (unsigned char)(buf[127]) < 148)
574572 indexable.addValue(factory->genreField, genres[(uint8_t)buf[127]]);
  
3333
3434const string
3535 videoClassName(
36 NMM_DRAFT "Video");
36 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Video");
3737
3838void MpegEndAnalyzerFactory::registerFields(FieldRegister& r) {
3939 fields["length"] = r.registerField(
  
6666 heightField = reg.registerField(
6767 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
6868 colorDepthField = reg.registerField(
69 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
69 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
7070 colorModeField = reg.registerField(
7171 "http://freedesktop.org/standards/xesam/1.0/core#colorSpace");
7272 compressionField = reg.registerField(
337337 as.addValue(factory->titleField, value);
338338 } else if ("Author" == key) {
339339 string authorUri = as.newAnonymousUri();
340
340
341341 as.addValue(factory->authorField, authorUri);
342342 as.addTriplet(authorUri, typeFieldName, contactClassName);
343343 as.addTriplet(authorUri, fullnameFieldName, value);
  
202202 // ensure a decent buffer size
203203 string name;
204204 AnalysisResult analysisresult(filepath, s.st_mtime, *p->writer, *this);
205 FileInputStream file(filepath.c_str());
206 if (file.status() == Ok) {
207 return analysisresult.index(&file);
205 InputStream* file = FileInputStream::open(filepath.c_str());
206 signed char r;
207 if (file->status() == Ok) {
208 r = analysisresult.index(file);
208209 } else {
209 return analysisresult.index(0);
210 r = analysisresult.index(0);
210211 }
212 delete file;
213 return r;
211214}
212215void
213216StreamAnalyzerPrivate::addFactory(StreamThroughAnalyzerFactory* f) {
318318 addFactory(new TarEndAnalyzerFactory());
319319 addFactory(new ArEndAnalyzerFactory());
320320 addFactory(new MailEndAnalyzerFactory());
321 addFactory(new MpegEndAnalyzerFactory());
321// addFactory(new MpegEndAnalyzerFactory()); //Xine fallback works so much better now
322322 addFactory(new OdfEndAnalyzerFactory());
323323 addFactory(new ZipEndAnalyzerFactory());
324324 addFactory(new ZipExeEndAnalyzerFactory());
  
2727 ADD_STRIGIEA(jpeg jpegendanalyzer.cpp)
2828 target_link_libraries(jpeg ${EXIV2_LIBRARIES})
2929endif(EXIV2_FOUND)
30
31if(XINE_FOUND)
32 include_directories(${XINE_INCLUDE_DIR})
33 ADD_STRIGIEA(xine xineendanalyzer.cpp)
34 target_link_libraries(xine ${XINE_LIBRARY})
35endif(XINE_FOUND)
36
37if(FFMPEG_FOUND)
38 include_directories(${FFMPEG_INCLUDE_DIRS})
39 ADD_STRIGIEA(ffmpeg ffmpegendanalyzer.cpp)
40 #set_target_properties( ffmpeg PROPERTIES COMPILE_FLAGS "${FFMPEG_DEFINITIONS}" )
41 target_link_libraries(ffmpeg ${FFMPEG_LIBRARIES})
42endif(FFMPEG_FOUND)
  
1/* This file is part of Strigi Desktop Search
2 *
3 * Copyright (C) 2010 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>
28extern "C" {
29#include <libavcodec/avcodec.h>
30#include <libavformat/avformat.h>
31#include <libswscale/swscale.h>
32}
33#include <cstring>
34#include <iostream>
35#include <sstream>
36using namespace Strigi;
37using namespace std;
38
39class FFMPEGEndAnalyzerFactory;
40
41class STRIGI_PLUGIN_API FFMPEGEndAnalyzer : public StreamEndAnalyzer {
42private:
43 const FFMPEGEndAnalyzerFactory* factory;
44public:
45 FFMPEGEndAnalyzer(const FFMPEGEndAnalyzerFactory* f) :factory(f) {}
46
47 ~FFMPEGEndAnalyzer() {}
48
49 const char* name() const {
50 return "FFMPEGEndAnalyzer";
51 }
52 bool checkHeader(const char* header, int32_t headersize) const;
53 signed char analyze(AnalysisResult& idx, ::InputStream* in);
54};
55
56class STRIGI_PLUGIN_API FFMPEGEndAnalyzerFactory : public StreamEndAnalyzerFactory {
57friend class FFMPEGEndAnalyzer;
58private:
59 StreamEndAnalyzer* newInstance() const {
60 av_register_all();
61 return new FFMPEGEndAnalyzer(this);
62 }
63 const char* name() const {
64 return "FFMPEGEndAnalyzer";
65 }
66 void registerFields(FieldRegister& );
67
68 const RegisteredField* durationProperty;
69 const RegisteredField* widthProperty;
70 const RegisteredField* heightProperty;
71 const RegisteredField* frameRateProperty;
72 const RegisteredField* codecProperty;
73 const RegisteredField* bitrateProperty;
74 const RegisteredField* channelsProperty;
75 const RegisteredField* samplerateProperty;
76 const RegisteredField* titleProperty;
77 const RegisteredField* creatorProperty;
78 const RegisteredField* copyrightProperty;
79 const RegisteredField* commentProperty;
80 const RegisteredField* albumProperty;
81 const RegisteredField* genreProperty;
82 const RegisteredField* trackProperty;
83 const RegisteredField* createdProperty;
84 const RegisteredField* typeProperty;
85 const RegisteredField* hasPartProperty;
86};
87
88const string
89 videoClassName =
90 NFO "Video",
91 audioClassName =
92 NFO "Audio",
93 musicPieceClassName =
94 NMM_DRAFT "MusicPiece",
95 albumClassName =
96 NMM_DRAFT "MusicAlbum",
97 embeddedClassName =
98 NFO "EmbeddedFileDataObject",
99 contactClassName =
100 NCO "Contact",
101
102 typePropertyName =
103 RDF "type",
104 hasPartPropertyName =
105 NIE "hasPart",
106 partOfPropertyName =
107 NIE "isPartOf",
108
109 titlePropertyName =
110 NIE "title",
111 fullnamePropertyName =
112 NCO "fullname",
113 commentPropertyName =
114 NIE "comment",
115 languagePropertyName =
116 NIE "language",
117 genrePropertyName =
118 NMM_DRAFT "genre",
119 trackPropertyName =
120 NMM_DRAFT "trackNumber",
121 createdPropertyName =
122 NIE "contentCreated",
123 creatorPropertyName =
124 NCO "creator",
125 copyrightPropertyName =
126 NIE "copyright",
127 albumPropertyName =
128 NMM_DRAFT "MusicAlbum",
129
130 sampleratePropertyName =
131 NFO "sampleRate",
132 codecPropertyName =
133 NFO "codec",
134 channelsPropertyName =
135 NFO "channels",
136 bitratePropertyName =
137 NFO "averageBitrate",
138 durationPropertyName =
139 NFO "duration",
140 widthPropertyName =
141 NFO "width",
142 heightPropertyName =
143 NFO "height",
144 aspectRatioPropertyName =
145 NFO "aspectRatio",
146 frameRatePropertyName =
147 NFO "frameRate";
148
149void
150FFMPEGEndAnalyzerFactory::registerFields(FieldRegister& r) {
151 durationProperty = r.registerField(durationPropertyName);
152 widthProperty = r.registerField(widthPropertyName);
153 heightProperty = r.registerField(heightPropertyName);
154 frameRateProperty = r.registerField(frameRatePropertyName);
155 codecProperty = r.registerField(codecPropertyName);
156 bitrateProperty = r.registerField(bitratePropertyName);
157 typeProperty = r.typeField;
158 channelsProperty = r.registerField(channelsPropertyName);
159 samplerateProperty = r.registerField(sampleratePropertyName);
160 titleProperty = r.registerField(titlePropertyName);
161 creatorProperty = r.registerField(creatorPropertyName);
162 copyrightProperty = r.registerField(copyrightPropertyName);
163 commentProperty = r.registerField(commentPropertyName);
164 albumProperty = r.registerField(albumPropertyName);
165 genreProperty = r.registerField(genrePropertyName);
166 trackProperty = r.registerField(trackPropertyName);
167 createdProperty = r.registerField(createdPropertyName);
168 hasPartProperty = r.registerField(hasPartPropertyName);
169}
170
171// Probe all input formats and obtain score.
172// Evil FFMPEG hid av_probe_input_format2, the function that does just this.
173AVInputFormat *probe_format(AVProbeData *pd, int *max_score) {
174 AVInputFormat *result = NULL;
175 *max_score = 0;
176
177 for (AVInputFormat *fmt = av_iformat_next(NULL); fmt != NULL; fmt = av_iformat_next(fmt))
178 // test only formats that are file-based and can detect the byte stream
179 if (!(fmt->flags & AVFMT_NOFILE) && fmt->read_probe) {
180 int score = fmt->read_probe(pd);
181 if (score > *max_score) {
182 *max_score = score;
183 result = fmt;
184 }
185 }
186
187 return result;
188}
189
190// Input format is probed twice, but compared to the expense of stream metadata extraction this isn't a huge deal.
191// Unfortunately you can't save probe results in checkHeader because it's const
192bool
193FFMPEGEndAnalyzer::checkHeader(const char* header, int32_t headersize) const {
194 AVProbeData pd;
195 pd.buf = (unsigned char*)header;
196 pd.buf_size = headersize;
197 pd.filename ="";
198 int max_score;
199
200 probe_format(&pd, &max_score);
201
202 cout<<"Detection score:"<<max_score<<endl<<flush;
203 // Most of formats return either 100 or nothing
204 // MPG, however, can go as low as 25 while still being a real video
205 return max_score >=25;
206}
207
208/*FIXME
209make it produce the same data in stream and file mode when possible.
210stream duration,size and bitrate reporting seem to be mostly nonexistent in ffmpeg
211handle subtitles
212*/
213
214extern "C" {
215int read_data(void *opaque, uint8_t *buf, int buf_size) {
216 cout<<"READ";
217 InputStream *s = (InputStream *) opaque;
218 if (!s)
219 return -1;
220
221 const char *sbuf;
222 cout<<s->position()<<" "<<flush;
223 int32_t len = s->read(sbuf, buf_size, buf_size);
224 cout<<s->position()<<" "<<buf_size<<" "<<len<<" "<<s->size()<<flush;
225 if (len>0)
226 memcpy( buf, sbuf, len);
227 cout<<" OK\n"<<flush;
228 return len;
229}
230
231int64_t seek_data(void *opaque, int64_t offset, int whence) {
232 InputStream *s = (InputStream *) opaque;
233 int64_t target = -1;
234 int64_t size;
235
236 cout<<"SEEK"<<offset<<" "<<whence<<"\n"<<flush;
237
238 if ( whence== SEEK_SET) {
239 target = offset;
240 } else if ( whence == SEEK_CUR ) {
241 target = s->position() + offset;
242 } else if ( (whence == SEEK_END) && (size = s->size()>=0) ) {
243 target = size+offset;
244 } else if ( whence == AVSEEK_SIZE ) {
245 return s->size();
246 } else
247 return -1;
248
249 int64_t t= s->reset(target);
250 cout<<t<<"\n"<<flush;
251 return (t == target ? target : -1);
252}
253}
254
255int64_t const no_bitrate = 0x8000000000000000ULL;
256
257signed char
258FFMPEGEndAnalyzer::analyze(AnalysisResult& ar, ::InputStream* in) {
259 uint8_t pDataBuffer[32768];//65536];
260 long lSize = 32768;
261
262 ByteIOContext ByteIOCtx;
263 if(init_put_byte(&ByteIOCtx, pDataBuffer, lSize, 0, in, read_data, NULL, seek_data) < 0)
264 return -1;
265
266 //pAVInputFormat->flags |= AVFMT_NOFILE;
267 ByteIOCtx.is_streamed = 0;
268
269 AVProbeData pd;
270 const char *buf;
271 pd.filename ="";
272 pd.buf_size = in->read(buf,262144,262144);
273 pd.buf = (unsigned char*)buf;
274 in->reset(0);
275
276 int score;
277 AVInputFormat* fmt = probe_format(&pd, &score);
278
279 AVFormatContext *fc = NULL;
280 if(av_open_input_stream(&fc, &ByteIOCtx, "", fmt, NULL) < 0)
281 return -1;
282
283 av_find_stream_info(fc);
284
285 // Dump information about file onto standard error
286 dump_format(fc, 0, ar.path().c_str(), false);
287
288 if(fc->bit_rate)
289 ar.addValue(factory->bitrateProperty, fc->bit_rate);
290 else if (fc->duration!= no_bitrate) {
291 cout<<"Trying to estimate bitrate\n";
292 int64_t size;
293 if ((size = in->size()) >= 0)
294 ar.addValue(factory->bitrateProperty, (uint32_t)((size/(fc->duration/AV_TIME_BASE))*8) );
295 }
296 if(fc->duration!= no_bitrate)
297 ar.addValue(factory->durationProperty, (uint32_t)(fc->duration / AV_TIME_BASE));
298 else if(fc->bit_rate) {
299 cout<<"Trying to estimate duration\n";
300 int64_t size;
301 if ((size = in->size()) >= 0)
302 ar.addValue(factory->durationProperty, (uint32_t)(size/(fc->bit_rate/8)));
303 }
304 if(fc->nb_streams==1 && fc->streams[0]->codec->codec_type == CODEC_TYPE_AUDIO) {
305 ar.addValue(factory->typeProperty, NFO "Audio");
306 ar.addValue(factory->typeProperty, NMM_DRAFT "MusicPiece");
307 } else {
308 ar.addValue(factory->typeProperty, NFO "Video");
309 }
310
311 for(uint32_t i=0; i<fc->nb_streams; i++) {
312 const AVStream &stream = *fc->streams[i];
313 const AVCodecContext &codec = *stream.codec;
314
315 if (codec.codec_type == CODEC_TYPE_AUDIO || codec.codec_type == CODEC_TYPE_VIDEO) {
316 const string streamuri = ar.newAnonymousUri();
317 ar.addValue(factory->hasPartProperty, streamuri);
318 ar.addTriplet(streamuri, partOfPropertyName, ar.path());
319 ar.addTriplet(streamuri, typePropertyName, embeddedClassName);
320
321 if ((stream.duration != no_bitrate) && stream.time_base.num && stream.time_base.den) {
322 ostringstream outs;
323 outs << (stream.duration * stream.time_base.num / stream.time_base.den);
324 ar.addTriplet(streamuri, durationPropertyName,outs.str());
325 }
326 if (size_t len = strlen(stream.language)) {
327 ar.addTriplet(streamuri, languagePropertyName, string(stream.language, len));
328 }
329 const AVCodec *p = avcodec_find_decoder(codec.codec_id);
330 if (p) {
331 if (size_t len = strlen(p->name)) {
332 ar.addTriplet(streamuri, codecPropertyName, string(p->name, len));
333 }
334 } else if (size_t len = strlen(codec.codec_name)) {
335 ar.addTriplet(streamuri, codecPropertyName, string(codec.codec_name, len));
336 }
337/*
33800792 } else if (enc->codec_id == CODEC_ID_MPEG2TS) {
339// fake mpeg2 transport stream codec (currently not
34000794 registered)
34100795 codec_name = "mpeg2ts";
34200798 } else {
34300799 // output avi tags
34400800 if( isprint(enc->codec_tag&0xFF) && isprint((enc->codec_tag>>8)&0xFF)
34500801 && isprint((enc->codec_tag>>16)&0xFF) && isprint((enc->codec_tag>>24)&0xFF)){
34600802 snprintf(buf1, sizeof(buf1), "%c%c%c%c / 0x%04X",
34700803 enc->codec_tag & 0xff,
34800804 (enc->codec_tag >> 8) & 0xff,
34900805 (enc->codec_tag >> 16) & 0xff,
35000806 (enc->codec_tag >> 24) & 0xff,
35100807 enc->codec_tag);
35200808 } else {
35300809 snprintf(buf1, sizeof(buf1), "0x%04x", enc->codec_tag);
35400810 }
35500811 codec_name = buf1;
35600812 }
357*/
358 if (codec.bit_rate) {
359 ostringstream outs;
360 outs << codec.bit_rate;
361 ar.addTriplet(streamuri, bitratePropertyName, outs.str());
362 }
363
364 if (codec.codec_type == CODEC_TYPE_AUDIO) {
365
366 ar.addTriplet(streamuri, typePropertyName, audioClassName);
367 if (codec.channels) {
368 ostringstream outs;
369 outs << codec.channels;
370 ar.addTriplet(streamuri, channelsPropertyName, outs.str());
371 }
372 if (codec.sample_rate) {
373 ostringstream outs;
374 outs << codec.sample_rate;
375 ar.addTriplet(streamuri, sampleratePropertyName, outs.str());
376 }
377 if (codec.sample_fmt != SAMPLE_FMT_NONE) {}//FIXME sample format
378
379 } else { // video stream
380
381 ar.addTriplet(streamuri, typePropertyName, videoClassName);
382 if (codec.width) {
383 ostringstream outs;
384 outs << codec.width;
385 ar.addTriplet(streamuri, widthPropertyName, outs.str());
386 if (codec.sample_aspect_ratio.num) {
387 AVRational aspectratio;
388 ostringstream outs;
389 av_reduce(&aspectratio.num, &aspectratio.den,
390 codec.width * codec.sample_aspect_ratio.num,
391 codec.height * codec.sample_aspect_ratio.den,
392 1024*1024);
393 outs << aspectratio.num << ":" << aspectratio.den;
394 ar.addTriplet(streamuri, aspectRatioPropertyName, outs.str());
395 }
396 }
397 if (codec.height) {
398 ostringstream outs;
399 outs << codec.height;
400 ar.addTriplet(streamuri, heightPropertyName, outs.str());
401 }
402 if (stream.r_frame_rate.num && stream.r_frame_rate.den) {
403 ostringstream outs;
404 outs << stream.r_frame_rate.num / stream.r_frame_rate.den;
405 ar.addTriplet(streamuri, frameRatePropertyName, outs.str());
406 }
407 if (codec.pix_fmt != PIX_FMT_NONE) {}//FIXME pixel format
408 }
409
410 }
411 }
412
413 // Tags
414
415 if (int32_t len = strlen(fc->title)) {
416 ar.addValue(factory->titleProperty, string(fc->title, len) );
417 }
418 if (int32_t len = strlen(fc->author)) {
419 const string creatoruri = ar.newAnonymousUri();
420 ar.addValue(factory->creatorProperty, creatoruri);
421 ar.addTriplet(creatoruri, typePropertyName, contactClassName);
422 ar.addTriplet(creatoruri, fullnamePropertyName, string(fc->author, len) );
423 }
424 if (int32_t len = strlen(fc->copyright)) {
425 ar.addValue(factory->copyrightProperty, string(fc->copyright, len) );
426 }
427 if (int32_t len = strlen(fc->comment)) {
428 ar.addValue(factory->commentProperty, string(fc->comment, len) );
429 }
430 if (int32_t len = strlen(fc->album)) {
431 const string album = ar.newAnonymousUri();
432 ar.addValue(factory->albumProperty, album);
433 ar.addTriplet(album, typePropertyName, albumClassName);
434 ar.addTriplet(album, titlePropertyName, string(fc->album, len) );
435 }
436 if (int32_t len = strlen(fc->genre)) {
437 ar.addValue(factory->genreProperty, string(fc->genre, len) );
438 }
439 if (fc->track) {
440 ar.addValue(factory->trackProperty, fc->track);
441 }
442 if (fc->year) {
443 ar.addValue(factory->createdProperty, fc->year);
444 }
445
446 av_close_input_stream(fc);
447 //url_fclose(&ByteIOCtx);
448
449 return 0;
450}
451
452/*
453 For plugins, we need to have a way to find out which plugins are defined in a
454 plugin. One instance of AnalyzerFactoryFactory per plugin profides this
455 information.
456*/
457class Factory : public AnalyzerFactoryFactory {
458public:
459 list<StreamEndAnalyzerFactory*>
460 streamEndAnalyzerFactories() const {
461 list<StreamEndAnalyzerFactory*> af;
462 af.push_back(new FFMPEGEndAnalyzerFactory());
463 return af;
464 }
465};
466
467/*
468 Register the AnalyzerFactoryFactory
469*/
470STRIGI_ANALYZER_FACTORY(Factory)
  
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>
31using namespace Strigi;
32using namespace std;
33
34class XineEndAnalyzerFactory;
35
36class STRIGI_PLUGIN_API XineEndAnalyzer : public StreamEndAnalyzer {
37private:
38 AnalysisResult* result;
39 const XineEndAnalyzerFactory* factory;
40 xine_t *engine;
41 xine_audio_port_t *audiodrv;
42 xine_video_port_t *videodrv;
43
44public:
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
74class STRIGI_PLUGIN_API XineEndAnalyzerFactory : public StreamEndAnalyzerFactory {
75friend class XineEndAnalyzer;
76private:
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
98const 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
128void
129XineEndAnalyzerFactory::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
144bool
145XineEndAnalyzer::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/*
162Left 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
200signed char
201XineEndAnalyzer::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*/
302class Factory : public AnalyzerFactoryFactory {
303public:
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*/
315STRIGI_ANALYZER_FACTORY(Factory)
  
5757 // only use this analyzer if the file has been deterined to be c/c++
5858 // this is not accurate at all but better than what we had before
5959 ready = i->mimeType() != "text/x-csrc"
60 && i->mimeType() != "text/x-chrd"
60 && i->mimeType() != "text/x-chdr"
6161 && i->mimeType() != "text/x-c++src"
62 && i->mimeType() != "text/x-c++hrd";
62 && i->mimeType() != "text/x-c++hdr";
6363}
6464void
6565CppLineAnalyzer::handleLine(const char* data, uint32_t length) {
  
132132 readPixelFormat ( InputStream* in, DDSPixelFormat & pf )
133133 {
134134 const char* c;
135
135
136136 if (4 != in->read(c, 4, 4))
137137 return false;
138138 pf.size = readLittleEndianUInt32(c);
191191
192192 return true;
193193 }
194
194
195195 bool
196196 readHeader(InputStream* in, DDSHeader& header)
197197 {
233233
234234 if (!readPixelFormat( in, header.pf))
235235 return false;
236
237236
237
238238 if (!readCaps ( in, header.caps))
239239 return false;
240240
257257 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
258258 volumeDepthField = reg.registerField( "http://strigi.sf.net/ontologies/homeless#ddsVolumeDepth");
259259 bitDepthField = reg.registerField(
260 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
260 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
261261 mipmapCountField = reg.registerField("http://strigi.sf.net/ontologies/homeless#ddsMipmapCount");
262262 typeField = reg.registerField("http://strigi.sf.net/ontologies/homeless#ddsImageType");
263263 colorModeField = reg.registerField(
290290
291291 //Remember: dds files are little-endian
292292 //read the beginning of the stream and make sure it looks ok
293
293
294294 if (4 != in->read(c, 4, 4)) {
295295 in->reset(0); // rewind to the start of the stream
296296 return in;
  
7676void
7777GifThroughAnalyzerFactory::registerFields(FieldRegister& reg) {
7878 colorDepthField = reg.registerField(
79 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
79 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
8080 widthField = reg.registerField(
8181 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width");
8282 heightField = reg.registerField(
  
4141 numberField = reg.registerField(
4242 "http://strigi.sf.net/ontologies/homeless#documentImageCount");
4343 bitsPerPixelField = reg.registerField(
44 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
44 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
4545 colorCountField = reg.registerField(
4646 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorCount");
4747
6565
6666 const char *c;
6767 int32_t n;
68
68
6969 // Remember: ICO files are little-endian
7070 // read the beginning of the stream and make sure it looks ok
71
71
7272 n = in->read(c, 6, 6);
7373 if (n != 6) {
7474 in->reset(0); // rewind to the start of the stream
154154 analysisResult->addValue( factory->colorCountField, icoe_colorcount );
155155 else if (icoe_bitcount > 0)
156156 analysisResult->addValue( factory->colorCountField, 2 ^ icoe_bitcount );
157
157
158158 in->reset(0); // rewind to the start of the stream
159159 return in;
160160}
  
4141 vResolutionField = reg.registerField(
4242 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#verticalResolution");
4343 colorDepthField = reg.registerField(
44 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
44 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
4545 typeField = reg.typeField;
4646
4747 addField(compressionField);
7070 in->reset(0);
7171 if (nread < nreq) return in;
7272 // check header for some magic bytes and determine if it is valid pcx file
73 if (header[0]!=10 || header[1]>5 || header[1]==1 || header[2]>1 ||
73 if (header[0]!=10 || header[1]>5 || header[1]==1 || header[2]>1 ||
7474 header[3]>8 || header[3]==3 || (header[3]>4 && header[3]<8) || header[64]!=0) return in;
7575 // header should be padded to 128 bytes with zeros
7676 for (int i=74;i<128;i++) if (header[i]!=0) return in;
77
77
7878 int w = ( readLittleEndianUInt16(header+8)-readLittleEndianUInt16(header+4) ) + 1;
7979 int h = ( readLittleEndianUInt16(header+10)-readLittleEndianUInt16(header+6) ) + 1;
8080 int bpp = header[3]*header[65];
  
2626#include <strigi/fieldtypes.h>
2727#include <cstring>
2828#include <map>
29//#include <config.h>
3029
3130using namespace std;
3231using namespace Strigi;
4343 heightField = reg.registerField(
4444 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height");
4545 bitDepthField = reg.registerField(
46 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
46 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
4747 imageNameField = reg.registerField(
4848 "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#title");
4949 sharedRowsField = reg.registerField(
110110 if (dimension == 1)
111111 ysize = 1;
112112
113 // report analysis
113 // report analysis
114114 analysisResult->addValue( factory->widthField, xsize );
115115 analysisResult->addValue( factory->heightField, ysize );
116116 analysisResult->addValue( factory->bitDepthField, zsize * 8 * bpc );
  
3434 compressionField = reg.registerField(
3535 "http://freedesktop.org/standards/xesam/1.0/core#compressionAlgorithm");
3636 colorDepthField = reg.registerField(
37 "http://www.semanticdesktop.org/ontologies/nfo#colorDepth");
37 "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#colorDepth");
3838 colorModeField = reg.registerField(
3939 "http://freedesktop.org/standards/xesam/1.0/core#colorSpace");
4040 widthField = reg.registerField(
  
167167 if (StreamBase<T>::m_status == Error) return -2;
168168 // check to see if we have this position
169169 int64_t d = StreamBase<T>::m_position - newpos;
170 if (buffer.readPos - d >= buffer.start && -d < buffer.avail) {
170 if (buffer.readPos >= buffer.start + d && -d <= buffer.avail) {
171171 StreamBase<T>::m_position -= d;
172172 buffer.avail += (int32_t)d;
173173 buffer.readPos -= d;
  
5151 FileInputStream(FILE* file, const char* filepath,
5252 int32_t buffersize=defaultBufferSize);
5353 ~FileInputStream();
54
55 enum StreamTypeHint {
56 /** let the system choose the most appropriate type **/
57 Automatic,
58 /** use an internal buffer, can be slow when skipping in large files **/
59 Buffered,
60 /** do not use an internal buffer, faster for most use cases **/
61 Unbuffered,
62 /** use mmap for reading the files, falls back to unbuffered when not
63 available **/
64 MMap
65 };
66
67 /**
68 * @brief Create an InputStream to access a file
69 *
70 * @param filepath the name of the file to open
71 * @param buffersize the size of the buffer to use, if applicable
72 * @param hint preferred type of stream
73 * @return opened input stream to the file, caller has responsibility to
74 * delete it
75 */
76 static InputStream* open(const char* filepath,
77 StreamTypeHint hint = Automatic,
78 int32_t buffersize = defaultBufferSize);
5479};
5580
5681} // end namespace Strigi
  
2222
2323#include <cstdlib>
2424#include <cstring>
25#include <cassert>
2526
2627namespace Strigi {
2728
123123StreamBuffer<T>::setSize(int32_t size) {
124124 // store pointer information
125125 int32_t offset = (int32_t)(readPos - start);
126 assert(size >= 0);
127 assert(avail >= 0);
128 assert(offset >= 0);
129 assert(avail+offset <= size); // catch broken offset and avail values when shrinking the buffer
126130
127131 // allocate memory in the buffer
128132 start = (T*)std::realloc(start, size*sizeof(T));
141141 // determine how much space is available for writing
142142 int32_t offset = (int32_t)(readPos - start);
143143 int32_t space = size - offset - avail;
144
145 assert(offset >= 0);
146 assert(size >= 0);
147 assert(avail >= 0);
148 assert(avail+offset <= size);
149
144150 if (space >= needed) {
145151 // there's enough space
146152 return space;
177177template <class T>
178178int32_t
179179StreamBuffer<T>::read(const T*& start, int32_t max) {
180 assert(size >= 0);
181 assert(avail >= 0);
182 assert(readPos >= this->start);
183 assert(avail+(readPos-this->start) <= size);
184
180185 start = readPos;
181186 if (max <= 0 || max > avail) {
182187 max = avail;
  
1818 * Boston, MA 02110-1301, USA.
1919 */
2020#include <strigi/fileinputstream.h>
21#include "mmapfileinputstream.h"
22#include "skippingfileinputstream.h"
23#include "skippingfileinputstream2.h"
2124#include <config.h>
2225#include <strigi/strigiconfig.h>
2326#include <iostream>
114114 }
115115 //cerr << "read " << nwritten << " bytes of\t" << filepath << endl;
116116 return nwritten;
117}
118InputStream*
119FileInputStream::open(const char* filepath, StreamTypeHint hint,
120 int32_t buffersize) {
121 switch (hint) {
122 case Buffered:
123 return new FileInputStream(filepath, buffersize);
124 case MMap:
125#ifndef _WIN32
126 return new MMapFileInputStream(filepath);
127#endif
128 case Unbuffered:
129 case Automatic:
130 default:
131 return new SkippingFileInputStream(filepath);
132 }
117133}
  
3030using namespace Strigi;
3131using namespace std;
3232
33MMapFileInputStream::MMapFileInputStream(const char* filepath) :p(0) {
33MMapFileInputStream::MMapFileInputStream(const char* filepath) {
3434 int fd = ::open(filepath, O_RDONLY);
3535 struct stat sb;
3636 if (fd == -1 || fstat(fd, &sb) == -1) {
  
2727/**
2828 * @brief Provides buffered access to a file
2929 */
30class STREAMS_EXPORT MMapFileInputStream : public InputStream {
30class MMapFileInputStream : public InputStream {
3131private:
32 class Private;
33 Private * const p;
3432 const char *buffer;
3533
3634 void open(FILE* f, const char* path);
  
3030using namespace Strigi;
3131using namespace std;
3232
33SkippingFileInputStream::SkippingFileInputStream(const char* filepath) :p(0) {
33SkippingFileInputStream::SkippingFileInputStream(const char* filepath) {
3434 buffer = 0;
3535 buffersize = 0;
3636 if (filepath == 0) {
4848 file = f;
4949 filepath.assign(path);
5050 if (file == 0) {
51 cerr << "ohoh" << endl;
5152 // handle error
5253 m_error = "Could not read file '";
5354 m_error += filepath;
7272 if (n == 1) {
7373 m_size = -1;
7474 fseeko(file, 0, SEEK_SET);
75 } else {
76 fclose(file);
77 file = 0;
78 return;
7975 }
8076 }
8177 }
8787}
8888int32_t
8989SkippingFileInputStream::read(const char*& start, int32_t _min, int32_t _max) {
90 if (!file) {
91 m_status = Error;
92 return -2; // error
93 }
9094 int32_t n = max(_min, _max);
9195 if (n > buffersize) {
9296 buffer = (char*)realloc(buffer, n);
118118}
119119int64_t
120120SkippingFileInputStream::reset(int64_t pos) {
121 if (!file) {
122 m_status = Error;
123 return -2; // error
124 }
121125 if (m_size >= 0 && pos > m_size) pos = m_size;
122126 if (fseek(file, pos, SEEK_SET)) {
123127 m_status = Error;
  
2525namespace Strigi {
2626
2727/**
28 * @brief Provides buffered access to a file
28 * @brief Private class that provides buffered access to a file
2929 */
30class STREAMS_EXPORT SkippingFileInputStream : public InputStream {
30class SkippingFileInputStream : public InputStream {
3131private:
32 class Private;
33 Private * const p;
3432 FILE *file;
3533 char *buffer;
3634 std::string filepath;
  
3232 TESTONARCHIVE(ZipInputStream, "a.zip");
3333
3434 // should have at least one stream
35 InputStream* file = new FileInputStream("a.zip");
35 InputStream* file = FileInputStream::open("a.zip");
3636 const char* data;
3737 file->read(data, 500, 500);
3838 file->reset(0);
  
2929 VERIFY(chdir(argv[1]) == 0);
3030
3131 for (int i=0; i<ninputstreamtests; ++i) {
32 MMapFileInputStream file("a.zip");
33 charinputstreamtests[i](&file);
32 InputStream* file = FileInputStream::open("a.zip", FileInputStream::MMap);
33 charinputstreamtests[i](file);
34 delete file;
3435 }
3536 return founderrors;
3637}
  
5454
5555#include "unknownsizestream.h"
5656#include <strigi/fileinputstream.h>
57#ifdef _WIN32
58 #define MMapFileInputStream FileInputStream
59#else
60 #ifdef STRIGI_INTERNAL_TEST
61 #include "skippingfileinputstream.h"
62 #include "skippingfileinputstream2.h"
63 #include "mmapfileinputstream.h"
64 #endif
65 #define MMapFileInputStream FileInputStream
66#endif
6757
6858#define TESTONFILE(CLASS, FILE) \
6959 for (int i=0; i<ninputstreamtests; ++i) { \
70 FileInputStream f1(FILE); \
71 CLASS s1(&f1); \
60 InputStream* f = FileInputStream::open(FILE); \
61 CLASS s1(f); \
7262 charinputstreamtests[i](&s1); \
63 delete f; \
7364\
74 MMapFileInputStream f2(FILE); \
75 CLASS s2(&f2); \
65 f = FileInputStream::open(FILE, FileInputStream::Buffered); \
66 CLASS s2(f); \
7667 charinputstreamtests[i](&s2); \
68 delete f; \
7769\
78 FileInputStream f3(FILE); \
79 UnknownSizeInputStream u3(&f3); \
70 f = FileInputStream::open(FILE); \
71 UnknownSizeInputStream u3(f); \
8072 CLASS s3(&u3); \
8173 charinputstreamtests[i](&s3); \
74 delete f; \
8275\
83 MMapFileInputStream f4(FILE); \
84 UnknownSizeInputStream u4(&f4); \
85 CLASS s4(&f4); \
76 f = FileInputStream::open(FILE, FileInputStream::Buffered); \
77 UnknownSizeInputStream u4(f); \
78 CLASS s4(&u4); \
8679 charinputstreamtests[i](&s4); \
80 delete f; \
8781 }
8882
8983#define TESTONFILE2(CLASS, ARG, FILE) \
9084 for (int i=0; i<ninputstreamtests; ++i) { \
91 FileInputStream f1(FILE); \
92 CLASS s1(&f1, ARG); \
85 InputStream* f = FileInputStream::open(FILE); \
86 CLASS s1(f, ARG); \
9387 charinputstreamtests[i](&s1); \
88 delete f; \
9489\
95 MMapFileInputStream f2(FILE); \
96 CLASS s2(&f2, ARG); \
90 f = FileInputStream::open(FILE, FileInputStream::Buffered); \
91 CLASS s2(f, ARG); \
9792 charinputstreamtests[i](&s2); \
93 delete f; \
9894\
99 FileInputStream f3(FILE); \
100 UnknownSizeInputStream u3(&f3); \
95 f = FileInputStream::open(FILE); \
96 UnknownSizeInputStream u3(f); \
10197 CLASS s3(&u3, ARG); \
10298 charinputstreamtests[i](&s3); \
99 delete f; \
103100\
104 MMapFileInputStream f4(FILE); \
105 UnknownSizeInputStream u4(&f4); \
101 f = FileInputStream::open(FILE, FileInputStream::Buffered); \
102 UnknownSizeInputStream u4(f); \
106103 CLASS s4(&u4, ARG); \
107104 charinputstreamtests[i](&s4); \
105 delete f; \
108106 }
109107
110108#define TESTONARCHIVE(CLASS, FILE) \
111109 for (int i=0; i<nstreamprovidertests; ++i) { \
112 FileInputStream f1(FILE); \
113 CLASS s1(&f1); \
110 InputStream* f = FileInputStream::open(FILE); \
111 CLASS s1(f); \
114112 streamprovidertests[i](&s1); \
113 delete f; \
115114\
116 MMapFileInputStream f2(FILE); \
117 CLASS s2(&f2); \
115 f = FileInputStream::open(FILE, FileInputStream::Buffered); \
116 CLASS s2(f); \
118117 streamprovidertests[i](&s2); \
118 delete f; \
119119\
120 FileInputStream f3(FILE); \
121 UnknownSizeInputStream u3(&f3); \
120 f = FileInputStream::open(FILE); \
121 UnknownSizeInputStream u3(f); \
122122 CLASS s3(&u3); \
123123 streamprovidertests[i](&s3); \
124 delete f; \
124125\
125 MMapFileInputStream f4(FILE); \
126 UnknownSizeInputStream u4(&f4); \
127 CLASS s4(&f4); \
126 f = FileInputStream::open(FILE, FileInputStream::Buffered); \
127 UnknownSizeInputStream u4(f); \
128 CLASS s4(&u4); \
128129 streamprovidertests[i](&s4); \
130 delete f; \
129131 }
130132
131133#endif