ProgramInfo *getProgramInfoForFile(const QString &inFile) { ProgramInfo *pinfo = NULL; QString chanID, startTime; bool bIsMythRecording = false; bIsMythRecording = extractDetailsFromFilename(inFile, chanID, startTime); if (bIsMythRecording) { uint chanid = chanID.toUInt(); QDateTime recstartts = MythDate::fromString(startTime); pinfo = new ProgramInfo(chanid, recstartts); if (pinfo->GetChanID()) { pinfo->SetPathname(pinfo->GetPlaybackURL(false, true)); } else { delete pinfo; pinfo = NULL; } } if (!pinfo) { // file is not a myth recording or is no longer in the db pinfo = new ProgramInfo(inFile); LOG(VB_JOBQUEUE, LOG_NOTICE, "File is not a MythTV recording."); } else LOG(VB_JOBQUEUE, LOG_NOTICE, "File is a MythTV recording."); return pinfo; }
void RecordingSelector::updateSelectedList() { if (!m_recordingList) return; m_selectedList.clear(); ProgramInfo *p; ArchiveItem *a; for (int x = 0; x < m_archiveList->size(); x++) { a = m_archiveList->at(x); for (uint y = 0; y < m_recordingList->size(); y++) { p = m_recordingList->at(y); if (p->GetPlaybackURL(false, true) == a->filename) { if (m_selectedList.indexOf(p) == -1) m_selectedList.append(p); break; } qApp->processEvents(); } } }
void RecordingSelector::getRecordingList(void) { ProgramInfo *p; m_recordingList = RemoteGetRecordedList(true); m_categories.clear(); if (m_recordingList && m_recordingList->size() > 0) { vector<ProgramInfo *>::iterator i = m_recordingList->begin(); for ( ; i != m_recordingList->end(); i++) { p = *i; // we can't handle recordings that have to be streamed to us if (p->GetPlaybackURL(false, true).startsWith("myth://")) { VERBOSE(VB_FILE, QString("MythArchive cannot handle this file because it isn't available locally - %1") .arg(p->GetPlaybackURL(false, true))); i = m_recordingList->erase(i); i--; continue; } // ignore live tv and deleted recordings if (p->GetRecordingGroup() == "LiveTV" || p->GetRecordingGroup() == "Deleted") { i = m_recordingList->erase(i); i--; continue; } if (m_categories.indexOf(p->GetTitle()) == -1) m_categories.append(p->GetTitle()); } } }
int main(int argc, char *argv[]) { uint chanid; QDateTime starttime; QString infile, outfile; QString profilename = QString("autodetect"); QString fifodir = NULL; int jobID = -1; int jobType = JOB_NONE; int otype = REPLEX_MPEG2; bool useCutlist = false, keyframesonly = false; bool build_index = false, fifosync = false; bool mpeg2 = false; bool fifo_info = false; bool cleanCut = false; QMap<QString, QString> settingsOverride; frm_dir_map_t deleteMap; frm_pos_map_t posMap; int AudioTrackNo = -1; int found_starttime = 0; int found_chanid = 0; int found_infile = 0; int update_index = 1; int isVideo = 0; bool passthru = false; MythTranscodeCommandLineParser cmdline; if (!cmdline.Parse(argc, argv)) { cmdline.PrintHelp(); return GENERIC_EXIT_INVALID_CMDLINE; } if (cmdline.toBool("showhelp")) { cmdline.PrintHelp(); return GENERIC_EXIT_OK; } if (cmdline.toBool("showversion")) { cmdline.PrintVersion(); return GENERIC_EXIT_OK; } QCoreApplication a(argc, argv); QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHTRANSCODE); if (cmdline.toBool("outputfile")) { outfile = cmdline.toString("outputfile"); update_index = 0; } bool showprogress = cmdline.toBool("showprogress"); int retval; QString mask("general"); bool quiet = (outfile == "-") || showprogress; if ((retval = cmdline.ConfigureLogging(mask, quiet)) != GENERIC_EXIT_OK) return retval; if (cmdline.toBool("starttime")) { starttime = cmdline.toDateTime("starttime"); found_starttime = 1; } if (cmdline.toBool("chanid")) { chanid = cmdline.toUInt("chanid"); found_chanid = 1; } if (cmdline.toBool("jobid")) jobID = cmdline.toInt("jobid"); if (cmdline.toBool("inputfile")) { infile = cmdline.toString("inputfile"); found_infile = 1; } if (cmdline.toBool("video")) isVideo = true; if (cmdline.toBool("profile")) profilename = cmdline.toString("profile"); if (cmdline.toBool("usecutlist")) { useCutlist = true; if (!cmdline.toString("usecutlist").isEmpty()) { if (!cmdline.toBool("inputfile") && !cmdline.toBool("hls")) { LOG(VB_GENERAL, LOG_CRIT, "External cutlists are only allowed " "when using the --infile option."); return GENERIC_EXIT_INVALID_CMDLINE; } uint64_t last = 0, start, end; QStringList cutlist = cmdline.toStringList("usecutlist", " "); QStringList::iterator it; for (it = cutlist.begin(); it != cutlist.end(); ++it) { QStringList startend = (*it).split("-", QString::SkipEmptyParts); if (startend.size() == 2) { start = startend.first().toULongLong(); end = startend.last().toULongLong(); if (cmdline.toBool("inversecut")) { LOG(VB_GENERAL, LOG_DEBUG, QString("Cutting section %1-%2.") .arg(last).arg(start)); deleteMap[start] = MARK_CUT_END; deleteMap[end] = MARK_CUT_START; last = end; } else { LOG(VB_GENERAL, LOG_DEBUG, QString("Cutting section %1-%2.") .arg(start).arg(end)); deleteMap[start] = MARK_CUT_START; deleteMap[end] = MARK_CUT_END; } } } if (cmdline.toBool("inversecut")) { if (deleteMap.contains(0) && (deleteMap[0] == MARK_CUT_END)) deleteMap.remove(0); else deleteMap[0] = MARK_CUT_START; deleteMap[999999999] = MARK_CUT_END; LOG(VB_GENERAL, LOG_DEBUG, QString("Cutting section %1-999999999.") .arg(last)); } // sanitize cutlist if (deleteMap.count() >= 2) { frm_dir_map_t::iterator cur = deleteMap.begin(), prev; prev = cur++; while (cur != deleteMap.end()) { if (prev.value() == cur.value()) { // two of the same type next to each other QString err("Cut %1points found at %3 and %4, with no " "%2 point in between."); if (prev.value() == MARK_CUT_END) err = err.arg("end").arg("start"); else err = err.arg("start").arg("end"); LOG(VB_GENERAL, LOG_CRIT, "Invalid cutlist defined!"); LOG(VB_GENERAL, LOG_CRIT, err.arg(prev.key()) .arg(cur.key())); return GENERIC_EXIT_INVALID_CMDLINE; } else if ( (prev.value() == MARK_CUT_START) && ((cur.key() - prev.key()) < 2) ) { LOG(VB_GENERAL, LOG_WARNING, QString("Discarding " "insufficiently long cut: %1-%2") .arg(prev.key()).arg(cur.key())); prev = deleteMap.erase(prev); cur = deleteMap.erase(cur); if (cur == deleteMap.end()) continue; } prev = cur++; } } } else if (cmdline.toBool("inversecut")) { cerr << "Cutlist inversion requires an external cutlist be" << endl << "provided using the --honorcutlist option." << endl; return GENERIC_EXIT_INVALID_CMDLINE; } } if (cmdline.toBool("cleancut")) cleanCut = true; if (cmdline.toBool("allkeys")) keyframesonly = true; if (cmdline.toBool("reindex")) build_index = true; if (cmdline.toBool("fifodir")) fifodir = cmdline.toString("fifodir"); if (cmdline.toBool("fifoinfo")) fifo_info = true; if (cmdline.toBool("fifosync")) fifosync = true; if (cmdline.toBool("recopt")) recorderOptions = cmdline.toString("recopt"); if (cmdline.toBool("mpeg2")) mpeg2 = true; if (cmdline.toBool("ostream")) { if (cmdline.toString("ostream") == "dvd") otype = REPLEX_DVD; else if (cmdline.toString("ostream") == "ts") otype = REPLEX_TS_SD; else { cerr << "Invalid 'ostream' type: " << cmdline.toString("ostream").toLocal8Bit().constData() << endl; return GENERIC_EXIT_INVALID_CMDLINE; } } if (cmdline.toBool("audiotrack")) AudioTrackNo = cmdline.toInt("audiotrack"); if (cmdline.toBool("passthru")) passthru = true; CleanupGuard callCleanup(cleanup); #ifndef _WIN32 QList<int> signallist; signallist << SIGINT << SIGTERM << SIGSEGV << SIGABRT << SIGBUS << SIGFPE << SIGILL; SignalHandler handler(signallist); signal(SIGHUP, SIG_IGN); #endif // Load the context gContext = new MythContext(MYTH_BINARY_VERSION); if (!gContext->Init(false)) { LOG(VB_GENERAL, LOG_ERR, "Failed to init MythContext, exiting."); return GENERIC_EXIT_NO_MYTHCONTEXT; } MythTranslation::load("mythfrontend"); cmdline.ApplySettingsOverride(); if (jobID != -1) { if (JobQueue::GetJobInfoFromID(jobID, jobType, chanid, starttime)) { found_starttime = 1; found_chanid = 1; } else { cerr << "mythtranscode: ERROR: Unable to find DB info for " << "JobQueue ID# " << jobID << endl; return GENERIC_EXIT_NO_RECORDING_DATA; } } if (((!found_infile && !(found_chanid && found_starttime)) || (found_infile && (found_chanid || found_starttime))) && (!cmdline.toBool("hls"))) { cerr << "Must specify -i OR -c AND -s options!" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (isVideo && !found_infile && !cmdline.toBool("hls")) { cerr << "Must specify --infile to use --video" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (jobID >= 0 && (found_infile || build_index)) { cerr << "Can't specify -j with --buildindex, --video or --infile" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if ((jobID >= 0) && build_index) { cerr << "Can't specify both -j and --buildindex" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (keyframesonly && !fifodir.isEmpty()) { cerr << "Cannot specify both --fifodir and --allkeys" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (fifosync && fifodir.isEmpty()) { cerr << "Must specify --fifodir to use --fifosync" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (fifo_info && !fifodir.isEmpty()) { cerr << "Cannot specify both --fifodir and --fifoinfo" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (cleanCut && fifodir.isEmpty() && !fifo_info) { cerr << "Clean cutting works only in fifodir mode" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (cleanCut && !useCutlist) { cerr << "--cleancut is pointless without --honorcutlist" << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (fifo_info) { // Setup a dummy fifodir path, so that the "fifodir" code path // is taken. The path wont actually be used. fifodir = "DummyFifoPath"; } if (!MSqlQuery::testDBConnection()) { LOG(VB_GENERAL, LOG_ERR, "couldn't open db"); return GENERIC_EXIT_DB_ERROR; } ProgramInfo *pginfo = NULL; if (cmdline.toBool("hls")) { pginfo = new ProgramInfo(); } else if (isVideo) { // We want the absolute file path for the filemarkup table QFileInfo inf(infile); infile = inf.absoluteFilePath(); pginfo = new ProgramInfo(infile); } else if (!found_infile) { pginfo = new ProgramInfo(chanid, starttime); if (!pginfo->GetChanID()) { LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find recording for chanid %1 @ %2") .arg(chanid).arg(starttime.toString(Qt::ISODate))); delete pginfo; return GENERIC_EXIT_NO_RECORDING_DATA; } infile = pginfo->GetPlaybackURL(false, true); } else { pginfo = new ProgramInfo(infile); if (!pginfo->GetChanID()) { LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find a recording for filename '%1'") .arg(infile)); delete pginfo; return GENERIC_EXIT_NO_RECORDING_DATA; } } if (!pginfo) { LOG(VB_GENERAL, LOG_ERR, "No program info found!"); return GENERIC_EXIT_NO_RECORDING_DATA; } if (cmdline.toBool("queue")) { QString hostname = cmdline.toString("queue"); return QueueTranscodeJob(pginfo, profilename, hostname, useCutlist); } if (infile.left(7) == "myth://" && (outfile.isEmpty() || outfile != "-") && fifodir.isEmpty() && !cmdline.toBool("hls") && !cmdline.toBool("avf")) { LOG(VB_GENERAL, LOG_ERR, QString("Attempted to transcode %1. Mythtranscode is currently " "unable to transcode remote files.") .arg(infile)); return GENERIC_EXIT_REMOTE_FILE; } if (outfile.isEmpty() && !build_index && fifodir.isEmpty()) outfile = infile + ".tmp"; if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_RUNNING); Transcode *transcode = new Transcode(pginfo); if (!build_index) { if (cmdline.toBool("hlsstreamid")) LOG(VB_GENERAL, LOG_NOTICE, QString("Transcoding HTTP Live Stream ID %1") .arg(cmdline.toInt("hlsstreamid"))); else if (fifodir.isEmpty()) LOG(VB_GENERAL, LOG_NOTICE, QString("Transcoding from %1 to %2") .arg(infile).arg(outfile)); else LOG(VB_GENERAL, LOG_NOTICE, QString("Transcoding from %1 to FIFO") .arg(infile)); } if (cmdline.toBool("avf")) { transcode->SetAVFMode(); if (cmdline.toBool("container")) transcode->SetCMDContainer(cmdline.toString("container")); if (cmdline.toBool("acodec")) transcode->SetCMDAudioCodec(cmdline.toString("acodec")); if (cmdline.toBool("vcodec")) transcode->SetCMDVideoCodec(cmdline.toString("vcodec")); } else if (cmdline.toBool("hls")) { transcode->SetHLSMode(); if (cmdline.toBool("hlsstreamid")) transcode->SetHLSStreamID(cmdline.toInt("hlsstreamid")); if (cmdline.toBool("maxsegments")) transcode->SetHLSMaxSegments(cmdline.toInt("maxsegments")); if (cmdline.toBool("noaudioonly")) transcode->DisableAudioOnlyHLS(); } if (cmdline.toBool("avf") || cmdline.toBool("hls")) { if (cmdline.toBool("width")) transcode->SetCMDWidth(cmdline.toInt("width")); if (cmdline.toBool("height")) transcode->SetCMDHeight(cmdline.toInt("height")); if (cmdline.toBool("bitrate")) transcode->SetCMDBitrate(cmdline.toInt("bitrate") * 1000); if (cmdline.toBool("audiobitrate")) transcode->SetCMDAudioBitrate(cmdline.toInt("audiobitrate") * 1000); } if (showprogress) transcode->ShowProgress(true); if (!recorderOptions.isEmpty()) transcode->SetRecorderOptions(recorderOptions); int result = 0; if ((!mpeg2 && !build_index) || cmdline.toBool("hls")) { result = transcode->TranscodeFile(infile, outfile, profilename, useCutlist, (fifosync || keyframesonly), jobID, fifodir, fifo_info, cleanCut, deleteMap, AudioTrackNo, passthru); if ((result == REENCODE_OK) && (jobID >= 0)) JobQueue::ChangeJobArgs(jobID, "RENAME_TO_NUV"); } if (fifo_info) { delete transcode; return GENERIC_EXIT_OK; } int exitcode = GENERIC_EXIT_OK; if ((result == REENCODE_MPEG2TRANS) || mpeg2 || build_index) { void (*update_func)(float) = NULL; int (*check_func)() = NULL; if (useCutlist && !found_infile) pginfo->QueryCutList(deleteMap); if (jobID >= 0) { glbl_jobID = jobID; update_func = &UpdateJobQueue; check_func = &CheckJobQueue; } MPEG2fixup *m2f = new MPEG2fixup(infile, outfile, &deleteMap, NULL, false, false, 20, showprogress, otype, update_func, check_func); if (build_index) { int err = BuildKeyframeIndex(m2f, infile, posMap, jobID); if (err) return err; if (update_index) UpdatePositionMap(posMap, NULL, pginfo); else UpdatePositionMap(posMap, outfile + QString(".map"), pginfo); } else { result = m2f->Start(); if (result == REENCODE_OK) { result = BuildKeyframeIndex(m2f, outfile, posMap, jobID); if (result == REENCODE_OK) { if (update_index) UpdatePositionMap(posMap, NULL, pginfo); else UpdatePositionMap(posMap, outfile + QString(".map"), pginfo); } } } delete m2f; } if (result == REENCODE_OK) { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_STOPPING); LOG(VB_GENERAL, LOG_NOTICE, QString("%1 %2 done") .arg(build_index ? "Building Index for" : "Transcoding") .arg(infile)); } else if (result == REENCODE_CUTLIST_CHANGE) { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_RETRY); LOG(VB_GENERAL, LOG_NOTICE, QString("Transcoding %1 aborted because of cutlist update") .arg(infile)); exitcode = GENERIC_EXIT_RESTART; } else if (result == REENCODE_STOPPED) { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_ABORTING); LOG(VB_GENERAL, LOG_NOTICE, QString("Transcoding %1 stopped because of stop command") .arg(infile)); exitcode = GENERIC_EXIT_KILLED; } else { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_ERRORING); LOG(VB_GENERAL, LOG_ERR, QString("Transcoding %1 failed").arg(infile)); exitcode = result; } if (jobID >= 0) CompleteJob(jobID, pginfo, useCutlist, &deleteMap, exitcode); transcode->deleteLater(); return exitcode; }
int preview_helper(uint chanid, QDateTime starttime, long long previewFrameNumber, long long previewSeconds, const QSize &previewSize, const QString &infile, const QString &outfile) { // Lower scheduling priority, to avoid problems with recordings. if (setpriority(PRIO_PROCESS, 0, 9)) LOG(VB_GENERAL, LOG_ERR, "Setting priority failed." + ENO); if (!chanid || !starttime.isValid()) ProgramInfo::QueryKeyFromPathname(infile, chanid, starttime); ProgramInfo *pginfo = NULL; if (chanid && starttime.isValid()) { pginfo = new ProgramInfo(chanid, starttime); if (!pginfo->GetChanID()) { LOG(VB_GENERAL, LOG_ERR, QString("Cannot locate recording made on '%1' at '%2'") .arg(chanid).arg(starttime.toString(Qt::ISODate))); delete pginfo; return GENERIC_EXIT_NOT_OK; } pginfo->SetPathname(pginfo->GetPlaybackURL(false, true)); } else if (!infile.isEmpty()) { if (!QFileInfo(infile).isReadable()) { LOG(VB_GENERAL, LOG_ERR, QString("Cannot read this file '%1'").arg(infile)); return GENERIC_EXIT_NOT_OK; } pginfo = new ProgramInfo( infile, ""/*plot*/, ""/*title*/, ""/*subtitle*/, ""/*director*/, 0/*season*/, 0/*episode*/, ""/*inetref*/, 120/*length_in_minutes*/, 1895/*year*/, ""/*id*/); } else { LOG(VB_GENERAL, LOG_ERR, "Cannot locate recording to preview"); return GENERIC_EXIT_NOT_OK; } PreviewGenerator *previewgen = new PreviewGenerator( pginfo, QString(), PreviewGenerator::kLocal); if (previewFrameNumber >= 0) previewgen->SetPreviewTimeAsFrameNumber(previewFrameNumber); if (previewSeconds >= 0) previewgen->SetPreviewTimeAsSeconds(previewSeconds); previewgen->SetOutputSize(previewSize); previewgen->SetOutputFilename(outfile); bool ok = previewgen->RunReal(); previewgen->deleteLater(); delete pginfo; return (ok) ? GENERIC_EXIT_OK : GENERIC_EXIT_NOT_OK; }
void RecordingSelector::OKPressed() { // loop though selected recordings and add them to the list ProgramInfo *p; ArchiveItem *a; // remove any items that have been removed from the list QList<ArchiveItem *> tempAList; for (int x = 0; x < m_archiveList->size(); x++) { a = m_archiveList->at(x); bool found = false; for (int y = 0; y < m_selectedList.size(); y++) { p = m_selectedList.at(y); if (a->type != "Recording" || a->filename == p->GetPlaybackURL(false, true)) { found = true; break; } } if (!found) tempAList.append(a); } for (int x = 0; x < tempAList.size(); x++) m_archiveList->removeAll(tempAList.at(x)); // remove any items that are already in the list QList<ProgramInfo *> tempSList; for (int x = 0; x < m_selectedList.size(); x++) { p = m_selectedList.at(x); for (int y = 0; y < m_archiveList->size(); y++) { a = m_archiveList->at(y); if (a->filename == p->GetPlaybackURL(false, true)) { tempSList.append(p); break; } } } for (int x = 0; x < tempSList.size(); x++) m_selectedList.removeAll(tempSList.at(x)); // add all that are left for (int x = 0; x < m_selectedList.size(); x++) { p = m_selectedList.at(x); a = new ArchiveItem; a->type = "Recording"; a->title = p->GetTitle(); a->subtitle = p->GetSubtitle(); a->description = p->GetDescription(); a->startDate = p->GetScheduledStartTime().toString("dd MMM yy"); a->startTime = p->GetScheduledStartTime().toString("(hh:mm)"); a->size = p->GetFilesize(); a->filename = p->GetPlaybackURL(false, true); a->hasCutlist = p->HasCutlist(); a->useCutlist = false; a->duration = 0; a->cutDuration = 0; a->videoWidth = 0; a->videoHeight = 0; a->fileCodec = ""; a->videoCodec = ""; a->encoderProfile = NULL; a->editedDetails = false; m_archiveList->append(a); } emit haveResult(true); Close(); }
int preview_helper(const QString &_chanid, const QString &starttime, long long previewFrameNumber, long long previewSeconds, const QSize &previewSize, const QString &infile, const QString &outfile) { // Lower scheduling priority, to avoid problems with recordings. if (setpriority(PRIO_PROCESS, 0, 9)) VERBOSE(VB_GENERAL, "Setting priority failed." + ENO); uint chanid = _chanid.toUInt(); QDateTime recstartts = myth_dt_from_string(starttime); if (!chanid || !recstartts.isValid()) ProgramInfo::ExtractKeyFromPathname(infile, chanid, recstartts); ProgramInfo *pginfo = NULL; if (chanid && recstartts.isValid()) { pginfo = new ProgramInfo(chanid, recstartts); if (!pginfo->GetChanID()) { VERBOSE(VB_IMPORTANT, QString( "Cannot locate recording made on '%1' at '%2'") .arg(chanid).arg(starttime)); delete pginfo; return PREVIEWGEN_EXIT_NOT_OK; } pginfo->SetPathname(pginfo->GetPlaybackURL(false, true)); } else if (!infile.isEmpty()) { if (!QFileInfo(infile).isReadable()) { VERBOSE(VB_IMPORTANT, QString( "Cannot read this file '%1'").arg(infile)); return PREVIEWGEN_EXIT_NOT_OK; } pginfo = new ProgramInfo( infile, ""/*plot*/, ""/*title*/, ""/*subtitle*/, ""/*director*/, 0/*season*/, 0/*episode*/, 120/*length_in_minutes*/, 1895/*year*/); } else { VERBOSE(VB_IMPORTANT, "Cannot locate recording to preview"); return PREVIEWGEN_EXIT_NOT_OK; } PreviewGenerator *previewgen = new PreviewGenerator( pginfo, QString(), PreviewGenerator::kLocal); if (previewFrameNumber >= 0) previewgen->SetPreviewTimeAsFrameNumber(previewFrameNumber); if (previewSeconds >= 0) previewgen->SetPreviewTimeAsSeconds(previewSeconds); previewgen->SetOutputSize(previewSize); previewgen->SetOutputFilename(outfile); bool ok = previewgen->RunReal(); previewgen->deleteLater(); delete pginfo; return (ok) ? PREVIEWGEN_EXIT_OK : PREVIEWGEN_EXIT_NOT_OK; }
int main(int argc, char *argv[]) { uint chanid; QString starttime, infile, outfile; QString profilename = QString("autodetect"); QString fifodir = NULL; int jobID = -1; QDateTime startts; int jobType = JOB_NONE; int otype = REPLEX_MPEG2; bool useCutlist = false, keyframesonly = false; bool build_index = false, fifosync = false, showprogress = false, mpeg2 = false; QMap<QString, QString> settingsOverride; frm_dir_map_t deleteMap; frm_pos_map_t posMap; srand(time(NULL)); int AudioTrackNo = -1; QCoreApplication a(argc, argv); QCoreApplication::setApplicationName(MYTH_APPNAME_MYTHTRANSCODE); print_verbose_messages = VB_IMPORTANT; verboseString = "important"; int found_starttime = 0; int found_chanid = 0; int found_infile = 0; int update_index = 1; int isVideo = 0; bool passthru = false; for (int argpos = 1; argpos < a.argc(); ++argpos) { if (!strcmp(a.argv()[argpos],"-s") || !strcmp(a.argv()[argpos],"--starttime")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { starttime = a.argv()[argpos + 1]; found_starttime = 1; ++argpos; } else { cerr << "Missing argument to -s/--starttime option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-c") || !strcmp(a.argv()[argpos],"--chanid")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { chanid = QString(a.argv()[argpos + 1]).toUInt(); found_chanid = 1; ++argpos; } else { cerr << "Missing argument to -c/--chanid option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos], "-j")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { jobID = QString(a.argv()[++argpos]).toInt(); } else { cerr << "Missing argument to -j option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-i") || !strcmp(a.argv()[argpos],"--infile")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { infile = a.argv()[argpos + 1]; found_infile = 1; ++argpos; } else { cerr << "Missing argument to -i/--infile option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"--video")) { isVideo = 1; //mpeg2 = true; } else if (!strcmp(a.argv()[argpos],"-o") || !strcmp(a.argv()[argpos],"--outfile")) { if ((a.argc()-1 > argpos) && (a.argv()[argpos+1][0] != '-' || a.argv()[argpos+1][1] == 0x0)) { outfile = a.argv()[argpos + 1]; update_index = 0; ++argpos; } else { cerr << "Missing argument to -o/--outfile option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-V")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { QString temp = a.argv()[++argpos]; print_verbose_messages = temp.toUInt(); } else { cerr << "Missing argument to -V option\n"; return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-v") || !strcmp(a.argv()[argpos],"--verbose")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { if (parse_verbose_arg(a.argv()[argpos+1]) == GENERIC_EXIT_INVALID_CMDLINE) return GENERIC_EXIT_INVALID_CMDLINE; ++argpos; } else { cerr << "Missing argument to -v/--verbose option\n"; return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-p") || !strcmp(a.argv()[argpos],"--profile")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { profilename = a.argv()[argpos + 1]; ++argpos; } else { cerr << "Missing argument to -p/--profile option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-l") || !strcmp(a.argv()[argpos],"--honorcutlist")) { useCutlist = true; if (!found_infile) continue; if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { QStringList cutlist = QString(a.argv()[argpos + 1]) .split(" ", QString::SkipEmptyParts); ++argpos; for (QStringList::Iterator it = cutlist.begin(); it != cutlist.end(); ++it ) { QStringList startend = (*it) .split("-", QString::SkipEmptyParts); if (startend.count() == 2) { cerr << "Cutting from: " << startend.first().toULongLong() << " to: " << startend.last().toULongLong() <<"\n"; deleteMap[startend.first().toULongLong()] = MARK_CUT_START; deleteMap[startend.last().toULongLong()] = MARK_CUT_END; } } } else { cerr << "Missing argument to -l/--honorcutlist option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"--inversecut")) { useCutlist = true; if (!found_infile) { cerr << "--inversecut option can only be used with --infile\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { uint64_t last = 0; QStringList cutlist = QString(a.argv()[argpos + 1]) .split(" ", QString::SkipEmptyParts); ++argpos; deleteMap[0] = MARK_CUT_START; for (QStringList::Iterator it = cutlist.begin(); it != cutlist.end(); ++it ) { QStringList startend = (*it).split( "-", QString::SkipEmptyParts); if (startend.count() == 2) { cerr << "Cutting from: " << last << " to: " << startend.first().toULongLong() <<"\n"; deleteMap[startend.first().toULongLong()] = MARK_CUT_END; deleteMap[startend.last().toULongLong()] = MARK_CUT_START; last = startend.last().toInt(); } } cerr << "Cutting from: " << last << " to the end\n"; deleteMap[999999999] = MARK_CUT_END; } else { cerr << "Missing argument to --inversecut option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-k") || !strcmp(a.argv()[argpos],"--allkeys")) { keyframesonly = true; } else if (!strcmp(a.argv()[argpos],"-b") || !strcmp(a.argv()[argpos],"--buildindex")) { build_index = true; } else if (!strcmp(a.argv()[argpos],"-f") || !strcmp(a.argv()[argpos],"--fifodir")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { fifodir = a.argv()[argpos + 1]; ++argpos; } else { cerr << "Missing argument to -f/--fifodir option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-ro") || !strcmp(a.argv()[argpos],"--recorderOptions")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { recorderOptions = a.argv()[argpos + 1]; ++argpos; } else { cerr << "Missing argument to -ro/--recorderOptions option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"--fifosync")) { fifosync = true; } else if (!strcmp(a.argv()[argpos],"--showprogress")) { showprogress = true; } else if (!strcmp(a.argv()[argpos],"-m") || !strcmp(a.argv()[argpos],"--mpeg2")) { mpeg2 = true; } else if (!strcmp(a.argv()[argpos],"-e") || !strcmp(a.argv()[argpos],"--ostream")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { if(!strcmp(a.argv()[argpos + 1], "dvd")) otype = REPLEX_DVD; if(!strcmp(a.argv()[argpos + 1], "ts")) otype = REPLEX_TS_SD; ++argpos; } else { cerr << "Missing argument to -e/--ostream option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } else if (!strcmp(a.argv()[argpos],"-O") || !strcmp(a.argv()[argpos],"--override-setting")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { QStringList pairs = QString(a.argv()[argpos+1]).split( ",", QString::SkipEmptyParts); for (int index = 0; index < pairs.size(); ++index) { QStringList tokens = pairs[index].split( "=", QString::SkipEmptyParts); tokens[0].replace(QRegExp("^[\"']"), ""); tokens[0].replace(QRegExp("[\"']$"), ""); tokens[1].replace(QRegExp("^[\"']"), ""); tokens[1].replace(QRegExp("[\"']$"), ""); settingsOverride[tokens[0]] = tokens[1]; } } else { cerr << "Invalid or missing argument to -O/--override-setting " "option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } ++argpos; } else if (!strcmp(a.argv()[argpos],"--audiotrack")) { if (a.argc()-1 > argpos && a.argv()[argpos+1][0] != '-') { AudioTrackNo = QString(a.argv()[argpos + 1]).toInt(); } else { cerr << "Invalid or missing argument to --audiotrack " "option\n"; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } ++argpos; } else if (!strcmp(a.argv()[argpos],"-h") || !strcmp(a.argv()[argpos],"--help")) { usage(a.argv()[0]); return GENERIC_EXIT_OK; } else if (!strcmp(a.argv()[argpos],"--passthrough")) { passthru = true; } else { cerr << "Unknown option: " << a.argv()[argpos] << endl; usage(a.argv()[0]); return GENERIC_EXIT_INVALID_CMDLINE; } } if (outfile == "-") print_verbose_messages = VB_NONE; // Load the context gContext = new MythContext(MYTH_BINARY_VERSION); if (!gContext->Init(false)) { VERBOSE(VB_IMPORTANT, "Failed to init MythContext, exiting."); return GENERIC_EXIT_NO_MYTHCONTEXT; } MythTranslation::load("mythfrontend"); if (settingsOverride.size()) { QMap<QString, QString>::iterator it; for (it = settingsOverride.begin(); it != settingsOverride.end(); ++it) { VERBOSE(VB_IMPORTANT, QString("Setting '%1' being forced to '%2'") .arg(it.key()).arg(*it)); gCoreContext->OverrideSettingForSession(it.key(), *it); } } if (jobID != -1) { if (JobQueue::GetJobInfoFromID( jobID, jobType, chanid, startts)) { starttime = startts.toString(Qt::ISODate); found_starttime = 1; found_chanid = 1; } else { cerr << "mythtranscode: ERROR: Unable to find DB info for " << "JobQueue ID# " << jobID << endl; return GENERIC_EXIT_NO_RECORDING_DATA; } } if ((! found_infile && !(found_chanid && found_starttime)) || (found_infile && (found_chanid || found_starttime)) ) { cerr << "Must specify -i OR -c AND -s options!\n"; return GENERIC_EXIT_INVALID_CMDLINE; } if (isVideo && !found_infile) { cerr << "Must specify --infile to use --video\n"; return GENERIC_EXIT_INVALID_CMDLINE; } if (jobID >= 0 && (found_infile || build_index)) { cerr << "Can't specify -j with --buildindex, --video or --infile\n"; return GENERIC_EXIT_INVALID_CMDLINE; } if ((jobID >= 0) && build_index) { cerr << "Can't specify both -j and --buildindex\n"; return GENERIC_EXIT_INVALID_CMDLINE; } if (keyframesonly && !fifodir.isEmpty()) { cerr << "Cannot specify both --fifodir and --allkeys\n"; return GENERIC_EXIT_INVALID_CMDLINE; } if (fifosync && fifodir.isEmpty()) { cerr << "Must specify --fifodir to use --fifosync\n"; return GENERIC_EXIT_INVALID_CMDLINE; } VERBOSE(VB_IMPORTANT, QString("Enabled verbose msgs: %1").arg(verboseString)); if (!MSqlQuery::testDBConnection()) { printf("couldn't open db\n"); return GENERIC_EXIT_DB_ERROR; } ProgramInfo *pginfo = NULL; if (isVideo) { // We want the absolute file path for the filemarkup table QFileInfo inf(infile); infile = inf.absoluteFilePath(); pginfo = new ProgramInfo(infile); } else if (!found_infile) { QDateTime recstartts = myth_dt_from_string(starttime); pginfo = new ProgramInfo(chanid, recstartts); if (!pginfo->GetChanID()) { QString msg = QString("Couldn't find recording for chanid %1 @ %2") .arg(chanid).arg(starttime); cerr << msg.toLocal8Bit().constData() << endl; delete pginfo; return GENERIC_EXIT_NO_RECORDING_DATA; } infile = pginfo->GetPlaybackURL(false, true); } else { pginfo = new ProgramInfo(infile); if (!pginfo->GetChanID()) { VERBOSE(VB_IMPORTANT, QString("Couldn't find a recording for filename '%1'") .arg(infile)); delete pginfo; pginfo = NULL; } } if (infile.left(7) == "myth://" && (outfile.isNull() || outfile != "-")) { VERBOSE(VB_IMPORTANT, QString("Attempted to transcode %1. " "Mythtranscode is currently unable to transcode remote " "files.") .arg(infile)); return GENERIC_EXIT_REMOTE_FILE; } if (outfile.isNull() && !build_index) outfile = infile + ".tmp"; if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_RUNNING); Transcode *transcode = new Transcode(pginfo); if (!build_index) VERBOSE(VB_GENERAL, QString("Transcoding from %1 to %2") .arg(infile).arg(outfile)); if (showprogress) transcode->ShowProgress(true); if (!recorderOptions.isEmpty()) transcode->SetRecorderOptions(recorderOptions); int result = 0; if (!mpeg2 && !build_index) { result = transcode->TranscodeFile(infile, outfile, profilename, useCutlist, (fifosync || keyframesonly), jobID, fifodir, deleteMap, AudioTrackNo, passthru); if ((result == REENCODE_OK) && (jobID >= 0)) JobQueue::ChangeJobArgs(jobID, "RENAME_TO_NUV"); } int exitcode = GENERIC_EXIT_OK; if ((result == REENCODE_MPEG2TRANS) || mpeg2 || build_index) { void (*update_func)(float) = NULL; int (*check_func)() = NULL; if (useCutlist && !found_infile) pginfo->QueryCutList(deleteMap); if (jobID >= 0) { glbl_jobID = jobID; update_func = &UpdateJobQueue; check_func = &CheckJobQueue; } MPEG2fixup *m2f = new MPEG2fixup( infile, outfile, &deleteMap, NULL, false, false, 20, showprogress, otype, update_func, check_func); if (build_index) { int err = BuildKeyframeIndex(m2f, infile, posMap, jobID); if (err) return err; if (update_index) UpdatePositionMap(posMap, NULL, pginfo); else UpdatePositionMap(posMap, outfile + QString(".map"), pginfo); } else { result = m2f->Start(); if (result == REENCODE_OK) { result = BuildKeyframeIndex(m2f, outfile, posMap, jobID); if (result == REENCODE_OK) { if (update_index) UpdatePositionMap(posMap, NULL, pginfo); else UpdatePositionMap(posMap, outfile + QString(".map"), pginfo); } } } delete m2f; } if (result == REENCODE_OK) { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_STOPPING); VERBOSE(VB_GENERAL, QString("%1 %2 done") .arg(build_index ? "Building Index for" : "Transcoding") .arg(infile)); } else if (result == REENCODE_CUTLIST_CHANGE) { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_RETRY); VERBOSE(VB_GENERAL, QString("Transcoding %1 aborted because of " "cutlist update").arg(infile)); exitcode = GENERIC_EXIT_RESTART; } else if (result == REENCODE_STOPPED) { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_ABORTING); VERBOSE(VB_GENERAL, QString("Transcoding %1 stopped because of " "stop command").arg(infile)); exitcode = GENERIC_EXIT_KILLED; } else { if (jobID >= 0) JobQueue::ChangeJobStatus(jobID, JOB_ERRORING); VERBOSE(VB_GENERAL, QString("Transcoding %1 failed").arg(infile)); exitcode = result; } if (jobID >= 0) CompleteJob(jobID, pginfo, useCutlist, &deleteMap, exitcode); transcode->deleteLater(); delete gContext; return exitcode; }
static int RunCCExtract(const ProgramInfo &program_info) { QString filename = program_info.GetPlaybackURL(); if (filename.startsWith("myth://")) { QString msg = QString("Only locally accessible files are supported (%1).") .arg(program_info.GetPathname()); cerr << qPrintable(msg) << endl; return GENERIC_EXIT_INVALID_CMDLINE; } if (!QFile::exists(filename)) { cerr << qPrintable( QString("Could not open input file (%1).").arg(filename)) << endl; return GENERIC_EXIT_INVALID_CMDLINE; } RingBuffer *tmprbuf = RingBuffer::Create(filename, false); if (!tmprbuf) { cerr << qPrintable(QString("Unable to create RingBuffer for %1") .arg(filename)) << endl; return GENERIC_EXIT_PERMISSIONS_ERROR; } if (program_info.GetRecordingEndTime() > MythDate::current()) { cout << "Program will end @ " << qPrintable(program_info.GetRecordingEndTime(MythDate::ISODate)) << endl; tmprbuf->SetWaitForWrite(); } PlayerFlags flags = (PlayerFlags)(kVideoIsNull | kAudioMuted | kDecodeNoLoopFilter | kDecodeFewBlocks | kDecodeLowRes | kDecodeSingleThreaded | kDecodeNoDecode); MythCCExtractorPlayer *ccp = new MythCCExtractorPlayer(flags, true, filename); PlayerContext *ctx = new PlayerContext(kCCExtractorInUseID); ctx->SetPlayingInfo(&program_info); ctx->SetRingBuffer(tmprbuf); ctx->SetPlayer(ccp); ccp->SetPlayerInfo(NULL, NULL, ctx); if (ccp->OpenFile() < 0) { cerr << "Failed to open " << qPrintable(filename) << endl; return GENERIC_EXIT_NOT_OK; } if (!ccp->run()) { cerr << "Failed to decode " << qPrintable(filename) << endl; return GENERIC_EXIT_NOT_OK; } delete ctx; return GENERIC_EXIT_OK; }