MythTV  0.26-pre
recorderbase.cpp
Go to the documentation of this file.
00001 #include <stdint.h>
00002 
00003 #include <algorithm> // for min
00004 using namespace std;
00005 
00006 #include "NuppelVideoRecorder.h"
00007 #include "firewirerecorder.h"
00008 #include "recordingprofile.h"
00009 #include "firewirechannel.h"
00010 #include "importrecorder.h"
00011 #include "cetonrecorder.h"
00012 #include "dummychannel.h"
00013 #include "hdhrrecorder.h"
00014 #include "iptvrecorder.h"
00015 #include "mpegrecorder.h"
00016 #include "recorderbase.h"
00017 #include "cetonchannel.h"
00018 #include "asirecorder.h"
00019 #include "dvbrecorder.h"
00020 #include "hdhrchannel.h"
00021 #include "iptvchannel.h"
00022 #include "mythlogging.h"
00023 #include "programinfo.h"
00024 #include "asichannel.h"
00025 #include "dtvchannel.h"
00026 #include "dvbchannel.h"
00027 #include "v4lchannel.h"
00028 #include "ringbuffer.h"
00029 #include "cardutil.h"
00030 #include "tv_rec.h"
00031 #include "mythmiscutil.h"
00032 
00033 #define TVREC_CARDNUM \
00034         ((tvrec != NULL) ? QString::number(tvrec->GetCaptureCardNum()) : "NULL")
00035 
00036 #define LOC      QString("RecBase(%1:%2): ") \
00037                  .arg(TVREC_CARDNUM).arg(videodevice)
00038 
00039 const uint RecorderBase::kTimeOfLatestDataIntervalTarget = 5000;
00040 
00041 RecorderBase::RecorderBase(TVRec *rec)
00042     : tvrec(rec),               ringBuffer(NULL),
00043       weMadeBuffer(true),       videocodec("rtjpeg"),
00044       ntsc(true),               ntsc_framerate(true),
00045       video_frame_rate(29.97),
00046       m_videoAspect(0),         m_videoHeight(0),
00047       m_videoWidth(0),          m_frameRate(0.0),
00048       curRecording(NULL),
00049       request_pause(false),     paused(false),
00050       request_recording(false), recording(false),
00051       nextRingBuffer(NULL),     nextRecording(NULL),
00052       positionMapType(MARK_GOP_BYFRAME)
00053 {
00054     ClearStatistics();
00055     QMutexLocker locker(avcodeclock);
00056 #if 0
00057     avcodec_init(); // init CRC's
00058 #endif
00059 }
00060 
00061 RecorderBase::~RecorderBase(void)
00062 {
00063     if (weMadeBuffer && ringBuffer)
00064     {
00065         delete ringBuffer;
00066         ringBuffer = NULL;
00067     }
00068     SetRecording(NULL);
00069 }
00070 
00071 void RecorderBase::SetRingBuffer(RingBuffer *rbuf)
00072 {
00073     if (VERBOSE_LEVEL_CHECK(VB_RECORD, LOG_INFO))
00074     {
00075         QString msg("");
00076         if (rbuf)
00077             msg = " '" + rbuf->GetFilename() + "'";
00078         LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRingBuffer(0x%1)")
00079                 .arg((uint64_t)rbuf,0,16) + msg);
00080     }
00081     ringBuffer = rbuf;
00082     weMadeBuffer = false;
00083 }
00084 
00085 void RecorderBase::SetRecording(const ProgramInfo *pginfo)
00086 {
00087     if (pginfo)
00088         LOG(VB_RECORD, LOG_INFO, LOC + QString("SetRecording(0x%1) title(%2)")
00089                 .arg((uint64_t)pginfo,0,16).arg(pginfo->GetTitle()));
00090     else
00091         LOG(VB_RECORD, LOG_INFO, LOC + "SetRecording(0x0)");
00092 
00093     ProgramInfo *oldrec = curRecording;
00094     if (pginfo)
00095         curRecording = new ProgramInfo(*pginfo);
00096     else
00097         curRecording = NULL;
00098 
00099     if (oldrec)
00100         delete oldrec;
00101 }
00102 
00103 void RecorderBase::SetOption(const QString &name, const QString &value)
00104 {
00105     if (name == "videocodec")
00106         videocodec = value;
00107     else if (name == "videodevice")
00108         videodevice = value;
00109     else if (name == "tvformat")
00110     {
00111         ntsc = false;
00112         if (value.toLower() == "ntsc" || value.toLower() == "ntsc-jp")
00113         {
00114             ntsc = true;
00115             SetFrameRate(29.97);
00116         }
00117         else if (value.toLower() == "pal-m")
00118             SetFrameRate(29.97);
00119         else if (value.toLower() == "atsc")
00120         {
00121             // Here we set the TV format values for ATSC. ATSC isn't really
00122             // NTSC, but users who configure a non-ATSC-recorder as ATSC
00123             // are far more likely to be using a mix of ATSC and NTSC than
00124             // a mix of ATSC and PAL or SECAM. The atsc recorder itself
00125             // does not care about these values, except in so much as tv_rec
00126             // cares anout video_frame_rate which should be neither 29.97
00127             // nor 25.0, but based on the actual video.
00128             ntsc = true;
00129             SetFrameRate(29.97);
00130         }
00131         else
00132             SetFrameRate(25.00);
00133     }
00134     else
00135     {
00136         LOG(VB_GENERAL, LOG_WARNING, LOC +
00137             QString("SetOption(%1,%2): Option not recognized")
00138                 .arg(name).arg(value));
00139     }
00140 }
00141 
00142 void RecorderBase::SetOption(const QString &name, int value)
00143 {
00144     LOG(VB_GENERAL, LOG_ERR, LOC +
00145         QString("SetOption(): Unknown int option: %1: %2")
00146             .arg(name).arg(value));
00147 }
00148 
00149 void RecorderBase::SetIntOption(RecordingProfile *profile, const QString &name)
00150 {
00151     const Setting *setting = profile->byName(name);
00152     if (setting)
00153         SetOption(name, setting->getValue().toInt());
00154     else
00155         LOG(VB_GENERAL, LOG_ERR, LOC + 
00156             QString("SetIntOption(...%1): Option not in profile.").arg(name));
00157 }
00158 
00159 void RecorderBase::SetStrOption(RecordingProfile *profile, const QString &name)
00160 {
00161     const Setting *setting = profile->byName(name);
00162     if (setting)
00163         SetOption(name, setting->getValue());
00164     else
00165         LOG(VB_GENERAL, LOG_ERR, LOC +
00166             QString("SetStrOption(...%1): Option not in profile.").arg(name));
00167 }
00168 
00174 void RecorderBase::StopRecording(void)
00175 {
00176     QMutexLocker locker(&pauseLock);
00177     request_recording = false;
00178     unpauseWait.wakeAll();
00179     while (recording)
00180     {
00181         recordingWait.wait(&pauseLock, 100);
00182         if (request_recording)
00183         {
00184             LOG(VB_GENERAL, LOG_ERR, LOC +
00185                 "Programmer Error: Recorder started while we were in "
00186                 "StopRecording");
00187             request_recording = false;
00188         }
00189     }
00190 }
00191 
00193 bool RecorderBase::IsRecording(void)
00194 {
00195     QMutexLocker locker(&pauseLock);
00196     return recording;
00197 }
00198 
00200 bool RecorderBase::IsRecordingRequested(void)
00201 {
00202     QMutexLocker locker(&pauseLock);
00203     return request_recording;
00204 }
00205 
00213 void RecorderBase::Pause(bool clear)
00214 {
00215     (void) clear;
00216     QMutexLocker locker(&pauseLock);
00217     request_pause = true;
00218 }
00219 
00224 void RecorderBase::Unpause(void)
00225 {
00226     QMutexLocker locker(&pauseLock);
00227     request_pause = false;
00228     unpauseWait.wakeAll();
00229 }
00230 
00232 bool RecorderBase::IsPaused(bool holding_lock) const
00233 {
00234     if (!holding_lock)
00235         pauseLock.lock();
00236     bool ret = paused;
00237     if (!holding_lock)
00238         pauseLock.unlock();
00239     return ret;
00240 }
00241 
00248 bool RecorderBase::WaitForPause(int timeout)
00249 {
00250     MythTimer t;
00251     t.start();
00252 
00253     QMutexLocker locker(&pauseLock);
00254     while (!IsPaused(true) && request_pause)
00255     {
00256         int wait = timeout - t.elapsed();
00257         if (wait <= 0)
00258             return false;
00259         pauseWait.wait(&pauseLock, wait);
00260     }
00261     return true;
00262 }
00263 
00276 bool RecorderBase::PauseAndWait(int timeout)
00277 {
00278     QMutexLocker locker(&pauseLock);
00279     if (request_pause)
00280     {
00281         if (!IsPaused(true))
00282         {
00283             paused = true;
00284             pauseWait.wakeAll();
00285             if (tvrec)
00286                 tvrec->RecorderPaused();
00287         }
00288 
00289         unpauseWait.wait(&pauseLock, timeout);
00290     }
00291 
00292     if (!request_pause && IsPaused(true))
00293     {
00294         paused = false;
00295         unpauseWait.wakeAll();
00296     }
00297 
00298     return IsPaused(true);
00299 }
00300 
00301 void RecorderBase::CheckForRingBufferSwitch(void)
00302 {
00303     nextRingBufferLock.lock();
00304 
00305     RecordingQuality *recq = NULL;
00306 
00307     if (nextRingBuffer)
00308     {
00309         FinishRecording();
00310 
00311         recq = GetRecordingQuality();
00312 
00313         ResetForNewFile();
00314 
00315         m_videoAspect = m_videoWidth = m_videoHeight = 0;
00316         m_frameRate = 0.0;
00317 
00318         SetRingBuffer(nextRingBuffer);
00319         SetRecording(nextRecording);
00320 
00321         nextRingBuffer = NULL;
00322         nextRecording = NULL;
00323 
00324         StartNewFile();
00325     }
00326     nextRingBufferLock.unlock();
00327 
00328     if (recq && tvrec)
00329         tvrec->RingBufferChanged(ringBuffer, curRecording, recq);
00330 }
00331 
00332 void RecorderBase::ClearStatistics(void)
00333 {
00334     QMutexLocker locker(&statisticsLock);
00335     timeOfFirstData = QDateTime();
00336     timeOfFirstDataIsSet.fetchAndStoreRelaxed(0);
00337     timeOfLatestData = QDateTime();
00338     timeOfLatestDataCount.fetchAndStoreRelaxed(0);
00339     timeOfLatestDataPacketInterval.fetchAndStoreRelaxed(2000);
00340     recordingGaps.clear();
00341 }
00342 
00343 RecordingQuality *RecorderBase::GetRecordingQuality(void) const
00344 {
00345     QMutexLocker locker(&statisticsLock);
00346     return new RecordingQuality(
00347         curRecording, recordingGaps,
00348         timeOfFirstData, timeOfLatestData);
00349 }
00350 
00351 int64_t RecorderBase::GetKeyframePosition(uint64_t desired) const
00352 {
00353     QMutexLocker locker(&positionMapLock);
00354     long long ret = -1;
00355 
00356     if (positionMap.empty())
00357         return ret;
00358 
00359     // find closest exact or previous keyframe position...
00360     frm_pos_map_t::const_iterator it = positionMap.lowerBound(desired);
00361     if (it == positionMap.end())
00362         ret = *positionMap.begin();
00363     else if (it.key() == desired)
00364         ret = *it;
00365     else if (--it != positionMap.end())
00366         ret = *it;
00367 
00368     return ret;
00369 }
00370 
00371 bool RecorderBase::GetKeyframePositions(
00372     int64_t start, int64_t end, frm_pos_map_t &map) const
00373 {
00374     map.clear();
00375 
00376     QMutexLocker locker(&positionMapLock);
00377     if (positionMap.empty())
00378         return true;
00379 
00380     frm_pos_map_t::const_iterator it = positionMap.lowerBound(start);
00381     end = (end < 0) ? INT64_MAX : end;
00382     for (; (it != positionMap.end()) &&
00383              (it.key() <= (uint64_t)end); ++it)
00384         map[it.key()] = *it;
00385 
00386     LOG(VB_GENERAL, LOG_INFO, LOC +
00387         QString("GetKeyframePositions(%1,%2,#%3) out of %4")
00388             .arg(start).arg(end).arg(map.size()).arg(positionMap.size()));
00389 
00390     return true;
00391 }
00392 
00400 void RecorderBase::SavePositionMap(bool force)
00401 {
00402     bool needToSave = force;
00403     positionMapLock.lock();
00404 
00405     uint delta_size = positionMapDelta.size();
00406     uint pm_elapsed = positionMapTimer.elapsed();
00407     // save on every 1.5 seconds if in the first few frames of a recording
00408     needToSave |= (positionMap.size() < 30) &&
00409         (delta_size >= 1) && (pm_elapsed >= 1500);
00410     // save every 10 seconds later on
00411     needToSave |= (delta_size >= 1) && (pm_elapsed >= 10000);
00412 
00413     if (curRecording && needToSave)
00414     {
00415         positionMapTimer.start();
00416         if (delta_size)
00417         {
00418             // copy the delta map because most times we are called it will be in
00419             // another thread and we don't want to lock the main recorder thread
00420             // which is populating the delta map
00421             frm_pos_map_t deltaCopy(positionMapDelta);
00422             positionMapDelta.clear();
00423             positionMapLock.unlock();
00424 
00425             curRecording->SavePositionMapDelta(deltaCopy, positionMapType);
00426         }
00427         else
00428         {
00429             positionMapLock.unlock();
00430         }
00431 
00432         if (ringBuffer)
00433         {
00434             curRecording->SaveFilesize(ringBuffer->GetWritePosition());
00435         }
00436     }
00437     else
00438     {
00439         positionMapLock.unlock();
00440     }
00441 }
00442 
00443 void RecorderBase::AspectChange(uint aspect, long long frame)
00444 {
00445     MarkTypes mark = MARK_ASPECT_4_3;
00446     uint customAspect = 0;
00447     if (aspect == ASPECT_1_1 || aspect >= ASPECT_CUSTOM)
00448     {
00449         if (aspect > 0x0F)
00450             customAspect = aspect;
00451         else if (m_videoWidth && m_videoHeight)
00452             customAspect = m_videoWidth * 1000000 / m_videoHeight;
00453 
00454         mark = (customAspect) ? MARK_ASPECT_CUSTOM : mark;
00455     }
00456     if (aspect == ASPECT_4_3)
00457         mark = MARK_ASPECT_4_3;
00458     if (aspect == ASPECT_16_9)
00459         mark = MARK_ASPECT_16_9;
00460     if (aspect == ASPECT_2_21_1)
00461         mark = MARK_ASPECT_2_21_1;
00462 
00463     if (curRecording)
00464         curRecording->SaveAspect(frame, mark, customAspect);
00465 }
00466 
00467 void RecorderBase::ResolutionChange(uint width, uint height, long long frame)
00468 {
00469     if (curRecording)
00470         curRecording->SaveResolution(frame, width, height);
00471 }
00472 
00473 void RecorderBase::FrameRateChange(uint framerate, long long frame)
00474 {
00475     if (curRecording)
00476         curRecording->SaveFrameRate(frame, framerate);
00477 }
00478 
00479 void RecorderBase::SetDuration(uint64_t duration)
00480 {
00481     if (curRecording)
00482         curRecording->SaveTotalDuration(duration);
00483 }
00484 
00485 void RecorderBase::SetTotalFrames(uint64_t total_frames)
00486 {
00487     if (curRecording)
00488         curRecording->SaveTotalFrames(total_frames);
00489 }
00490 
00491 
00492 RecorderBase *RecorderBase::CreateRecorder(
00493     TVRec                  *tvrec,
00494     ChannelBase            *channel,
00495     const RecordingProfile &profile,
00496     const GeneralDBOptions &genOpt,
00497     const DVBDBOptions     &dvbOpt)
00498 {
00499     if (!channel)
00500         return NULL;
00501 
00502     RecorderBase *recorder = NULL;
00503     if (genOpt.cardtype == "MPEG")
00504     {
00505 #ifdef USING_IVTV
00506         recorder = new MpegRecorder(tvrec);
00507 #endif // USING_IVTV
00508     }
00509     else if (genOpt.cardtype == "HDPVR")
00510     {
00511 #ifdef USING_HDPVR
00512         recorder = new MpegRecorder(tvrec);
00513 #endif // USING_HDPVR
00514     }
00515     else if (genOpt.cardtype == "FIREWIRE")
00516     {
00517 #ifdef USING_FIREWIRE
00518         recorder = new FirewireRecorder(
00519             tvrec, dynamic_cast<FirewireChannel*>(channel));
00520 #endif // USING_FIREWIRE
00521     }
00522     else if (genOpt.cardtype == "HDHOMERUN")
00523     {
00524 #ifdef USING_HDHOMERUN
00525         recorder = new HDHRRecorder(
00526             tvrec, dynamic_cast<HDHRChannel*>(channel));
00527         recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
00528 #endif // USING_HDHOMERUN
00529     }
00530     else if (genOpt.cardtype == "CETON")
00531     {
00532 #ifdef USING_CETON
00533         recorder = new CetonRecorder(
00534             tvrec, dynamic_cast<CetonChannel*>(channel));
00535         recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
00536 #endif // USING_CETON
00537     }
00538     else if (genOpt.cardtype == "DVB")
00539     {
00540 #ifdef USING_DVB
00541         recorder = new DVBRecorder(
00542             tvrec, dynamic_cast<DVBChannel*>(channel));
00543         recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
00544 #endif // USING_DVB
00545     }
00546     else if (genOpt.cardtype == "FREEBOX")
00547     {
00548 #ifdef USING_IPTV
00549         recorder = new IPTVRecorder(
00550             tvrec, dynamic_cast<IPTVChannel*>(channel));
00551         recorder->SetOption("mrl", genOpt.videodev);
00552 #endif // USING_IPTV
00553     }
00554     else if (genOpt.cardtype == "ASI")
00555     {
00556 #ifdef USING_ASI
00557         recorder = new ASIRecorder(
00558             tvrec, dynamic_cast<ASIChannel*>(channel));
00559         recorder->SetOption("wait_for_seqstart", genOpt.wait_for_seqstart);
00560 #endif // USING_ASI
00561     }
00562     else if (genOpt.cardtype == "IMPORT")
00563     {
00564         recorder = new ImportRecorder(tvrec);
00565     }
00566     else if (genOpt.cardtype == "DEMO")
00567     {
00568 #ifdef USING_IVTV
00569         recorder = new MpegRecorder(tvrec);
00570 #else
00571         recorder = new ImportRecorder(tvrec);
00572 #endif
00573     }
00574     else if (CardUtil::IsV4L(genOpt.cardtype))
00575     {
00576 #ifdef USING_V4L2
00577         // V4L/MJPEG/GO7007 from here on
00578         recorder = new NuppelVideoRecorder(tvrec, channel);
00579         recorder->SetOption("skipbtaudio", genOpt.skip_btaudio);
00580 #endif // USING_V4L2
00581     }
00582 
00583     if (recorder)
00584     {
00585         recorder->SetOptionsFromProfile(
00586             const_cast<RecordingProfile*>(&profile),
00587             genOpt.videodev, genOpt.audiodev, genOpt.vbidev);
00588         // Override the samplerate defined in the profile if this card
00589         // was configured with a fixed rate.
00590         if (genOpt.audiosamplerate)
00591             recorder->SetOption("samplerate", genOpt.audiosamplerate);
00592     }
00593     else
00594     {
00595         QString msg = "Need %1 recorder, but compiled without %2 support!";
00596         msg = msg.arg(genOpt.cardtype).arg(genOpt.cardtype);
00597         LOG(VB_GENERAL, LOG_ERR,
00598             "RecorderBase::CreateRecorder() Error, " + msg);
00599     }
00600 
00601     return recorder;
00602 }
00603 
00604 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends