Example #1
0
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;
}
Example #2
0
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();
        }
    }
}
Example #3
0
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());
        }
    }
}
Example #4
0
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;
}
Example #5
0
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;
}
Example #6
0
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();
}
Example #7
0
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;
}
Example #8
0
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;
}
Example #9
0
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;
}