3
MediaTomb - http://www.mediatomb.cc/
5
ffmpeg_handler.cc - this file is part of MediaTomb.
7
Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
8
Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
10
Copyright (C) 2006-2009 Gena Batyan <bgeradz@mediatomb.cc>,
11
Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
12
Leonhard Wimmer <leo@mediatomb.cc>
14
MediaTomb is free software; you can redistribute it and/or modify
15
it under the terms of the GNU General Public License version 2
16
as published by the Free Software Foundation.
18
MediaTomb is distributed in the hope that it will be useful,
19
but WITHOUT ANY WARRANTY; without even the implied warranty of
20
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
GNU General Public License for more details.
23
You should have received a copy of the GNU General Public License
24
version 2 along with MediaTomb; if not, write to the Free Software
25
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
27
$Id: ffmpeg_handler.cc 2010 2009-01-11 19:10:43Z lww $
30
This code was contributed by
31
Copyright (C) 2007 Ingo Preiml <ipreiml@edu.uni-klu.ac.at>
34
/// \file ffmpeg_handler.cc
35
/// \brief Implementeation of the FfmpegHandler class.
37
// Information about the stream are to be found in the structure
38
// AVFormatContext, defined in libavformat/avformat.h:335
39
// and in the structure
40
// AVCodecContext, defined in libavcodec/avcodec.h:722
41
// in the ffmpeg sources
44
#include "autoconfig.h"
49
// ffmpeg needs the following sources
50
// INT64_C is not defined in ffmpeg/avformat.h but is needed
51
// macro defines included via autoconfig.h
54
//#ifdef FFMPEG_NEEDS_EXTERN_C
59
#include AVFORMAT_INCLUDE
61
//#ifdef FFMPEG_NEEDS_EXTERN_C
65
#ifdef HAVE_FFMPEGTHUMBNAILER
66
#include <libffmpegthumbnailer/videothumbnailerc.h>
69
#include "config_manager.h"
70
#include "ffmpeg_handler.h"
71
#include "string_converter.h"
75
//#include "mxml/mxml.h"
76
#include "mem_io_handler.h"
80
//using namespace mxml;
82
// Default constructor
83
FfmpegHandler::FfmpegHandler() : MetadataHandler()
87
static void addFfmpegMetadataFields(Ref<CdsItem> item, AVFormatContext *pFormatCtx)
90
Ref<StringConverter> sc = StringConverter::m2i();
92
if (strlen(pFormatCtx->title) > 0)
94
log_debug("Added metadata title: %s\n", pFormatCtx->title);
95
item->setMetadata(String(MT_KEYS[M_TITLE].upnp),
96
sc->convert(String(pFormatCtx->title)));
98
if (strlen(pFormatCtx->author) > 0)
100
log_debug("Added metadata author: %s\n", pFormatCtx->author);
101
item->setMetadata(String(MT_KEYS[M_ARTIST].upnp),
102
sc->convert(String(pFormatCtx->author)));
104
if (strlen(pFormatCtx->album) > 0)
106
log_debug("Added metadata album: %s\n", pFormatCtx->album);
107
item->setMetadata(String(MT_KEYS[M_ALBUM].upnp),
108
sc->convert(String(pFormatCtx->album)));
110
if (pFormatCtx->year > 0)
112
log_debug("Added metadata year: %d\n", pFormatCtx->year);
113
item->setMetadata(String(MT_KEYS[M_DATE].upnp),
114
sc->convert(String::from(pFormatCtx->year)));
116
if (strlen(pFormatCtx->genre) > 0)
118
log_debug("Added metadata genre: %s\n", pFormatCtx->genre);
119
item->setMetadata(String(MT_KEYS[M_GENRE].upnp),
120
sc->convert(String(pFormatCtx->genre)));
122
if (strlen(pFormatCtx->comment) > 0)
124
log_debug("Added metadata comment: %s\n", pFormatCtx->comment);
125
item->setMetadata(String(MT_KEYS[M_DESCRIPTION].upnp),
126
sc->convert(String(pFormatCtx->comment)));
128
if (pFormatCtx->track > 0)
130
log_debug("Added metadata track: %d\n", pFormatCtx->track);
131
item->setMetadata(String(MT_KEYS[M_TRACKNUMBER].upnp),
132
sc->convert(String::from(pFormatCtx->track)));
136
// ffmpeg library calls
137
static void addFfmpegResourceFields(Ref<CdsItem> item, AVFormatContext *pFormatCtx, int *x, int *y)
140
int hours, mins, secs, us;
141
int audioch = 0, samplefreq = 0;
142
bool audioset, videoset;
146
// Initialize the buffers
153
secs = pFormatCtx->duration / AV_TIME_BASE;
154
us = pFormatCtx->duration % AV_TIME_BASE;
159
if ((hours + mins + secs) > 0)
161
sprintf(duration, "%02d:%02d:%02d.%01d", hours, mins,
162
secs, (10 * us) / AV_TIME_BASE);
163
log_debug("Added duration: %s\n", duration);
164
item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_DURATION), String(duration));
168
if (pFormatCtx->bit_rate > 0)
170
log_debug("Added overall bitrate: %d kb/s\n",
171
pFormatCtx->bit_rate/1000);
172
item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_BITRATE), String::from(pFormatCtx->bit_rate/1000));
175
// video resolution, audio sampling rate, nr of audio channels
178
for(i=0; i<pFormatCtx->nb_streams; i++)
180
AVStream *st = pFormatCtx->streams[i];
181
if((st != NULL) && (videoset == false) && (st->codec->codec_type == CODEC_TYPE_VIDEO))
183
if (st->codec->codec_tag > 0)
186
fourcc[0] = st->codec->codec_tag;
187
fourcc[1] = st->codec->codec_tag >> 8;
188
fourcc[2] = st->codec->codec_tag >> 16;
189
fourcc[3] = st->codec->codec_tag >> 24;
192
log_debug("FourCC: %x = %s\n",
193
st->codec->codec_tag, fourcc);
194
String fcc = String(fourcc);
196
item->getResource(0)->addOption(_(RESOURCE_OPTION_FOURCC),
200
if ((st->codec->width > 0) && (st->codec->height > 0))
202
resolution = String::from(st->codec->width) + "x" +
203
String::from(st->codec->height);
205
log_debug("Added resolution: %s pixel\n", resolution.c_str());
206
item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION), resolution);
208
*x = st->codec->width;
209
*y = st->codec->height;
212
if(st->codec->codec_type == CODEC_TYPE_AUDIO)
214
// Increase number of audiochannels
216
// Get the sample rate
217
if ((audioset == false) && (st->codec->sample_rate > 0))
219
samplefreq = st->codec->sample_rate;
222
log_debug("Added sample frequency: %d Hz\n", samplefreq);
223
item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_SAMPLEFREQUENCY), String::from(samplefreq));
232
log_debug("Added number of audio channels: %d\n", audioch);
233
item->getResource(0)->addAttribute(MetadataHandler::getResAttrName(R_NRAUDIOCHANNELS), String::from(audioch));
235
} // addFfmpegResourceFields
237
/*double time_to_double(struct timeval time) {
238
return time.tv_sec + (time.tv_usec / 1000000.0);
241
// Stub for suppressing ffmpeg error messages during matadata extraction
242
void FfmpegNoOutputStub(void* ptr, int level, const char* fmt, va_list vl)
247
void FfmpegHandler::fillMetadata(Ref<CdsItem> item)
249
log_debug("Running ffmpeg handler on %s\n", item->getLocation().c_str());
254
AVFormatContext *pFormatCtx;
256
// Suppress all log messages
257
av_log_set_callback(FfmpegNoOutputStub);
259
// Register all formats and codecs
263
if (av_open_input_file(&pFormatCtx,
264
item->getLocation().c_str(), NULL, 0, NULL) != 0)
265
return; // Couldn't open file
267
// Retrieve stream information
268
if (av_find_stream_info(pFormatCtx) < 0)
270
av_close_input_file(pFormatCtx);
271
return; // Couldn't find stream information
273
// Add metadata using ffmpeg library calls
274
addFfmpegMetadataFields(item, pFormatCtx);
275
// Add resources using ffmpeg library calls
276
addFfmpegResourceFields(item, pFormatCtx, &x, &y);
278
// Close the video file
279
av_close_input_file(pFormatCtx);
282
Ref<IOHandler> FfmpegHandler::serveContent(Ref<CdsItem> item, int resNum, off_t *data_size)
285
#ifdef HAVE_FFMPEGTHUMBNAILER
286
Ref<ConfigManager> cfg = ConfigManager::getInstance();
288
if (!cfg->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_ENABLED))
291
video_thumbnailer *th = create_thumbnailer();
292
image_data *img = create_image_data();
294
th->seek_percentage = cfg->getIntOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_SEEK_PERCENTAGE);
296
if (cfg->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_FILMSTRIP_OVERLAY))
297
th->overlay_film_strip = 1;
299
th->overlay_film_strip = 0;
301
th->thumbnail_size = cfg->getIntOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_THUMBSIZE);
302
th->thumbnail_image_type = Jpeg;
304
log_debug("Generating thumbnail for file: %s\n", item->getLocation().c_str());
305
if (generate_thumbnail_to_buffer(th, item->getLocation().c_str(), img) != 0)
306
throw _Exception(_("Could not generate thumbnail for ") + item->getLocation());
308
*data_size = (off_t)img->image_data_size;
309
Ref<IOHandler> h(new MemIOHandler((void *)img->image_data_ptr,
310
img->image_data_size));
311
destroy_image_data(img);
312
destroy_thumbnailer(th);
319
String FfmpegHandler::getMimeType()
321
Ref<ConfigManager> cfg = ConfigManager::getInstance();
323
Ref<Dictionary> mappings = cfg->getDictionaryOption(CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
324
String thumb_mimetype = mappings->get(_(CONTENT_TYPE_JPG));
325
if (!string_ok(thumb_mimetype))
326
thumb_mimetype = _("image/jpeg");
328
return thumb_mimetype;
330
#endif // HAVE_FFMPEG