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
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include "autoconfig.h"
00034 #endif
00035
00036 #ifdef EXTERNAL_TRANSCODING
00037
00038 #include "transcode_ext_handler.h"
00039 #include "server.h"
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 #include <fcntl.h>
00043 #include <unistd.h>
00044 #include <string.h>
00045 #include <stdio.h>
00046 #include <signal.h>
00047 #include <limits.h>
00048 #include "common.h"
00049 #include "storage.h"
00050 #include "cds_objects.h"
00051 #include "process.h"
00052 #include "update_manager.h"
00053 #include "session_manager.h"
00054 #include "ixml.h"
00055 #include "process_io_handler.h"
00056 #include "buffered_io_handler.h"
00057 #include "dictionary.h"
00058 #include "metadata_handler.h"
00059 #include "tools.h"
00060 #include "file_io_handler.h"
00061 #include "transcoding_process_executor.h"
00062 #include "io_handler_chainer.h"
00063 #include "play_hook.h"
00064
00065 #ifdef HAVE_CURL
00066 #include "curl_io_handler.h"
00067 #endif
00068
00069 #ifdef HAVE_LIBDVDNAV
00070 #include "dvd_io_handler.h"
00071 #include "metadata/dvd_handler.h"
00072 #include "fd_io_handler.h"
00073 #include "mpegremux_processor.h"
00074 #endif
00075
00076 using namespace zmm;
00077
00078 TranscodeExternalHandler::TranscodeExternalHandler() : TranscodeHandler()
00079 {
00080 }
00081
00082 Ref<IOHandler> TranscodeExternalHandler::open(Ref<TranscodingProfile> profile,
00083 String location,
00084 Ref<CdsObject> obj,
00085 struct File_Info *info)
00086 {
00087 bool isURL = false;
00088
00089
00090 log_debug("start\n");
00091 char fifo_template[]="mt_transcode_XXXXXX";
00092
00093 if (profile == nil)
00094 throw _Exception(_("Transcoding of file ") + location +
00095 "requested but no profile given");
00096
00097 isURL = (IS_CDS_ITEM_INTERNAL_URL(obj->getObjectType()) ||
00098 IS_CDS_ITEM_EXTERNAL_URL(obj->getObjectType()));
00099
00100 String mimeType = profile->getTargetMimeType();
00101
00102 if (IS_CDS_ITEM(obj->getObjectType()))
00103 {
00104 Ref<CdsItem> it = RefCast(obj, CdsItem);
00105 Ref<Dictionary> mappings = ConfigManager::getInstance()->getDictionaryOption(
00106 CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
00107
00108 if (mappings->get(mimeType) == CONTENT_TYPE_PCM)
00109 {
00110 String freq = it->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_SAMPLEFREQUENCY));
00111 String nrch = it->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_NRAUDIOCHANNELS));
00112
00113 if (string_ok(freq))
00114 mimeType = mimeType + _(";rate=") + freq;
00115 if (string_ok(nrch))
00116 mimeType = mimeType + _(";channels=") + nrch;
00117 }
00118 }
00119
00120 info->content_type = ixmlCloneDOMString(mimeType.c_str());
00121
00122 #ifdef EXTEND_PROTOCOLINFO
00123 String header;
00124 header = getDLNAtransferHeader(mimeType, header);
00125 if (string_ok(header))
00126 info->http_header = ixmlCloneDOMString(header.c_str());
00127 #endif
00128
00129 info->file_length = UNKNOWN_CONTENT_LENGTH;
00130 info->force_chunked = (int)profile->getChunked();
00131
00132 Ref<ConfigManager> cfg = ConfigManager::getInstance();
00133
00134 String fifo_name = normalizePath(tempName(cfg->getOption(CFG_SERVER_TMPDIR),
00135 fifo_template));
00136 String arguments;
00137 String temp;
00138 String command;
00139 Ref<Array<StringBase> > arglist;
00140 Ref<Array<ProcListItem> > proc_list = nil;
00141
00142 #ifdef SOPCAST
00143 service_type_t service = OS_None;
00144 if (obj->getFlag(OBJECT_FLAG_ONLINE_SERVICE))
00145 {
00146 service = (service_type_t)(obj->getAuxData(_(ONLINE_SERVICE_AUX_ID)).toInt());
00147 }
00148
00149 if (service == OS_SopCast)
00150 {
00151 Ref<Array<StringBase> > sop_args;
00152 int p1 = find_local_port(45000,65500);
00153 int p2 = find_local_port(45000,65500);
00154 sop_args = parseCommandLine(location + " " + String::from(p1) + " " +
00155 String::from(p2), nil, nil);
00156 Ref<ProcessExecutor> spsc(new ProcessExecutor(_("sp-sc-auth"),
00157 sop_args));
00158 proc_list = Ref<Array<ProcListItem> >(new Array<ProcListItem>(1));
00159 Ref<ProcListItem> pr_item(new ProcListItem(RefCast(spsc, Executor)));
00160 proc_list->append(pr_item);
00161 location = _("http://localhost:") + String::from(p2) + "/tv.asf";
00162 #warning check if socket is ready
00163 sleep(15);
00164 }
00165 #warning check if we can use "accept url" with sopcast
00166 else
00167 {
00168 #endif
00169 if (isURL && (!profile->acceptURL()))
00170 {
00171 #ifdef HAVE_CURL
00172 String url = location;
00173 strcpy(fifo_template, "mt_transcode_XXXXXX");
00174 location = normalizePath(tempName(cfg->getOption(CFG_SERVER_TMPDIR), fifo_template));
00175 log_debug("creating reader fifo: %s\n", location.c_str());
00176 if (mkfifo(location.c_str(), O_RDWR) == -1)
00177 {
00178 log_error("Failed to create fifo for the remote content "
00179 "reading thread: %s\n", strerror(errno));
00180 throw _Exception(_("Could not create reader fifo!\n"));
00181 }
00182
00183 try
00184 {
00185 chmod(location.c_str(), S_IWUSR | S_IRUSR);
00186
00187 Ref<IOHandler> c_ioh(new CurlIOHandler(url, NULL,
00188 cfg->getIntOption(CFG_EXTERNAL_TRANSCODING_CURL_BUFFER_SIZE),
00189 cfg->getIntOption(CFG_EXTERNAL_TRANSCODING_CURL_FILL_SIZE)));
00190
00191 Ref<IOHandler> p_ioh(new ProcessIOHandler(location, nil));
00192 Ref<Executor> ch(new IOHandlerChainer(c_ioh, p_ioh, 16384));
00193 proc_list = Ref<Array<ProcListItem> >(new Array<ProcListItem>(1));
00194 Ref<ProcListItem> pr_item(new ProcListItem(ch));
00195 proc_list->append(pr_item);
00196 }
00197 catch (Exception ex)
00198 {
00199 unlink(location.c_str());
00200 throw ex;
00201 }
00202 #else
00203 throw _Exception(_("MediaTomb was compiled without libcurl support,"
00204 "data proxying is not available"));
00205 #endif
00206
00207 }
00208 #ifdef SOPCAST
00209 }
00210 #endif
00211
00212 #ifdef HAVE_LIBDVDNAV
00213 if (obj->getFlag(OBJECT_FLAG_DVD_IMAGE))
00214 {
00215 strcpy(fifo_template, "mt_transcode_XXXXXX");
00216 location = normalizePath(tempName(cfg->getOption(CFG_SERVER_TMPDIR), fifo_template));
00217 log_debug("creating reader fifo: %s\n", location.c_str());
00218 if (mkfifo(location.c_str(), O_RDWR) == -1)
00219 {
00220 log_error("Failed to create fifo for the DVD image "
00221 "reading thread: %s\n", strerror(errno));
00222 throw _Exception(_("Could not create reader fifo!\n"));
00223 }
00224
00225
00226 try
00227 {
00228 String tmp = obj->getResource(0)->getParameter(DVDHandler::renderKey(DVD_Title));
00229 if (!string_ok(tmp))
00230 throw _Exception(_("DVD Image requested but title parameter is missing!"));
00231 int title = tmp.toInt();
00232 if (title < 0)
00233 throw _Exception(_("DVD Image - requested invalid title!"));
00234
00235 tmp = obj->getResource(0)->getParameter(DVDHandler::renderKey(DVD_Chapter));
00236 if (!string_ok(tmp))
00237 throw _Exception(_("DVD Image requested but chapter parameter is missing!"));
00238 int chapter = tmp.toInt();
00239 if (chapter < 0)
00240 throw _Exception(_("DVD Image - requested invalid chapter!"));
00241
00242
00243 tmp = obj->getResource(0)->getParameter(DVDHandler::renderKey(DVD_AudioStreamID));
00244 if (!string_ok(tmp))
00245 throw _Exception(_("DVD Image requested but audio track parameter is missing!"));
00246 int audio_track = tmp.toInt();
00247 if (audio_track < 0)
00248 throw _Exception(_("DVD Image - requested invalid audio stream ID!"));
00249
00250 chmod(location.c_str(), S_IWUSR | S_IRUSR);
00251
00252 Ref<IOHandler> dvd_ioh(new DVDIOHandler(obj->getLocation(), title, chapter, audio_track));
00253
00254 int from_dvd_fd[2];
00255 if (pipe(from_dvd_fd) == -1)
00256 throw _Exception(_("Failed to create DVD input pipe!"));
00257
00258 int from_remux_fd[2];
00259 if (pipe(from_remux_fd) == -1)
00260 {
00261 close(from_dvd_fd[0]);
00262 close(from_dvd_fd[1]);
00263 throw _Exception(_("Failed to create remux output pipe!"));
00264 }
00265
00266 Ref<IOHandler> fd_writer(new FDIOHandler(from_dvd_fd[1]));
00267 Ref<Executor> from_dvd(new IOHandlerChainer(dvd_ioh,
00268 fd_writer, 16384));
00269
00270 Ref<IOHandler> fd_reader(new FDIOHandler(from_remux_fd[0]));
00271
00272 Ref<MPEGRemuxProcessor> remux(new MPEGRemuxProcessor(from_dvd_fd[0],
00273 from_remux_fd[1],
00274 (unsigned char)audio_track));
00275
00276 RefCast(fd_reader, FDIOHandler)->addReference(RefCast(remux, Object));
00277 RefCast(fd_reader, FDIOHandler)->addReference(RefCast(from_dvd, Object));
00278 RefCast(fd_reader, FDIOHandler)->addReference(RefCast(fd_writer, Object));
00279 RefCast(fd_reader, FDIOHandler)->closeOther(fd_writer);
00280
00281
00282 Ref<IOHandler> p_ioh(new ProcessIOHandler(location, nil));
00283 Ref<Executor> ch(new IOHandlerChainer(fd_reader, p_ioh, 16384));
00284 proc_list = Ref<Array<ProcListItem> >(new Array<ProcListItem>(2));
00285 Ref<ProcListItem> pr_item(new ProcListItem(ch));
00286 proc_list->append(pr_item);
00287 Ref<ProcListItem> pr2_item(new ProcListItem(from_dvd));
00288 proc_list->append(pr2_item);
00289 }
00290 catch (Exception ex)
00291 {
00292 unlink(location.c_str());
00293 throw ex;
00294 }
00295 }
00296 #endif
00297
00298 String check;
00299 if (profile->getCommand().startsWith(_(_DIR_SEPARATOR)))
00300 {
00301 if (!check_path(profile->getCommand()))
00302 throw _Exception(_("Could not find transcoder: ") +
00303 profile->getCommand());
00304
00305 check = profile->getCommand();
00306 }
00307 else
00308 {
00309 check = find_in_path(profile->getCommand());
00310
00311 if (!string_ok(check))
00312 throw _Exception(_("Could not find transcoder ") +
00313 profile->getCommand() + " in $PATH");
00314
00315 }
00316
00317 int err = 0;
00318 if (!is_executable(check, &err))
00319 throw _Exception(_("Transcoder ") + profile->getCommand() +
00320 " is not executable: " + strerror(err));
00321
00322 log_debug("creating fifo: %s\n", fifo_name.c_str());
00323 if (mkfifo(fifo_name.c_str(), O_RDWR) == -1)
00324 {
00325 log_error("Failed to create fifo for the transcoding process!: %s\n", strerror(errno));
00326 throw _Exception(_("Could not create fifo!\n"));
00327 }
00328
00329 chmod(fifo_name.c_str(), S_IWUSR | S_IRUSR);
00330
00331 arglist = parseCommandLine(profile->getArguments(), location, fifo_name);
00332
00333 log_info("Arguments: %s\n", profile->getArguments().c_str());
00334 Ref<TranscodingProcessExecutor> main_proc(new TranscodingProcessExecutor(profile->getCommand(), arglist));
00335 main_proc->removeFile(fifo_name);
00336 if (isURL && (!profile->acceptURL()))
00337 {
00338 main_proc->removeFile(location);
00339 }
00340 #ifdef HAVE_LIBDVDNAV
00341 if (obj->getFlag(OBJECT_FLAG_DVD_IMAGE))
00342 {
00343 main_proc->removeFile(location);
00344 }
00345 #endif
00346 Ref<IOHandler> io_handler(new BufferedIOHandler(Ref<IOHandler> (new ProcessIOHandler(fifo_name, RefCast(main_proc, Executor), proc_list)), profile->getBufferSize(), profile->getBufferChunkSize(), profile->getBufferInitialFillSize()));
00347
00348 io_handler->open(UPNP_READ);
00349 PlayHook::getInstance()->trigger(obj);
00350 return io_handler;
00351 }
00352
00353 #endif//EXTERNAL_TRANSCODING