00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00036
00037
00038
00039
00040
00041
00042
00043 #ifdef HAVE_CONFIG_H
00044 #include "autoconfig.h"
00045 #endif
00046
00047 #ifdef HAVE_FFMPEG
00048
00049
00050
00051
00052 #include <stdint.h>
00053
00054
00055 extern "C"
00056 {
00057
00058
00059 #include AVFORMAT_INCLUDE
00060
00061
00062 }
00063
00064
00065 #ifdef HAVE_FFMPEGTHUMBNAILER
00066 #include <libffmpegthumbnailer/videothumbnailerc.h>
00067 #endif
00068
00069 #include "config_manager.h"
00070 #include "ffmpeg_handler.h"
00071 #include "string_converter.h"
00072 #include "common.h"
00073 #include "tools.h"
00074 #include "rexp.h"
00075
00076 #include "mem_io_handler.h"
00077
00078
00079 using namespace zmm;
00080
00081
00082
00083 FfmpegHandler::FfmpegHandler() : MetadataHandler()
00084 {
00085 }
00086
00087 static void addFfmpegMetadataFields(Ref<CdsItem> item, AVFormatContext *pFormatCtx)
00088 {
00089
00090 Ref<StringConverter> sc = StringConverter::m2i();
00091
00092 if (strlen(pFormatCtx->title) > 0)
00093 {
00094 log_debug("Added metadata title: %s\n", pFormatCtx->title);
00095 item->setMetadata(MT_KEYS[M_TITLE].upnp,
00096 sc->convert(pFormatCtx->title));
00097 }
00098 if (strlen(pFormatCtx->author) > 0)
00099 {
00100 log_debug("Added metadata author: %s\n", pFormatCtx->author);
00101 item->setMetadata(MT_KEYS[M_ARTIST].upnp,
00102 sc->convert(pFormatCtx->author));
00103 }
00104 if (strlen(pFormatCtx->album) > 0)
00105 {
00106 log_debug("Added metadata album: %s\n", pFormatCtx->album);
00107 item->setMetadata(MT_KEYS[M_ALBUM].upnp,
00108 sc->convert(pFormatCtx->album));
00109 }
00110 if (pFormatCtx->year > 0)
00111 {
00112 log_debug("Added metadata year: %d\n", pFormatCtx->year);
00113 item->setMetadata(MT_KEYS[M_DATE].upnp,
00114 sc->convert(String::from(pFormatCtx->year)));
00115 }
00116 if (strlen(pFormatCtx->genre) > 0)
00117 {
00118 log_debug("Added metadata genre: %s\n", pFormatCtx->genre);
00119 item->setMetadata(MT_KEYS[M_GENRE].upnp,
00120 sc->convert(pFormatCtx->genre));
00121 }
00122 if (strlen(pFormatCtx->comment) > 0)
00123 {
00124 log_debug("Added metadata comment: %s\n", pFormatCtx->comment);
00125 item->setMetadata(MT_KEYS[M_DESCRIPTION].upnp,
00126 sc->convert(pFormatCtx->comment));
00127 }
00128 if (pFormatCtx->track > 0)
00129 {
00130 log_debug("Added metadata track: %d\n", pFormatCtx->track);
00131 item->setMetadata(MT_KEYS[M_TRACKNUMBER].upnp,
00132 sc->convert(String::from(pFormatCtx->track)));
00133 }
00134 }
00135
00136
00137 static void addFfmpegResourceFields(Ref<CdsItem> item, AVFormatContext *pFormatCtx, int *x, int *y)
00138 {
00139 unsigned int i;
00140 int hours, mins, secs, us;
00141 int audioch = 0, samplefreq = 0;
00142 bool audioset, videoset;
00143 String resolution;
00144 char duration[15];
00145
00146
00147 duration[0] = 0;
00148
00149 *x = 0;
00150 *y = 0;
00151
00152
00153 secs = pFormatCtx->duration / AV_TIME_BASE;
00154 us = pFormatCtx->duration % AV_TIME_BASE;
00155 mins = secs / 60;
00156 secs %= 60;
00157 hours = mins / 60;
00158 mins %= 60;
00159 if ((hours + mins + secs) > 0)
00160 {
00161 sprintf(duration, "%02d:%02d:%02d.%01d", hours, mins,
00162 secs, (10 * us) / AV_TIME_BASE);
00163 log_debug("Added duration: %s\n", duration);
00164 item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_DURATION), duration);
00165 }
00166
00167
00168 if (pFormatCtx->bit_rate > 0)
00169 {
00170 log_debug("Added overall bitrate: %d kb/s\n",
00171 pFormatCtx->bit_rate/1000);
00172 item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_BITRATE), String::from(pFormatCtx->bit_rate/1000));
00173 }
00174
00175
00176 audioset = false;
00177 videoset = false;
00178 for(i=0; i<pFormatCtx->nb_streams; i++)
00179 {
00180 AVStream *st = pFormatCtx->streams[i];
00181 if((st != NULL) && (videoset == false) && (st->codec->codec_type == CODEC_TYPE_VIDEO))
00182 {
00183 if (st->codec->codec_tag > 0)
00184 {
00185 char fourcc[5];
00186 fourcc[0] = st->codec->codec_tag;
00187 fourcc[1] = st->codec->codec_tag >> 8;
00188 fourcc[2] = st->codec->codec_tag >> 16;
00189 fourcc[3] = st->codec->codec_tag >> 24;
00190 fourcc[4] = '\0';
00191
00192 log_debug("FourCC: %x = %s\n",
00193 st->codec->codec_tag, fourcc);
00194 String fcc = fourcc;
00195 if (string_ok(fcc))
00196 item->getResource(0)->addOption(_(RESOURCE_OPTION_FOURCC),
00197 fcc);
00198 }
00199
00200 if ((st->codec->width > 0) && (st->codec->height > 0))
00201 {
00202 resolution = String::from(st->codec->width) + "x" +
00203 String::from(st->codec->height);
00204
00205 log_debug("Added resolution: %s pixel\n", resolution.c_str());
00206 item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION), resolution);
00207 videoset = true;
00208 *x = st->codec->width;
00209 *y = st->codec->height;
00210 }
00211 }
00212 if(st->codec->codec_type == CODEC_TYPE_AUDIO)
00213 {
00214
00215 audioch++;
00216
00217 if ((audioset == false) && (st->codec->sample_rate > 0))
00218 {
00219 samplefreq = st->codec->sample_rate;
00220 if (samplefreq > 0)
00221 {
00222 log_debug("Added sample frequency: %d Hz\n", samplefreq);
00223 item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_SAMPLEFREQUENCY), String::from(samplefreq));
00224 audioset = true;
00225 }
00226 }
00227 }
00228 }
00229
00230 if (audioch > 0)
00231 {
00232 log_debug("Added number of audio channels: %d\n", audioch);
00233 item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_NRAUDIOCHANNELS), String::from(audioch));
00234 }
00235 }
00236
00237
00238
00239
00240
00241
00242 void FfmpegNoOutputStub(void* ptr, int level, const char* fmt, va_list vl)
00243 {
00244
00245 }
00246
00247 void FfmpegHandler::fillMetadata(Ref<CdsItem> item)
00248 {
00249 log_debug("Running ffmpeg handler on %s\n", item->getLocation().c_str());
00250
00251 int x = 0;
00252 int y = 0;
00253
00254 AVFormatContext *pFormatCtx;
00255
00256
00257 av_log_set_callback(FfmpegNoOutputStub);
00258
00259
00260 av_register_all();
00261
00262
00263 if (av_open_input_file(&pFormatCtx,
00264 item->getLocation().c_str(), NULL, 0, NULL) != 0)
00265 return;
00266
00267
00268 if (av_find_stream_info(pFormatCtx) < 0)
00269 {
00270 av_close_input_file(pFormatCtx);
00271 return;
00272 }
00273
00274 addFfmpegMetadataFields(item, pFormatCtx);
00275
00276 addFfmpegResourceFields(item, pFormatCtx, &x, &y);
00277
00278
00279 av_close_input_file(pFormatCtx);
00280 }
00281
00282 Ref<IOHandler> FfmpegHandler::serveContent(Ref<CdsItem> item, int resNum, off_t *data_size)
00283 {
00284 *data_size = -1;
00285 #ifdef HAVE_FFMPEGTHUMBNAILER
00286 Ref<ConfigManager> cfg = ConfigManager::getInstance();
00287
00288 if (!cfg->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_ENABLED))
00289 return nil;
00290
00291 #ifdef FFMPEGTHUMBNAILER_OLD_API
00292 video_thumbnailer *th = create_thumbnailer();
00293 image_data *img = create_image_data();
00294 #else
00295 video_thumbnailer *th = video_thumbnailer_create();
00296 image_data *img = video_thumbnailer_create_image_data();
00297 #endif // old api
00298
00299
00300 th->seek_percentage = cfg->getIntOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_SEEK_PERCENTAGE);
00301
00302 if (cfg->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_FILMSTRIP_OVERLAY))
00303 th->overlay_film_strip = 1;
00304 else
00305 th->overlay_film_strip = 0;
00306
00307 th->thumbnail_size = cfg->getIntOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_THUMBSIZE);
00308 th->thumbnail_image_quality = cfg->getIntOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_IMAGE_QUALITY);
00309 th->thumbnail_image_type = Jpeg;
00310
00311 log_debug("Generating thumbnail for file: %s\n", item->getLocation().c_str());
00312
00313 #ifdef FFMPEGTHUMBNAILER_OLD_API
00314 if (generate_thumbnail_to_buffer(th, item->getLocation().c_str(), img) != 0)
00315 #else
00316 if (video_thumbnailer_generate_thumbnail_to_buffer(th,
00317 item->getLocation().c_str(), img) != 0)
00318 #endif // old api
00319 throw _Exception(_("Could not generate thumbnail for ") +
00320 item->getLocation());
00321
00322 *data_size = (off_t)img->image_data_size;
00323 Ref<IOHandler> h(new MemIOHandler((void *)img->image_data_ptr,
00324 img->image_data_size));
00325 #ifdef FFMPEGTHUMBNAILER_OLD_API
00326 destroy_image_data(img);
00327 destroy_thumbnailer(th);
00328 #else
00329 video_thumbnailer_destroy_image_data(img);
00330 video_thumbnailer_destroy(th);
00331 #endif// old api
00332 return h;
00333 #else
00334 return nil;
00335 #endif
00336 }
00337
00338 String FfmpegHandler::getMimeType()
00339 {
00340 Ref<ConfigManager> cfg = ConfigManager::getInstance();
00341
00342 Ref<Dictionary> mappings = cfg->getDictionaryOption(CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
00343 String thumb_mimetype = mappings->get(_(CONTENT_TYPE_JPG));
00344 if (!string_ok(thumb_mimetype))
00345 thumb_mimetype = _("image/jpeg");
00346
00347 return thumb_mimetype;
00348 }
00349 #endif // HAVE_FFMPEG