MythTV  0.26-pre
transcode.cpp
Go to the documentation of this file.
00001 #include <fcntl.h>
00002 #include <math.h>
00003 #include <iostream>
00004 
00005 #include <QStringList>
00006 #include <QMap>
00007 #include <QRegExp>
00008 #include <QList>
00009 #include <QWaitCondition>
00010 #include <QMutex>
00011 #include <QMutexLocker>
00012 #include <QByteArray>
00013 
00014 #include "mythconfig.h"
00015 
00016 #include "transcode.h"
00017 #include "audiooutput.h"
00018 #include "recordingprofile.h"
00019 #include "mythcorecontext.h"
00020 #include "jobqueue.h"
00021 #include "exitcodes.h"
00022 #include "mthreadpool.h"
00023 #include "deletemap.h"
00024 
00025 #include "NuppelVideoRecorder.h"
00026 #include "mythplayer.h"
00027 #include "programinfo.h"
00028 #include "mythdbcon.h"
00029 #include "avformatwriter.h"
00030 #include "HLS/httplivestream.h"
00031 
00032 
00033 extern "C" {
00034 #include "libavcodec/avcodec.h"
00035 #include "libswscale/swscale.h"
00036 }
00037 
00038 using namespace std;
00039 
00040 #define LOC QString("Transcode: ")
00041 
00042 class AudioBuffer
00043 {
00044   public:
00045     AudioBuffer() : m_buffer(QByteArray()), m_time(-1) {};
00046     AudioBuffer(const AudioBuffer &old) : m_buffer(old.m_buffer),
00047         m_time(old.m_time) {};
00048 
00049     ~AudioBuffer() {};
00050 
00051     void appendData(unsigned char *buffer, int len, long long time)
00052     {
00053         m_buffer.append((const char *)buffer, len);
00054         m_time = time;
00055     }
00056 
00057     void setData(unsigned char *buffer, int len, long long time)
00058     {
00059         m_buffer.clear();
00060         appendData(buffer, len, time);
00061     }
00062 
00063     void clear(void)
00064     {
00065         m_buffer.clear();
00066         m_time = -1;
00067     }
00068 
00069     QByteArray m_buffer;
00070     long long m_time;
00071 };
00072 
00073 // This class is to act as a fake audio output device to store the data
00074 // for reencoding.
00075 class AudioReencodeBuffer : public AudioOutput
00076 {
00077   public:
00078     AudioReencodeBuffer(AudioFormat audio_format, int audio_channels,
00079                         bool passthru) :
00080         m_saveBuffer(NULL)
00081     {
00082         Reset();
00083         const AudioSettings settings(audio_format, audio_channels, 0, 0, false);
00084         Reconfigure(settings);
00085         m_initpassthru = passthru;
00086         m_audioFrameSize = 0;
00087     }
00088 
00089     ~AudioReencodeBuffer()
00090     {
00091         Reset();
00092         if (m_saveBuffer)
00093             delete m_saveBuffer;
00094     }
00095 
00096     // reconfigure sound out for new params
00097     virtual void Reconfigure(const AudioSettings &settings)
00098     {
00099         ClearError();
00100 
00101         m_passthru        = settings.use_passthru;
00102         m_channels        = settings.channels;
00103         m_bytes_per_frame = m_channels *
00104             AudioOutputSettings::SampleSize(settings.format);
00105         m_eff_audiorate   = settings.samplerate;
00106     }
00107 
00108     // dsprate is in 100 * frames/second
00109     virtual void SetEffDsp(int dsprate)
00110     {
00111         m_eff_audiorate = (dsprate / 100);
00112     }
00113 
00114     virtual void Reset(void)
00115     {
00116         QMutexLocker locker(&m_bufferMutex);
00117         for (QList<AudioBuffer *>::iterator it = m_bufferList.begin();
00118              it != m_bufferList.end(); it = m_bufferList.erase(it))
00119         {
00120             AudioBuffer *ab = *it;
00121             delete ab;
00122         }
00123     }
00124 
00125     // timecode is in milliseconds.
00126     virtual bool AddFrames(void *buffer, int frames, int64_t timecode)
00127     {
00128         return AddData(buffer, frames * m_bytes_per_frame, timecode, frames);
00129     }
00130 
00131     // timecode is in milliseconds.
00132     virtual bool AddData(void *buffer, int len, int64_t timecode, int frames)
00133     {
00134         if (len == 0)
00135             return true;
00136 
00137         QMutexLocker locker(&m_bufferMutex);
00138         AudioBuffer *ab = NULL;
00139         unsigned char *buf = (unsigned char *)buffer;
00140         int savedlen = 0;
00141         int firstlen = 0;
00142 
00143         if (m_audioFrameSize)
00144         {
00145             savedlen = (m_saveBuffer ? m_saveBuffer->m_buffer.size() : 0);
00146             if (savedlen)
00147                 firstlen = m_audioFrameSize - savedlen;
00148 
00149             int overage = (len + savedlen) % m_audioFrameSize;
00150 
00151             LOG(VB_AUDIO, LOG_DEBUG, 
00152                 QString("len: %1, savedlen: %2, overage: %3")
00153                 .arg(len).arg(savedlen).arg(overage));
00154 
00155             if (overage)
00156             {
00157                 long long newtime = timecode + ((len / m_bytes_per_frame) /
00158                                                 (m_eff_audiorate / 1000));
00159                 if (overage < len)
00160                 {
00161                     if (!m_saveBuffer)
00162                         ab = new AudioBuffer;
00163                     else
00164                         ab = m_saveBuffer;
00165                     m_saveBuffer = new AudioBuffer; 
00166                     len -= overage;
00167                     m_saveBuffer->setData(&buf[len], overage, newtime);
00168                 }
00169                 else
00170                 {
00171                     if (!m_saveBuffer)
00172                         m_saveBuffer = new AudioBuffer; 
00173                     m_saveBuffer->appendData(buf, len, newtime);
00174                     len = 0;
00175                 }
00176             }
00177             else if (savedlen)
00178             {
00179                 ab = m_saveBuffer;
00180                 m_saveBuffer = NULL;
00181             }
00182             else
00183                 ab = new AudioBuffer;
00184         }
00185         else
00186         {
00187             ab = new AudioBuffer;
00188         }
00189 
00190         if (!ab)
00191             return true;
00192 
00193         int bufflen = (m_audioFrameSize ? m_audioFrameSize : len);
00194         int index = 0;
00195 
00196         do
00197         {
00198             if (!len)
00199                 break;
00200 
00201             int blocklen = (firstlen ? firstlen : bufflen);
00202             blocklen = (blocklen > len ? len : blocklen);
00203 
00204             m_last_audiotime = timecode + ((blocklen / m_bytes_per_frame) /
00205                                            (m_eff_audiorate / 1000));
00206             ab->appendData(&buf[index], blocklen, m_last_audiotime);
00207             m_bufferList.append(ab);
00208 
00209             timecode = m_last_audiotime;
00210 
00211             LOG(VB_AUDIO, LOG_DEBUG, 
00212                 QString("blocklen: %1, index: %2, timecode: %3")
00213                 .arg(blocklen).arg(index).arg(timecode));
00214 
00215             index += blocklen;
00216             firstlen = 0;
00217 
00218             ab = new AudioBuffer;
00219         } while (index < len);
00220 
00221         // The last buffer is actually unused
00222         delete ab;
00223 
00224         return true;
00225     }
00226 
00227     AudioBuffer *GetData(void)
00228     {
00229         QMutexLocker locker(&m_bufferMutex);
00230 
00231         if (m_bufferList.isEmpty())
00232             return NULL;
00233 
00234         AudioBuffer *ab = m_bufferList.takeFirst();
00235         return ab;
00236     }
00237 
00238     int GetCount(long long time)
00239     {
00240         QMutexLocker locker(&m_bufferMutex);
00241 
00242         if (m_bufferList.isEmpty())
00243             return 0;
00244 
00245         int count = 0;
00246         for (QList<AudioBuffer *>::iterator it = m_bufferList.begin();
00247              it != m_bufferList.end(); ++it)
00248         {
00249             AudioBuffer *ab = *it;
00250 
00251             if (ab->m_time <= time)
00252                 count++;
00253             else
00254                 break;
00255         }
00256         return count;
00257     }
00258 
00259     long long GetSamples(long long time)
00260     {
00261         QMutexLocker locker(&m_bufferMutex);
00262 
00263         if (m_bufferList.isEmpty())
00264             return 0;
00265 
00266         long long samples = 0;
00267         for (QList<AudioBuffer *>::iterator it = m_bufferList.begin();
00268              it != m_bufferList.end(); ++it)
00269         {
00270             AudioBuffer *ab = *it;
00271 
00272             if (ab->m_time <= time)
00273                 samples += ab->m_buffer.size();
00274             else
00275                 break;
00276         }
00277         return samples / m_bytes_per_frame;
00278     }
00279 
00280     virtual void SetTimecode(int64_t timecode)
00281     {
00282         m_last_audiotime = timecode;
00283     }
00284     virtual bool IsPaused(void) const
00285     {
00286         return false;
00287     }
00288     virtual void Pause(bool paused)
00289     {
00290         (void)paused;
00291     }
00292     virtual void PauseUntilBuffered(void)
00293     {
00294         // Do nothing
00295     }
00296     virtual void Drain(void)
00297     {
00298         // Do nothing
00299     }
00300 
00301     virtual int64_t GetAudiotime(void)
00302     {
00303         return m_last_audiotime;
00304     }
00305 
00306     virtual int GetVolumeChannel(int) const
00307     {
00308         // Do nothing
00309         return 100;
00310     }
00311     virtual void SetVolumeChannel(int, int)
00312     {
00313         // Do nothing
00314     }
00315     virtual void SetVolumeAll(int)
00316     {
00317         // Do nothing
00318     }
00319     virtual uint GetCurrentVolume(void) const
00320     {
00321         // Do nothing
00322         return 100;
00323     }
00324     virtual void SetCurrentVolume(int)
00325     {
00326         // Do nothing
00327     }
00328     virtual void AdjustCurrentVolume(int)
00329     {
00330         // Do nothing
00331     }
00332     virtual void SetMute(bool)
00333     {
00334         // Do nothing
00335     }
00336     virtual void ToggleMute(void)
00337     {
00338         // Do nothing
00339     }
00340     virtual MuteState GetMuteState(void) const
00341     {
00342         // Do nothing
00343         return kMuteOff;
00344     }
00345     virtual MuteState IterateMutedChannels(void)
00346     {
00347         // Do nothing
00348         return kMuteOff;
00349     }
00350 
00351     virtual bool IsUpmixing(void)
00352     {
00353         // Do nothing
00354         return false;
00355     }
00356 
00357     virtual bool ToggleUpmix(void)
00358     {
00359         // Do nothing
00360         return false;
00361     }
00362 
00363     virtual bool CanUpmix(void)
00364     {
00365         // Do nothing
00366         return false;
00367     }
00368 
00369     virtual void SetSWVolume(int new_volume, bool save)
00370     {
00371         // Do nothing
00372         return;
00373     }
00374     virtual int GetSWVolume(void)
00375     {
00376         // Do nothing
00377         return 100;
00378     }
00379 
00380     //  These are pure virtual in AudioOutput, but we don't need them here
00381     virtual void bufferOutputData(bool){ return; }
00382     virtual int readOutputData(unsigned char*, int ){ return 0; }
00383 
00387     virtual bool CanPassthrough(int, int, int, int) const
00388         { return m_initpassthru; }
00389 
00390     int                 m_channels;
00391     int                 m_bits;
00392     int                 m_bytes_per_frame;
00393     int                 m_eff_audiorate;
00394     long long           m_last_audiotime;
00395     bool                m_passthru;
00396     int                 m_audioFrameSize;
00397   private:
00398     bool                m_initpassthru;
00399     QMutex              m_bufferMutex;
00400     QList<AudioBuffer *> m_bufferList;
00401     AudioBuffer         *m_saveBuffer;
00402 };
00403 
00404 // Cutter object is used in performing clean cutting. The
00405 // act of cutting is shared between the player and the
00406 // transcode loop. The player performs the initial part
00407 // of the cut by seeking, and the transcode loop handles
00408 // the remaining part by discarding data.
00409 class Cutter
00410 {
00411   private:
00412     bool          active;
00413     frm_dir_map_t foreshortenedCutList;
00414     DeleteMap     tracker;
00415     int64_t       totalFrames;
00416     int64_t       videoFramesToCut;
00417     int64_t       audioFramesToCut;
00418     float         audioFramesPerVideoFrame;
00419     enum
00420     {
00421         MAXLEADIN  = 200,
00422         MINCUT     = 20
00423     };
00424 
00425   public:
00426     Cutter() : active(false), videoFramesToCut(0), audioFramesToCut(0),
00427         audioFramesPerVideoFrame(0.0) {};
00428 
00429     void SetCutList(frm_dir_map_t &deleteMap)
00430     {
00431         // Break each cut into two parts, the first for
00432         // the player and the second for the transcode loop.
00433         frm_dir_map_t           remainingCutList;
00434         frm_dir_map_t::Iterator it;
00435         int64_t                 start = 0;
00436         int64_t                 leadinLength;
00437 
00438         foreshortenedCutList.clear();
00439 
00440         for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
00441         {
00442             switch(it.value())
00443             {
00444                 case MARK_CUT_START:
00445                     foreshortenedCutList[it.key()] = MARK_CUT_START;
00446                     start = it.key();
00447                     break;
00448 
00449                 case MARK_CUT_END:
00450                     leadinLength = min((int64_t)(it.key() - start),
00451                                        (int64_t)MAXLEADIN);
00452                     if (leadinLength >= MINCUT)
00453                     {
00454                         foreshortenedCutList[it.key() - leadinLength + 2] =
00455                             MARK_CUT_END;
00456                         remainingCutList[it.key() - leadinLength + 1] =
00457                             MARK_CUT_START;
00458                         remainingCutList[it.key()] = MARK_CUT_END;
00459                     }
00460                     else
00461                     {
00462                         // Cut too short to use new method.
00463                         foreshortenedCutList[it.key()] = MARK_CUT_END;
00464                     }
00465                     break;
00466             }
00467         }
00468 
00469         tracker.SetMap(remainingCutList);
00470     }
00471 
00472     frm_dir_map_t AdjustedCutList() const
00473     {
00474         return foreshortenedCutList;
00475     }
00476 
00477     void Activate(float v2a, int64_t total)
00478     {
00479         active = true;
00480         audioFramesPerVideoFrame = v2a;
00481         totalFrames = total;
00482         videoFramesToCut = 0;
00483         audioFramesToCut = 0;
00484         tracker.TrackerReset(0, totalFrames);
00485     }
00486 
00487     void NewFrame(int64_t currentFrame)
00488     {
00489         if (active)
00490         {
00491             if (videoFramesToCut == 0)
00492             {
00493                 uint64_t jumpTo = 0;
00494 
00495                 if (tracker.TrackerWantsToJump(currentFrame, totalFrames,
00496                                                jumpTo))
00497                 {
00498                     // Reset the tracker and work out how much video and audio
00499                     // to drop
00500                     tracker.TrackerReset(jumpTo, totalFrames);
00501                     videoFramesToCut = jumpTo - currentFrame;
00502                     audioFramesToCut += (int64_t)(videoFramesToCut *
00503                                         audioFramesPerVideoFrame + 0.5);
00504                     LOG(VB_GENERAL, LOG_INFO,
00505                         QString("Clean cut: discarding frame from %1 to %2: "
00506                                 "vid %3 aud %4")
00507                         .arg((long)currentFrame).arg((long)jumpTo)
00508                         .arg((long)videoFramesToCut)
00509                         .arg((long)audioFramesToCut));
00510                 }
00511             }
00512         }
00513     }
00514 
00515     bool InhibitUseVideoFrame()
00516     {
00517         if (videoFramesToCut == 0)
00518         {
00519             return false;
00520         }
00521         else
00522         {
00523             // We are inside a cut. Inhibit use of this frame
00524             videoFramesToCut--;
00525 
00526             if(videoFramesToCut == 0)
00527                 LOG(VB_GENERAL, LOG_INFO,
00528                     QString("Clean cut: end of video cut; audio frames left "
00529                             "to cut %1") .arg((long)audioFramesToCut));
00530 
00531             return true;
00532         }
00533     }
00534 
00535     bool InhibitUseAudioFrames(int64_t frames, long *totalAudio)
00536     {
00537         if (audioFramesToCut == 0)
00538         {
00539             return false;
00540         }
00541         else if (abs(audioFramesToCut - frames) < audioFramesToCut)
00542         {
00543             // Drop the packet containing these frames if doing
00544             // so gets us closer to zero left to drop
00545             audioFramesToCut -= frames;
00546             if(audioFramesToCut == 0)
00547                 LOG(VB_GENERAL, LOG_INFO,
00548                     QString("Clean cut: end of audio cut; vidio frames left "
00549                             "to cut %1") .arg((long)videoFramesToCut));
00550             return true;
00551         }
00552         else
00553         {
00554             // Don't drop this packet even though we still have frames to cut,
00555             // because doing so would put us further out. Instead, inflate the 
00556             // callers record of how many audio frames have been output.
00557             *totalAudio += audioFramesToCut;
00558             audioFramesToCut = 0;
00559             LOG(VB_GENERAL, LOG_INFO,
00560                 QString("Clean cut: end of audio cut; vidio frames left to "
00561                         "cut %1") .arg((long)videoFramesToCut));
00562             return false;
00563         }
00564     }
00565 
00566     bool InhibitDummyFrame()
00567     {
00568         if (audioFramesToCut > 0)
00569         {
00570             // If the cutter is in the process of dropping audio then
00571             // it is better to drop more audio rather than insert a dummy frame
00572             audioFramesToCut += (int64_t)(audioFramesPerVideoFrame + 0.5);
00573             return true;
00574         }
00575         else
00576         {
00577             return false;
00578         }
00579     }
00580 
00581     bool InhibitDropFrame()
00582     {
00583         if (audioFramesToCut > (int64_t)(audioFramesPerVideoFrame + 0.5))
00584         {
00585             // If the cutter is in the process of dropping audio and the
00586             // amount to drop is sufficient then we can drop less
00587             // audio rather than drop a frame
00588             audioFramesToCut -= (int64_t)(audioFramesPerVideoFrame + 0.5);
00589             return true;
00590         }
00591         else
00592         {
00593             return false;
00594         }
00595     }
00596 };
00597 
00598 typedef struct transcodeFrameInfo
00599 {
00600     VideoFrame *frame;
00601     int         didFF;
00602     bool        isKey;
00603 } TranscodeFrameInfo;
00604 
00605 class TranscodeFrameQueue : public QRunnable
00606 {
00607   public:
00608     TranscodeFrameQueue(MythPlayer *player, VideoOutput *videoout,
00609         bool cutlist, int size = 5)
00610       : m_player(player),         m_videoOutput(videoout),
00611         m_honorCutlist(cutlist),
00612         m_eof(false),             m_maxFrames(size),
00613         m_runThread(true),        m_isRunning(false)
00614     {
00615 
00616     }
00617 
00618     ~TranscodeFrameQueue()
00619     {
00620         m_runThread = false;
00621         m_frameWaitCond.wakeAll();
00622 
00623         while (m_isRunning)
00624             usleep(50000);
00625     }
00626 
00627     void stop(void)
00628     {
00629         m_runThread = false;
00630         m_frameWaitCond.wakeAll();
00631 
00632         while (m_isRunning)
00633             usleep(50000);
00634     }
00635 
00636     void run()
00637     {
00638         frm_dir_map_t::iterator dm_iter;
00639 
00640         m_isRunning = true;
00641         while (m_runThread)
00642         {
00643             if (m_frameList.size() < m_maxFrames && !m_eof)
00644             {
00645                 TranscodeFrameInfo tfInfo;
00646                 tfInfo.frame = NULL;
00647                 tfInfo.didFF = 0;
00648                 tfInfo.isKey = false;
00649 
00650                 if (m_player->TranscodeGetNextFrame(dm_iter, tfInfo.didFF,
00651                     tfInfo.isKey, m_honorCutlist))
00652                 {
00653                     tfInfo.frame = m_videoOutput->GetLastDecodedFrame();
00654 
00655                     QMutexLocker locker(&m_queueLock);
00656                     m_frameList.append(tfInfo);
00657                 }
00658                 else
00659                 {
00660                     m_eof = true;
00661                 }
00662 
00663                 m_frameWaitCond.wakeAll();
00664             }
00665             else
00666             {
00667                 m_frameWaitLock.lock();
00668                 m_frameWaitCond.wait(&m_frameWaitLock);
00669                 m_frameWaitLock.unlock();
00670             }
00671         }
00672         m_isRunning = false;
00673     }
00674 
00675     VideoFrame *GetFrame(int &didFF, bool &isKey)
00676     {
00677         m_queueLock.lock();
00678 
00679         if (m_frameList.isEmpty())
00680         {
00681             m_queueLock.unlock();
00682 
00683             if (m_eof)
00684                 return NULL;
00685 
00686             m_frameWaitLock.lock();
00687             m_frameWaitCond.wait(&m_frameWaitLock);
00688             m_frameWaitLock.unlock();
00689 
00690             if (m_frameList.isEmpty())
00691                 return NULL;
00692 
00693             m_queueLock.lock();
00694         }
00695 
00696         TranscodeFrameInfo tfInfo = m_frameList.takeFirst();
00697         m_queueLock.unlock();
00698         m_frameWaitCond.wakeAll();
00699 
00700         didFF = tfInfo.didFF;
00701         isKey = tfInfo.isKey;
00702 
00703         return tfInfo.frame;
00704     }
00705 
00706   private:
00707     MythPlayer               *m_player;
00708     VideoOutput              *m_videoOutput;
00709     bool                      m_honorCutlist;
00710     bool                      m_eof;
00711     int                       m_maxFrames;
00712     bool                      m_runThread;
00713     bool                      m_isRunning;
00714     QMutex                    m_queueLock;
00715     QList<TranscodeFrameInfo> m_frameList;
00716     QWaitCondition            m_frameWaitCond;
00717     QMutex                    m_frameWaitLock;
00718 };
00719 
00720 Transcode::Transcode(ProgramInfo *pginfo) :
00721     m_proginfo(pginfo),
00722     keyframedist(30),
00723     nvr(NULL),
00724     player(NULL),
00725     player_ctx(NULL),
00726     inRingBuffer(NULL),
00727     outRingBuffer(NULL),
00728     fifow(NULL),
00729     kfa_table(NULL),
00730     showprogress(false),
00731     recorderOptions(""),
00732     avfMode(false),
00733     hlsMode(false),                 hlsStreamID(-1),
00734     hlsDisableAudioOnly(false),
00735     hlsMaxSegments(0),
00736     cmdContainer("mpegts"),         cmdAudioCodec("libmp3lame"),
00737     cmdVideoCodec("libx264"),
00738     cmdWidth(480),                  cmdHeight(0),
00739     cmdBitrate(800000),             cmdAudioBitrate(64000)
00740 {
00741 }
00742 
00743 Transcode::~Transcode()
00744 {
00745     if (nvr)
00746         delete nvr;
00747     if (player_ctx)
00748     {
00749         player       = NULL;
00750         inRingBuffer = NULL;
00751         delete player_ctx;
00752     }
00753     if (outRingBuffer)
00754         delete outRingBuffer;
00755     if (fifow)
00756         delete fifow;
00757     if (kfa_table)
00758         delete kfa_table;
00759 }
00760 void Transcode::ReencoderAddKFA(long curframe, long lastkey, long num_keyframes)
00761 {
00762     long delta = curframe - lastkey;
00763     if (delta != 0 && delta != keyframedist)
00764     {
00765         struct kfatable_entry kfate;
00766         kfate.adjust = keyframedist - delta;
00767         kfate.keyframe_number = num_keyframes;
00768         kfa_table->push_back(kfate);
00769     }
00770 }
00771 
00772 bool Transcode::GetProfile(QString profileName, QString encodingType,
00773                            int height, int frameRate)
00774 {
00775     if (profileName.toLower() == "autodetect")
00776     {
00777         if (height == 1088)
00778             height = 1080;
00779 
00780         QString autoProfileName = QObject::tr("Autodetect from %1").arg(height);
00781         if (frameRate == 25 || frameRate == 30)
00782             autoProfileName += "i";
00783         if (frameRate == 50 || frameRate == 60)
00784             autoProfileName += "p";
00785 
00786         bool result = false;
00787         LOG(VB_GENERAL, LOG_NOTICE,
00788             QString("Transcode: Looking for autodetect profile: %1")
00789                 .arg(autoProfileName));
00790         result = profile.loadByGroup(autoProfileName, "Transcoders");
00791 
00792         if (!result && encodingType == "MPEG-2")
00793         {
00794             result = profile.loadByGroup("MPEG2", "Transcoders");
00795             autoProfileName = "MPEG2";
00796         }
00797         if (!result && (encodingType == "MPEG-4" || encodingType == "RTjpeg"))
00798         {
00799             result = profile.loadByGroup("RTjpeg/MPEG4",
00800                                          "Transcoders");
00801             autoProfileName = "RTjpeg/MPEG4";
00802         }
00803         if (!result)
00804         {
00805             LOG(VB_GENERAL, LOG_ERR,
00806                 QString("Transcode: Couldn't find profile for : %1")
00807                     .arg(encodingType));
00808 
00809             return false;
00810         }
00811 
00812         LOG(VB_GENERAL, LOG_NOTICE,
00813             QString("Transcode: Using autodetect profile: %1")
00814                 .arg(autoProfileName));
00815     }
00816     else
00817     {
00818         bool isNum;
00819         int profileID;
00820         profileID = profileName.toInt(&isNum);
00821         // If a bad profile is specified, there will be trouble
00822         if (isNum && profileID > 0)
00823             profile.loadByID(profileID);
00824         else if (!profile.loadByGroup(profileName, "Transcoders"))
00825         {
00826             LOG(VB_GENERAL, LOG_ERR, QString("Couldn't find profile #: %1")
00827                     .arg(profileName));
00828             return false;
00829         }
00830     }
00831     return true;
00832 }
00833 
00834 static QString get_str_option(RecordingProfile &profile, const QString &name)
00835 {
00836     const Setting *setting = profile.byName(name);
00837     if (setting)
00838         return setting->getValue();
00839 
00840     LOG(VB_GENERAL, LOG_ERR, LOC +
00841         QString("get_str_option(...%1): Option not in profile.").arg(name));
00842 
00843     return QString::null;
00844 }
00845 
00846 static int get_int_option(RecordingProfile &profile, const QString &name)
00847 {
00848     QString ret_str = get_str_option(profile, name);
00849     if (ret_str.isEmpty())
00850         return 0;
00851 
00852     bool ok = false;
00853     int ret_int = ret_str.toInt(&ok);
00854 
00855     if (!ok)
00856     {
00857         LOG(VB_GENERAL, LOG_ERR, LOC +
00858             QString("get_int_option(...%1): Option is not an int.").arg(name));
00859     }
00860 
00861     return ret_int;
00862 }
00863 
00864 static void TranscodeWriteText(void *ptr, unsigned char *buf, int len,
00865                                int timecode, int pagenr)
00866 {
00867     NuppelVideoRecorder *nvr = (NuppelVideoRecorder *)ptr;
00868     nvr->WriteText(buf, len, timecode, pagenr);
00869 }
00870 
00871 int Transcode::TranscodeFile(const QString &inputname,
00872                              const QString &outputname,
00873                              const QString &profileName,
00874                              bool honorCutList, bool framecontrol,
00875                              int jobID, QString fifodir,
00876                              bool fifo_info, bool cleanCut,
00877                              frm_dir_map_t &deleteMap,
00878                              int AudioTrackNo,
00879                              bool passthru)
00880 {
00881     QDateTime curtime = QDateTime::currentDateTime();
00882     QDateTime statustime = curtime;
00883     int audioFrame = 0;
00884     Cutter *cutter = NULL;
00885     AVFormatWriter *avfw = NULL;
00886     AVFormatWriter *avfw2 = NULL;
00887     HTTPLiveStream *hls = NULL;
00888     int hlsSegmentSize = 0;
00889     int hlsSegmentFrames = 0;
00890 
00891     if (jobID >= 0)
00892         JobQueue::ChangeJobComment(jobID, "0% " + QObject::tr("Completed"));
00893 
00894     if (hlsMode)
00895     {
00896         avfMode = true;
00897 
00898         if (hlsStreamID != -1)
00899         {
00900             hls = new HTTPLiveStream(hlsStreamID);
00901             hls->UpdateStatus(kHLSStatusStarting);
00902             cmdWidth = hls->GetWidth();
00903             cmdHeight = hls->GetHeight();
00904             cmdBitrate = hls->GetBitrate();
00905             cmdAudioBitrate = hls->GetAudioBitrate();
00906         }
00907     }
00908 
00909     if (!avfMode)
00910     {
00911         nvr = new NuppelVideoRecorder(NULL, NULL);
00912 
00913         if (!nvr)
00914         {
00915             LOG(VB_GENERAL, LOG_ERR,
00916                 "Transcoding aborted, error creating NuppelVideoRecorder.");
00917             return REENCODE_ERROR;
00918         }
00919     }
00920 
00921     // Input setup
00922     if (hls && (hlsStreamID != -1))
00923         inRingBuffer = RingBuffer::Create(hls->GetSourceFile(), false, false);
00924     else
00925         inRingBuffer = RingBuffer::Create(inputname, false, false);
00926 
00927     player = new MythPlayer(kVideoIsNull);
00928 
00929     player_ctx = new PlayerContext(kTranscoderInUseID);
00930     player_ctx->SetPlayingInfo(m_proginfo);
00931     player_ctx->SetRingBuffer(inRingBuffer);
00932     player_ctx->SetPlayer(player);
00933     player->SetPlayerInfo(NULL, NULL, player_ctx);
00934 
00935     if (showprogress)
00936     {
00937         statustime = statustime.addSecs(5);
00938     }
00939 
00940     AudioOutput *audioOutput = new AudioReencodeBuffer(FORMAT_NONE, 0,
00941                                                        passthru);
00942     AudioReencodeBuffer *arb = ((AudioReencodeBuffer*)audioOutput);
00943     player->GetAudio()->SetAudioOutput(audioOutput);
00944     player->SetTranscoding(true);
00945 
00946     if (player->OpenFile() < 0)
00947     {
00948         LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, error opening file.");
00949         if (player_ctx)
00950             delete player_ctx;
00951         return REENCODE_ERROR;
00952     }
00953 
00954     if (AudioTrackNo > -1)
00955     {
00956         LOG(VB_GENERAL, LOG_INFO,
00957             QString("Set audiotrack number to %1").arg(AudioTrackNo));
00958         player->GetDecoder()->SetTrack(kTrackTypeAudio, AudioTrackNo);
00959     }
00960 
00961     long long total_frame_count = player->GetTotalFrameCount();
00962     long long new_frame_count = total_frame_count;
00963     if (honorCutList && m_proginfo)
00964     {
00965         LOG(VB_GENERAL, LOG_INFO, "Honoring the cutlist while transcoding");
00966 
00967         frm_dir_map_t::const_iterator it;
00968         QString cutStr;
00969         long long lastStart = 0;
00970 
00971         if (deleteMap.size() == 0)
00972             m_proginfo->QueryCutList(deleteMap);
00973 
00974         for (it = deleteMap.begin(); it != deleteMap.end(); ++it)
00975         {
00976             if (*it)
00977             {
00978                 if (!cutStr.isEmpty())
00979                     cutStr += ",";
00980                 cutStr += QString("%1-").arg((long)it.key());
00981                 lastStart = it.key();
00982             }
00983             else
00984             {
00985                 if (cutStr.isEmpty())
00986                     cutStr += "0-";
00987                 cutStr += QString("%1").arg((long)it.key());
00988                 new_frame_count -= (it.key() - lastStart);
00989             }
00990         }
00991         if (cutStr.isEmpty())
00992             cutStr = "Is Empty";
00993         else if (cutStr.endsWith('-') && (total_frame_count > lastStart))
00994         {
00995             new_frame_count -= (total_frame_count - lastStart);
00996             cutStr += QString("%1").arg(total_frame_count);
00997         }
00998         LOG(VB_GENERAL, LOG_INFO, QString("Cutlist        : %1").arg(cutStr));
00999         LOG(VB_GENERAL, LOG_INFO, QString("Original Length: %1 frames")
01000                 .arg((long)total_frame_count));
01001         LOG(VB_GENERAL, LOG_INFO, QString("New Length     : %1 frames")
01002                 .arg((long)new_frame_count));
01003 
01004         if ((m_proginfo->QueryIsEditing()) ||
01005             (JobQueue::IsJobRunning(JOB_COMMFLAG, *m_proginfo)))
01006         {
01007             LOG(VB_GENERAL, LOG_INFO, "Transcoding aborted, cutlist changed");
01008             if (player_ctx)
01009                 delete player_ctx;
01010             return REENCODE_CUTLIST_CHANGE;
01011         }
01012         m_proginfo->ClearMarkupFlag(MARK_UPDATED_CUT);
01013         curtime = curtime.addSecs(60);
01014     }
01015 
01016     player->GetAudio()->ReinitAudio();
01017     QString encodingType = player->GetEncodingType();
01018     bool copyvideo = false, copyaudio = false;
01019 
01020     QString vidsetting = NULL, audsetting = NULL, vidfilters = NULL;
01021 
01022     QSize buf_size = player->GetVideoBufferSize();
01023     int video_width = buf_size.width();
01024     int video_height = buf_size.height();
01025 
01026     if (video_height == 1088) {
01027        LOG(VB_GENERAL, LOG_NOTICE,
01028            "Found video height of 1088.  This is unusual and "
01029            "more than likely the video is actually 1080 so mythtranscode "
01030            "will treat it as such.");
01031     }
01032 
01033     DecoderBase* dec = player->GetDecoder();
01034     float video_aspect = dec ? dec->GetVideoAspect() : 4.0f / 3.0f;
01035     float video_frame_rate = player->GetFrameRate();
01036     int newWidth = video_width;
01037     int newHeight = video_height;
01038     bool halfFramerate = false;
01039     bool skippedLastFrame = false;
01040 
01041     kfa_table = new vector<struct kfatable_entry>;
01042 
01043     if (avfMode)
01044     {
01045         newWidth = cmdWidth;
01046         newHeight = cmdHeight;
01047 
01048         // TODO: is this necessary?  It got commented out, but may still be
01049         // needed.
01050         // int actualHeight = (video_height == 1088 ? 1080 : video_height);
01051 
01052         // If height or width are 0, then we need to calculate them
01053         if (newHeight == 0 && newWidth > 0)
01054             newHeight = (int)(1.0 * newWidth / video_aspect);
01055         else if (newWidth == 0 && newHeight > 0)
01056             newWidth = (int)(1.0 * newHeight * video_aspect);
01057         else if (newWidth == 0 && newHeight == 0)
01058         {
01059             newHeight = 480;
01060             newWidth = (int)(1.0 * 480 * video_aspect);
01061             if (newWidth > 640)
01062             {
01063                 newWidth = 640;
01064                 newHeight = (int)(1.0 * 640 / video_aspect);
01065             }
01066         }
01067 
01068         // make sure dimensions are valid for MPEG codecs
01069         newHeight = (newHeight + 15) & ~0xF;
01070         newWidth  = (newWidth  + 15) & ~0xF;
01071 
01072         avfw = new AVFormatWriter();
01073         if (!avfw)
01074         {
01075             LOG(VB_GENERAL, LOG_ERR,
01076                 "Transcoding aborted, error creating AVFormatWriter.");
01077             if (player_ctx)
01078                 delete player_ctx;
01079             return REENCODE_ERROR;
01080         }
01081 
01082         avfw->SetVideoBitrate(cmdBitrate);
01083         avfw->SetHeight(newHeight);
01084         avfw->SetWidth(newWidth);
01085         avfw->SetAspect(video_aspect);
01086         avfw->SetAudioBitrate(cmdAudioBitrate);
01087         avfw->SetAudioChannels(arb->m_channels);
01088         avfw->SetAudioBits(16);
01089         avfw->SetAudioSampleRate(arb->m_eff_audiorate);
01090         avfw->SetAudioSampleBytes(2);
01091 
01092         if (hlsMode)
01093         {
01094             int segmentSize = 10;
01095             int audioOnlyBitrate = 0;
01096 
01097             if (!hlsDisableAudioOnly)
01098             {
01099                 audioOnlyBitrate = 48000;
01100 
01101                 avfw2 = new AVFormatWriter();
01102                 if (!avfw2)
01103                 {
01104                     LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, error "
01105                         "creating low-bitrate AVFormatWriter.");
01106                     if (player_ctx)
01107                         delete player_ctx;
01108                     return REENCODE_ERROR;
01109                 }
01110 
01111                 avfw2->SetContainer("mpegts");
01112                 avfw2->SetAudioCodec("libmp3lame");
01113                 //avfw2->SetAudioCodec("libfaac"); // --enable-libfaac to use this
01114                 avfw2->SetAudioBitrate(audioOnlyBitrate);
01115                 avfw2->SetAudioChannels(arb->m_channels);
01116                 avfw2->SetAudioBits(16);
01117                 avfw2->SetAudioSampleRate(arb->m_eff_audiorate);
01118                 avfw2->SetAudioSampleBytes(2);
01119             }
01120 
01121             avfw->SetContainer("mpegts");
01122             avfw->SetVideoCodec("libx264");
01123             avfw->SetAudioCodec("libmp3lame");
01124             //avfw->SetAudioCodec("libfaac");  // --enable-libfaac to use this
01125 
01126             if (hlsStreamID == -1)
01127                 hls = new HTTPLiveStream(inputname, newWidth, newHeight,
01128                                          cmdBitrate,
01129                                          cmdAudioBitrate, hlsMaxSegments,
01130                                          segmentSize, audioOnlyBitrate);
01131 
01132             hls->UpdateStatus(kHLSStatusStarting);
01133             hls->UpdateSizeInfo(newWidth, newHeight, video_width, video_height);
01134 
01135             if ((hlsStreamID != -1) &&
01136                 (!hls->InitForWrite()))
01137             {
01138                 LOG(VB_GENERAL, LOG_ERR, "hls->InitForWrite() failed");
01139                 if (player_ctx)
01140                     delete player_ctx;
01141                 return REENCODE_ERROR;
01142             }
01143 
01144             if (video_frame_rate > 30)
01145             {
01146                 halfFramerate = true;
01147                 avfw->SetFramerate(video_frame_rate/2);
01148 
01149                 if (avfw2)
01150                     avfw2->SetFramerate(video_frame_rate/2);
01151 
01152                 hlsSegmentSize = (int)(segmentSize * video_frame_rate / 2);
01153             }
01154             else
01155             {
01156                 avfw->SetFramerate(video_frame_rate);
01157 
01158                 if (avfw2)
01159                     avfw2->SetFramerate(video_frame_rate);
01160 
01161                 hlsSegmentSize = (int)(segmentSize * video_frame_rate);
01162             }
01163 
01164             avfw->SetKeyFrameDist(90);
01165             if (avfw2)
01166                 avfw2->SetKeyFrameDist(90);
01167 
01168             hls->AddSegment();
01169             avfw->SetFilename(hls->GetCurrentFilename());
01170             if (avfw2)
01171                 avfw2->SetFilename(hls->GetCurrentFilename(true));
01172         }
01173         else
01174         {
01175             avfw->SetContainer(cmdContainer);
01176             avfw->SetVideoCodec(cmdVideoCodec);
01177             avfw->SetAudioCodec(cmdAudioCodec);
01178             avfw->SetFilename(outputname);
01179             avfw->SetFramerate(video_frame_rate);
01180         }
01181 
01182         avfw->SetThreadCount(
01183             gCoreContext->GetNumSetting("HTTPLiveStreamThreads", 2));
01184 
01185         if (avfw2)
01186             avfw2->SetThreadCount(1);
01187 
01188         if (!avfw->Init())
01189         {
01190             LOG(VB_GENERAL, LOG_ERR, "avfw->Init() failed");
01191             if (player_ctx)
01192                 delete player_ctx;
01193             return REENCODE_ERROR;
01194         }
01195 
01196         if (!avfw->OpenFile())
01197         {
01198             LOG(VB_GENERAL, LOG_ERR, "avfw->OpenFile() failed");
01199             if (player_ctx)
01200                 delete player_ctx;
01201             return REENCODE_ERROR;
01202         }
01203 
01204         if (avfw2 && !avfw2->Init())
01205         {
01206             LOG(VB_GENERAL, LOG_ERR, "avfw2->Init() failed");
01207             if (player_ctx)
01208                 delete player_ctx;
01209             return REENCODE_ERROR;
01210         }
01211 
01212         if (avfw2 && !avfw2->OpenFile())
01213         {
01214             LOG(VB_GENERAL, LOG_ERR, "avfw2->OpenFile() failed");
01215             if (player_ctx)
01216                 delete player_ctx;
01217             return REENCODE_ERROR;
01218         }
01219 
01220         arb->m_audioFrameSize = avfw->GetAudioFrameSize() * arb->m_channels * 2;
01221 
01222         player->SetVideoFilters(
01223             gCoreContext->GetSetting("HTTPLiveStreamFilters"));
01224     }
01225     else if (fifodir.isEmpty())
01226     {
01227         if (!GetProfile(profileName, encodingType, video_height,
01228                         (int)round(video_frame_rate))) {
01229             LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, no profile found.");
01230             if (player_ctx)
01231                 delete player_ctx;
01232             return REENCODE_ERROR;
01233         }
01234 
01235         // For overriding settings on the command line
01236         QMap<QString, QString> recorderOptionsMap;
01237         if (!recorderOptions.isEmpty())
01238         {
01239             QStringList options = recorderOptions
01240                 .split(",", QString::SkipEmptyParts);
01241             int loop = 0;
01242             while (loop < options.size())
01243             {
01244                 QStringList tokens = options[loop].split("=");
01245                 recorderOptionsMap[tokens[0]] = tokens[1];
01246 
01247                 loop++;
01248             }
01249         }
01250 
01251         vidsetting = get_str_option(profile, "videocodec");
01252         audsetting = get_str_option(profile, "audiocodec");
01253         vidfilters = get_str_option(profile, "transcodefilters");
01254 
01255         if (encodingType == "MPEG-2" &&
01256             get_int_option(profile, "transcodelossless"))
01257         {
01258             LOG(VB_GENERAL, LOG_NOTICE, "Switching to MPEG-2 transcoder.");
01259             if (player_ctx)
01260                 delete player_ctx;
01261             return REENCODE_MPEG2TRANS;
01262         }
01263 
01264         // Recorder setup
01265         if (get_int_option(profile, "transcodelossless"))
01266         {
01267             vidsetting = encodingType;
01268             audsetting = "MP3";
01269         }
01270         else if (get_int_option(profile, "transcoderesize"))
01271         {
01272             int actualHeight = (video_height == 1088 ? 1080 : video_height);
01273 
01274             player->SetVideoFilters(vidfilters);
01275             newWidth = get_int_option(profile, "width");
01276             newHeight = get_int_option(profile, "height");
01277 
01278             // If height or width are 0, then we need to calculate them
01279             if (newHeight == 0 && newWidth > 0)
01280                 newHeight = (int)(1.0 * newWidth * actualHeight / video_width);
01281             else if (newWidth == 0 && newHeight > 0)
01282                 newWidth = (int)(1.0 * newHeight * video_width / actualHeight);
01283             else if (newWidth == 0 && newHeight == 0)
01284             {
01285                 newHeight = 480;
01286                 newWidth = (int)(1.0 * 480 * video_width / actualHeight);
01287                 if (newWidth > 640)
01288                 {
01289                     newWidth = 640;
01290                     newHeight = (int)(1.0 * 640 * actualHeight / video_width);
01291                 }
01292             }
01293 
01294             if (encodingType.left(4).toLower() == "mpeg")
01295             {
01296                 // make sure dimensions are valid for MPEG codecs
01297                 newHeight = (newHeight + 15) & ~0xF;
01298                 newWidth  = (newWidth  + 15) & ~0xF;
01299             }
01300 
01301             LOG(VB_GENERAL, LOG_INFO, QString("Resizing from %1x%2 to %3x%4")
01302                     .arg(video_width).arg(video_height)
01303                     .arg(newWidth).arg(newHeight));
01304         }
01305         else  // lossy and no resize
01306             player->SetVideoFilters(vidfilters);
01307 
01308         // this is ripped from tv_rec SetupRecording. It'd be nice to merge
01309         nvr->SetOption("inpixfmt", FMT_YV12);
01310 
01311         nvr->SetOption("width", newWidth);
01312         nvr->SetOption("height", newHeight);
01313 
01314         nvr->SetOption("tvformat", gCoreContext->GetSetting("TVFormat"));
01315         nvr->SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat"));
01316 
01317         nvr->SetFrameRate(video_frame_rate);
01318         nvr->SetVideoAspect(video_aspect);
01319         nvr->SetTranscoding(true);
01320 
01321         if ((vidsetting == "MPEG-4") ||
01322             (recorderOptionsMap["videocodec"] == "mpeg4"))
01323         {
01324             nvr->SetOption("videocodec", "mpeg4");
01325 
01326             nvr->SetIntOption(&profile, "mpeg4bitrate");
01327             nvr->SetIntOption(&profile, "scalebitrate");
01328             nvr->SetIntOption(&profile, "mpeg4maxquality");
01329             nvr->SetIntOption(&profile, "mpeg4minquality");
01330             nvr->SetIntOption(&profile, "mpeg4qualdiff");
01331             nvr->SetIntOption(&profile, "mpeg4optionvhq");
01332             nvr->SetIntOption(&profile, "mpeg4option4mv");
01333 #ifdef USING_FFMPEG_THREADS
01334             nvr->SetIntOption(profile, "encodingthreadcount");
01335 #endif
01336         }
01337         else if ((vidsetting == "MPEG-2") ||
01338                  (recorderOptionsMap["videocodec"] == "mpeg2video"))
01339         {
01340             nvr->SetOption("videocodec", "mpeg2video");
01341 
01342             nvr->SetIntOption(&profile, "mpeg2bitrate");
01343             nvr->SetIntOption(&profile, "scalebitrate");
01344 #ifdef USING_FFMPEG_THREADS
01345             nvr->SetIntOption(&profile, "encodingthreadcount");
01346 #endif
01347         }
01348         else if ((vidsetting == "RTjpeg") ||
01349                  (recorderOptionsMap["videocodec"] == "rtjpeg"))
01350         {
01351             nvr->SetOption("videocodec", "rtjpeg");
01352             nvr->SetIntOption(&profile, "rtjpegquality");
01353             nvr->SetIntOption(&profile, "rtjpegchromafilter");
01354             nvr->SetIntOption(&profile, "rtjpeglumafilter");
01355         }
01356         else if (vidsetting.isEmpty())
01357         {
01358             LOG(VB_GENERAL, LOG_ERR, "No video information found!");
01359             LOG(VB_GENERAL, LOG_ERR, "Please ensure that recording profiles "
01360                                      "for the transcoder are set");
01361             if (player_ctx)
01362                 delete player_ctx;
01363             return REENCODE_ERROR;
01364         }
01365         else
01366         {
01367             LOG(VB_GENERAL, LOG_ERR,
01368                 QString("Unknown video codec: %1").arg(vidsetting));
01369             if (player_ctx)
01370                 delete player_ctx;
01371             return REENCODE_ERROR;
01372         }
01373 
01374         nvr->SetOption("samplerate", arb->m_eff_audiorate);
01375         if (audsetting == "MP3")
01376         {
01377             nvr->SetOption("audiocompression", 1);
01378             nvr->SetIntOption(&profile, "mp3quality");
01379             copyaudio = true;
01380         }
01381         else if (audsetting == "Uncompressed")
01382         {
01383             nvr->SetOption("audiocompression", 0);
01384         }
01385         else
01386         {
01387             LOG(VB_GENERAL, LOG_ERR,
01388                 QString("Unknown audio codec: %1").arg(audsetting));
01389         }
01390 
01391         nvr->AudioInit(true);
01392 
01393         // For overriding settings on the command line
01394         if (recorderOptionsMap.size() > 0)
01395         {
01396             QMap<QString, QString>::Iterator it;
01397             QString key, value;
01398             for (it = recorderOptionsMap.begin();
01399                  it != recorderOptionsMap.end(); ++it)
01400             {
01401                 key   = it.key();
01402                 value = *it;
01403 
01404                 LOG(VB_GENERAL, LOG_NOTICE,
01405                     QString("Forcing Recorder option '%1' to '%2'")
01406                         .arg(key).arg(value));
01407 
01408                 if (value.contains(QRegExp("[^0-9]")))
01409                     nvr->SetOption(key, value);
01410                 else
01411                     nvr->SetOption(key, value.toInt());
01412 
01413                 if (key == "width")
01414                     newWidth  = (value.toInt() + 15) & ~0xF;
01415                 else if (key == "height")
01416                     newHeight = (value.toInt() + 15) & ~0xF;
01417                 else if (key == "videocodec")
01418                 {
01419                     if (value == "mpeg4")
01420                         vidsetting = "MPEG-4";
01421                     else if (value == "mpeg2video")
01422                         vidsetting = "MPEG-2";
01423                     else if (value == "rtjpeg")
01424                         vidsetting = "RTjpeg";
01425                 }
01426             }
01427         }
01428 
01429         if ((vidsetting == "MPEG-4") ||
01430             (vidsetting == "MPEG-2"))
01431             nvr->SetupAVCodecVideo();
01432         else if (vidsetting == "RTjpeg")
01433             nvr->SetupRTjpeg();
01434 
01435         outRingBuffer = RingBuffer::Create(outputname, true, false);
01436         nvr->SetRingBuffer(outRingBuffer);
01437         nvr->WriteHeader();
01438         nvr->StreamAllocate();
01439     }
01440 
01441     if (vidsetting == encodingType && !framecontrol && !avfMode &&
01442         fifodir.isEmpty() && honorCutList &&
01443         video_width == newWidth && video_height == newHeight)
01444     {
01445         copyvideo = true;
01446         LOG(VB_GENERAL, LOG_INFO, "Reencoding video in 'raw' mode");
01447     }
01448 
01449     if (honorCutList && deleteMap.size() > 0)
01450     {
01451         if (cleanCut)
01452         {
01453             // Have the player seek only part of the way
01454             // through a cut, and then use the cutter to
01455             // discard the rest
01456             cutter = new Cutter();
01457             cutter->SetCutList(deleteMap);
01458 
01459             player->SetCutList(cutter->AdjustedCutList());
01460         }
01461         else
01462         {
01463             // Have the player apply the cut list
01464             player->SetCutList(deleteMap);
01465         }
01466     }
01467 
01468     player->InitForTranscode(copyaudio, copyvideo);
01469     if (player->IsErrored())
01470     {
01471         LOG(VB_GENERAL, LOG_ERR,
01472             "Unable to initialize MythPlayer for Transcode");
01473         if (player_ctx)
01474             delete player_ctx;
01475         return REENCODE_ERROR;
01476     }
01477 
01478     int vidSize = 0;
01479 
01480     // 1920x1080 video is actually 1920x1088 because of the 16x16 blocks so
01481     // we have to fudge the output size here.  nuvexport knows how to handle
01482     // this and as of right now it is the only app that uses the fifo ability.
01483     if (video_height == 1080 && video_width == 1920)
01484         vidSize = (1088 * 1920) * 3 / 2;
01485     else
01486         vidSize = (video_height * video_width) * 3 / 2;
01487 
01488     VideoFrame frame;
01489     frame.codec = FMT_YV12;
01490     frame.width = newWidth;
01491     frame.height = newHeight;
01492     frame.size = newWidth * newHeight * 3 / 2;
01493 
01494     if (!fifodir.isEmpty())
01495     {
01496         AudioPlayer *aplayer = player->GetAudio();
01497         const char  *audio_codec_name;
01498 
01499         switch(aplayer->GetCodec())
01500         {
01501             case CODEC_ID_AC3:
01502                 audio_codec_name = "ac3";
01503                 break;
01504             case CODEC_ID_EAC3:
01505                 audio_codec_name = "eac3";
01506                 break;
01507             case CODEC_ID_DTS:
01508                 audio_codec_name = "dts";
01509                 break;
01510             case CODEC_ID_TRUEHD:
01511                 audio_codec_name = "truehd";
01512                 break;
01513             case CODEC_ID_MP3:
01514                 audio_codec_name = "mp3";
01515                 break;
01516             case CODEC_ID_MP2:
01517                 audio_codec_name = "mp2";
01518                 break;
01519             default:
01520                 audio_codec_name = "unknown";
01521         }
01522 
01523         if (!arb->m_passthru)
01524             audio_codec_name = "raw";
01525 
01526         // If cutlist is used then get info on first uncut frame
01527         if (honorCutList && fifo_info)
01528         {
01529             bool is_key;
01530             int did_ff;
01531             frm_dir_map_t::iterator dm_iter;
01532             player->TranscodeGetNextFrame(dm_iter, did_ff, is_key, true);
01533 
01534             QSize buf_size = player->GetVideoBufferSize();
01535             video_width = buf_size.width();
01536             video_height = buf_size.height();
01537             video_aspect = player->GetVideoAspect();
01538             video_frame_rate = player->GetFrameRate();
01539         }
01540 
01541         // Display details of the format of the fifo data.
01542         LOG(VB_GENERAL, LOG_INFO,
01543             QString("FifoVideoWidth %1").arg(video_width));
01544         LOG(VB_GENERAL, LOG_INFO,
01545             QString("FifoVideoHeight %1").arg(video_height));
01546         LOG(VB_GENERAL, LOG_INFO,
01547             QString("FifoVideoAspectRatio %1").arg(video_aspect));
01548         LOG(VB_GENERAL, LOG_INFO,
01549             QString("FifoVideoFrameRate %1").arg(video_frame_rate));
01550         LOG(VB_GENERAL, LOG_INFO,
01551             QString("FifoAudioFormat %1").arg(audio_codec_name));
01552         LOG(VB_GENERAL, LOG_INFO,
01553             QString("FifoAudioChannels %1").arg(arb->m_channels));
01554         LOG(VB_GENERAL, LOG_INFO,
01555             QString("FifoAudioSampleRate %1").arg(arb->m_eff_audiorate));
01556 
01557         if(fifo_info)
01558         {
01559             // Request was for just the format of fifo data, not for
01560             // the actual transcode, so stop here.
01561             unlink(outputname.toLocal8Bit().constData());
01562             if (player_ctx)
01563                 delete player_ctx;
01564             return REENCODE_OK;
01565         }
01566 
01567         QString audfifo = fifodir + QString("/audout");
01568         QString vidfifo = fifodir + QString("/vidout");
01569         int audio_size = arb->m_eff_audiorate * arb->m_bytes_per_frame;
01570         // framecontrol is true if we want to enforce fifo sync.
01571         if (framecontrol)
01572             LOG(VB_GENERAL, LOG_INFO, "Enforcing sync on fifos");
01573         fifow = new FIFOWriter(2, framecontrol);
01574 
01575         if (!fifow->FIFOInit(0, QString("video"), vidfifo, vidSize, 50) ||
01576             !fifow->FIFOInit(1, QString("audio"), audfifo, audio_size, 25))
01577         {
01578             LOG(VB_GENERAL, LOG_ERR,
01579                 "Error initializing fifo writer.  Aborting");
01580             unlink(outputname.toLocal8Bit().constData());
01581             if (player_ctx)
01582                 delete player_ctx;
01583             return REENCODE_ERROR;
01584         }
01585         LOG(VB_GENERAL, LOG_INFO,
01586             QString("Video %1x%2@%3fps Audio rate: %4")
01587                 .arg(video_width).arg(video_height)
01588                 .arg(video_frame_rate)
01589                 .arg(arb->m_eff_audiorate));
01590         LOG(VB_GENERAL, LOG_INFO, "Created fifos. Waiting for connection.");
01591     }
01592 
01593     bool forceKeyFrames = (fifow == NULL) ? framecontrol : false;
01594 
01595     frm_dir_map_t::iterator dm_iter;
01596     bool writekeyframe = true;
01597 
01598     int num_keyframes = 0;
01599 
01600     int did_ff = 0;
01601 
01602     long curFrameNum = 0;
01603     frame.frameNumber = 1;
01604     long lastKeyFrame = 0;
01605     long totalAudio = 0;
01606     int dropvideo = 0;
01607     long long lasttimecode = 0;
01608     long long timecodeOffset = 0;
01609 
01610     float rateTimeConv = arb->m_eff_audiorate / 1000.0f;
01611     float vidFrameTime = 1000.0f / video_frame_rate;
01612     int wait_recover = 0;
01613     VideoOutput *videoOutput = player->GetVideoOutput();
01614     bool is_key = 0;
01615     bool first_loop = true;
01616     unsigned char *newFrame = new unsigned char[frame.size];
01617     frame.buf = newFrame;
01618     AVPicture imageIn, imageOut;
01619     struct SwsContext  *scontext = NULL;
01620 
01621     if (fifow)
01622         LOG(VB_GENERAL, LOG_INFO, "Dumping Video and Audio data to fifos");
01623     else if (copyaudio)
01624         LOG(VB_GENERAL, LOG_INFO, "Copying Audio while transcoding Video");
01625     else if (hlsMode)
01626         LOG(VB_GENERAL, LOG_INFO, "Transcoding for HTTP Live Streaming");
01627     else if (avfMode)
01628         LOG(VB_GENERAL, LOG_INFO, "Transcoding to libavformat container");
01629     else
01630         LOG(VB_GENERAL, LOG_INFO, "Transcoding Video and Audio");
01631 
01632     TranscodeFrameQueue *frameQueue =
01633         new TranscodeFrameQueue(player, videoOutput, honorCutList);
01634     MThreadPool::globalInstance()->start(frameQueue, "TranscodeFrameQueue");
01635 
01636     QTime flagTime;
01637     flagTime.start();
01638 
01639     if (cutter)
01640         cutter->Activate(vidFrameTime * rateTimeConv, total_frame_count);
01641 
01642     bool stopSignalled = false;
01643     VideoFrame *lastDecode = NULL;
01644 
01645     if (hls)
01646         hls->UpdateStatus(kHLSStatusRunning);
01647 
01648     while ((!stopSignalled) &&
01649            (lastDecode = frameQueue->GetFrame(did_ff, is_key)))
01650     {
01651         if (first_loop)
01652         {
01653             copyaudio = player->GetRawAudioState();
01654             first_loop = false;
01655         }
01656 
01657         float new_aspect = lastDecode->aspect;
01658 
01659         if (cutter)
01660             cutter->NewFrame(lastDecode->frameNumber);
01661 
01662         frame.timecode = lastDecode->timecode;
01663 
01664         if (frame.timecode < lasttimecode)
01665             frame.timecode = (long long)(lasttimecode + vidFrameTime);
01666 
01667         if (fifow)
01668         {
01669             frame.buf = lastDecode->buf;
01670             totalAudio += arb->GetSamples(frame.timecode);
01671             int audbufTime = (int)(totalAudio / rateTimeConv);
01672             int auddelta = frame.timecode - audbufTime;
01673             int vidTime = (int)(curFrameNum * vidFrameTime + 0.5);
01674             int viddelta = frame.timecode - vidTime;
01675             int delta = viddelta - auddelta;
01676             if (abs(delta) < 500 && abs(delta) >= vidFrameTime)
01677             {
01678                QString msg = QString("Audio is %1ms %2 video at # %3: "
01679                                      "auddelta=%4, viddelta=%5")
01680                    .arg(abs(delta))
01681                    .arg(((delta > 0) ? "ahead of" : "behind"))
01682                    .arg((int)curFrameNum)
01683                    .arg(auddelta)
01684                    .arg(viddelta);
01685                 LOG(VB_GENERAL, LOG_INFO, msg);
01686                 dropvideo = (delta > 0) ? 1 : -1;
01687                 wait_recover = 0;
01688             }
01689             else if (delta >= 500 && delta < 10000)
01690             {
01691                 if (wait_recover == 0)
01692                 {
01693                     dropvideo = 5;
01694                     wait_recover = 6;
01695                 }
01696                 else if (wait_recover == 1)
01697                 {
01698                     // Video is badly lagging.  Try to catch up.
01699                     int count = 0;
01700                     while (delta > vidFrameTime)
01701                     {
01702                         if (!cutter || !cutter->InhibitDummyFrame())
01703                             fifow->FIFOWrite(0, frame.buf, vidSize);
01704 
01705                         count++;
01706                         delta -= (int)vidFrameTime;
01707                     }
01708                     QString msg = QString("Added %1 blank video frames")
01709                                   .arg(count);
01710                     LOG(VB_GENERAL, LOG_INFO, msg);
01711                     curFrameNum += count;
01712                     dropvideo = 0;
01713                     wait_recover = 0;
01714                 }
01715                 else
01716                     wait_recover--;
01717             }
01718             else
01719             {
01720                 dropvideo = 0;
01721                 wait_recover = 0;
01722             }
01723 
01724 #if 0
01725             int buflen = (int)(arb->audiobuffer_len / rateTimeConv);
01726             LOG(VB_GENERAL, LOG_DEBUG,
01727                 QString("%1: video time: %2 audio time: %3 "
01728                         "buf: %4 exp: %5 delta: %6")
01729                     .arg(curFrameNum) .arg(frame.timecode)
01730                     .arg(arb->last_audiotime) .arg(buflen) .arg(audbufTime)
01731                     .arg(delta));
01732 #endif
01733             while (arb->GetCount(frame.timecode))
01734             {
01735                 AudioBuffer *ab = arb->GetData();
01736 
01737                 if (!cutter ||
01738                     !cutter->InhibitUseAudioFrames(1, &totalAudio))
01739                     fifow->FIFOWrite(1, ab->m_buffer.data(),
01740                                      ab->m_buffer.size());
01741 
01742                 delete ab;
01743             }
01744 
01745             if (dropvideo < 0)
01746             {
01747                 if (cutter && cutter->InhibitDropFrame())
01748                     fifow->FIFOWrite(0, frame.buf, vidSize);
01749 
01750                 LOG(VB_GENERAL, LOG_INFO, "Dropping video frame");
01751                 dropvideo++;
01752                 curFrameNum--;
01753             }
01754             else
01755             {
01756                 if (!cutter || !cutter->InhibitUseVideoFrame())
01757                     fifow->FIFOWrite(0, frame.buf, vidSize);
01758 
01759                 if (dropvideo)
01760                 {
01761                     if (!cutter || !cutter->InhibitDummyFrame())
01762                         fifow->FIFOWrite(0, frame.buf, vidSize);
01763 
01764                     curFrameNum++;
01765                     dropvideo--;
01766                 }
01767             }
01768             videoOutput->DoneDisplayingFrame(lastDecode);
01769             player->GetCC608Reader()->FlushTxtBuffers();
01770             lasttimecode = frame.timecode;
01771         }
01772         else if (copyaudio)
01773         {
01774             // Encoding from NuppelVideo to NuppelVideo with MP3 audio
01775             // So let's not decode/reencode audio
01776             if (!player->GetRawAudioState())
01777             {
01778                 // The Raw state changed during decode.  This is not good
01779                 LOG(VB_GENERAL, LOG_ERR, "Transcoding aborted, MythPlayer "
01780                                          "is not in raw audio mode.");
01781 
01782                 unlink(outputname.toLocal8Bit().constData());
01783                 delete [] newFrame;
01784                 if (player_ctx)
01785                     delete player_ctx;
01786                 if (frameQueue)
01787                     frameQueue->stop();
01788                 return REENCODE_ERROR;
01789             }
01790 
01791             if (forceKeyFrames)
01792                 writekeyframe = true;
01793             else
01794             {
01795                 writekeyframe = is_key;
01796                 if (writekeyframe)
01797                 {
01798                     // Currently, we don't create new sync frames,
01799                     // (though we do create new 'I' frames), so we mark
01800                     // the key-frames before deciding whether we need a
01801                     // new 'I' frame.
01802 
01803                     //need to correct the frame# and timecode here
01804                     // Question:  Is it necessary to change the timecodes?
01805                     long sync_offset;
01806                     sync_offset = player->UpdateStoredFrameNum(curFrameNum);
01807                     nvr->UpdateSeekTable(num_keyframes, sync_offset);
01808                     ReencoderAddKFA(curFrameNum, lastKeyFrame, num_keyframes);
01809                     num_keyframes++;
01810                     lastKeyFrame = curFrameNum;
01811 
01812                     if (did_ff)
01813                         did_ff = 0;
01814                 }
01815             }
01816 
01817             if (did_ff == 1)
01818             {
01819                 timecodeOffset +=
01820                     (frame.timecode - lasttimecode - (int)vidFrameTime);
01821             }
01822             lasttimecode = frame.timecode;
01823             frame.timecode -= timecodeOffset;
01824 
01825             if (! player->WriteStoredData(outRingBuffer, (did_ff == 0),
01826                                        timecodeOffset))
01827             {
01828                 if (video_aspect != new_aspect)
01829                 {
01830                     video_aspect = new_aspect;
01831                     nvr->SetNewVideoParams(video_aspect);
01832                 }
01833 
01834                 QSize buf_size = player->GetVideoBufferSize();
01835 
01836                 if (video_width != buf_size.width() ||
01837                     video_height != buf_size.height())
01838                 {
01839                     video_width = buf_size.width();
01840                     video_height = buf_size.height();
01841 
01842                     LOG(VB_GENERAL, LOG_INFO,
01843                         QString("Resizing from %1x%2 to %3x%4")
01844                             .arg(video_width).arg(video_height)
01845                             .arg(newWidth).arg(newHeight));
01846 
01847                 }
01848 
01849                 if (did_ff == 1)
01850                 {
01851                   // Create a new 'I' frame if we just processed a cut.
01852                   did_ff = 2;
01853                   writekeyframe = true;
01854                 }
01855 
01856                 if ((video_width == newWidth) && (video_height == newHeight))
01857                 {
01858                     frame.buf = lastDecode->buf;
01859                 }
01860                 else
01861                 {
01862                     frame.buf = newFrame;
01863                     avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
01864                                    video_width, video_height);
01865                     avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
01866                                    newWidth, newHeight);
01867 
01868                     int bottomBand = (video_height == 1088) ? 8 : 0;
01869                     scontext = sws_getCachedContext(scontext, video_width,
01870                                    video_height, PIX_FMT_YUV420P, newWidth,
01871                                    newHeight, PIX_FMT_YUV420P,
01872                                    SWS_FAST_BILINEAR, NULL, NULL, NULL);
01873 
01874                     sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
01875                               video_height - bottomBand,
01876                               imageOut.data, imageOut.linesize);
01877                 }
01878 
01879                 nvr->WriteVideo(&frame, true, writekeyframe);
01880             }
01881             player->GetCC608Reader()->FlushTxtBuffers();
01882         }
01883         else
01884         {
01885             if (did_ff == 1)
01886             {
01887                 did_ff = 2;
01888                 timecodeOffset +=
01889                     (frame.timecode - lasttimecode - (int)vidFrameTime);
01890             }
01891 
01892             if (video_aspect != new_aspect)
01893             {
01894                 video_aspect = new_aspect;
01895                 if (nvr)
01896                     nvr->SetNewVideoParams(video_aspect);
01897             }
01898 
01899 
01900             QSize buf_size = player->GetVideoBufferSize();
01901 
01902             if (video_width != buf_size.width() ||
01903                 video_height != buf_size.height())
01904             {
01905                 video_width = buf_size.width();
01906                 video_height = buf_size.height();
01907 
01908                 LOG(VB_GENERAL, LOG_INFO,
01909                     QString("Resizing from %1x%2 to %3x%4")
01910                         .arg(video_width).arg(video_height)
01911                         .arg(newWidth).arg(newHeight));
01912             }
01913 
01914             if ((video_width == newWidth) && (video_height == newHeight))
01915             {
01916                 frame.buf = lastDecode->buf;
01917             }
01918             else
01919             {
01920                 frame.buf = newFrame;
01921                 avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
01922                                video_width, video_height);
01923                 avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
01924                                newWidth, newHeight);
01925 
01926                 int bottomBand = (video_height == 1088) ? 8 : 0;
01927                 scontext = sws_getCachedContext(scontext, video_width,
01928                                video_height, PIX_FMT_YUV420P, newWidth,
01929                                newHeight, PIX_FMT_YUV420P,
01930                                SWS_FAST_BILINEAR, NULL, NULL, NULL);
01931 
01932                 sws_scale(scontext, imageIn.data, imageIn.linesize, 0,
01933                           video_height - bottomBand,
01934                           imageOut.data, imageOut.linesize);
01935             }
01936 
01937             // audio is fully decoded, so we need to reencode it
01938             if (arb->GetCount(frame.timecode))
01939             {
01940                 int loop = 0;
01941                 int bytesConsumed = 0;
01942                 int buffersConsumed = 0;
01943                 int count = arb->GetCount(frame.timecode);
01944                 for (loop = 0; loop < count; loop++)
01945                 {
01946                     AudioBuffer *ab = arb->GetData();
01947                     unsigned char *buf = (unsigned char *)ab->m_buffer.data();
01948                     if (avfMode)
01949                     {
01950                         if (did_ff != 1)
01951                         {
01952                             avfw->WriteAudioFrame(buf, audioFrame,
01953                                                   ab->m_time - timecodeOffset);
01954 
01955                             if (avfw2)
01956                             {
01957                                 if ((avfw2->GetTimecodeOffset() == -1) &&
01958                                     (avfw->GetTimecodeOffset() != -1))
01959                                 {
01960                                     avfw2->SetTimecodeOffset(
01961                                         avfw->GetTimecodeOffset());
01962                                 }
01963 
01964                                 avfw2->WriteAudioFrame(buf, audioFrame,
01965                                                    ab->m_time - timecodeOffset);
01966                             }
01967 
01968                             ++audioFrame;
01969                         }
01970                     }
01971                     else
01972                     {
01973                         nvr->SetOption("audioframesize", ab->m_buffer.size());
01974                         nvr->WriteAudio(buf, audioFrame++,
01975                                         ab->m_time - timecodeOffset);
01976                         if (nvr->IsErrored())
01977                         {
01978                             LOG(VB_GENERAL, LOG_ERR,
01979                                 "Transcode: Encountered irrecoverable error in "
01980                                 "NVR::WriteAudio");
01981 
01982                             delete [] newFrame;
01983                             if (player_ctx)
01984                                 delete player_ctx;
01985                             if (frameQueue)
01986                                 frameQueue->stop();
01987                             delete ab;
01988                             return REENCODE_ERROR;
01989                         }
01990                     }
01991 
01992                     ++buffersConsumed;
01993                     bytesConsumed += ab->m_buffer.size();
01994 
01995                     delete ab;
01996                 }
01997             }
01998 
01999             if (!avfMode)
02000                 player->GetCC608Reader()->
02001                     TranscodeWriteText(&TranscodeWriteText, (void *)(nvr));
02002             lasttimecode = frame.timecode;
02003             frame.timecode -= timecodeOffset;
02004 
02005             if (avfMode)
02006             {
02007                 if (halfFramerate && !skippedLastFrame)
02008                 {
02009                     skippedLastFrame = true;
02010                 }
02011                 else
02012                 {
02013                     skippedLastFrame = false;
02014 
02015                     if ((hls) &&
02016                         (avfw->GetFramesWritten()) &&
02017                         (hlsSegmentFrames > hlsSegmentSize) &&
02018                         (avfw->NextFrameIsKeyFrame()))
02019                     {
02020                         hls->AddSegment();
02021                         avfw->ReOpen(hls->GetCurrentFilename());
02022 
02023                         if (avfw2)
02024                             avfw2->ReOpen(hls->GetCurrentFilename(true));
02025 
02026                         hlsSegmentFrames = 0;
02027                     }
02028 
02029                     avfw->WriteVideoFrame(&frame);
02030                     ++hlsSegmentFrames;
02031                 }
02032             }
02033             else
02034             {
02035                 if (forceKeyFrames)
02036                     nvr->WriteVideo(&frame, true, true);
02037                 else
02038                     nvr->WriteVideo(&frame);
02039             }
02040         }
02041         if (QDateTime::currentDateTime() > statustime)
02042         {
02043             if (showprogress)
02044             {
02045                 LOG(VB_GENERAL, LOG_INFO,
02046                     QString("Processed: %1 of %2 frames(%3 seconds)").
02047                         arg((long)curFrameNum).arg((long)total_frame_count).
02048                         arg((long)(curFrameNum / video_frame_rate)));
02049             }
02050 
02051             if (hls && hls->CheckStop())
02052             {
02053                 hls->UpdateStatus(kHLSStatusStopping);
02054                 stopSignalled = true;
02055             }
02056 
02057             statustime = QDateTime::currentDateTime();
02058             statustime = statustime.addSecs(5);
02059         }
02060         if (QDateTime::currentDateTime() > curtime)
02061         {
02062             if (honorCutList && m_proginfo && !hls &&
02063                 m_proginfo->QueryMarkupFlag(MARK_UPDATED_CUT))
02064             {
02065                 LOG(VB_GENERAL, LOG_NOTICE,
02066                     "Transcoding aborted, cutlist updated");
02067 
02068                 unlink(outputname.toLocal8Bit().constData());
02069                 delete [] newFrame;
02070                 if (player_ctx)
02071                     delete player_ctx;
02072                 if (frameQueue)
02073                     frameQueue->stop();
02074                 return REENCODE_CUTLIST_CHANGE;
02075             }
02076 
02077             if ((jobID >= 0) || (VERBOSE_LEVEL_CHECK(VB_GENERAL, LOG_INFO)))
02078             {
02079                 if (JobQueue::GetJobCmd(jobID) == JOB_STOP)
02080                 {
02081                     LOG(VB_GENERAL, LOG_NOTICE,
02082                         "Transcoding STOPped by JobQueue");
02083 
02084                     unlink(outputname.toLocal8Bit().constData());
02085                     delete [] newFrame;
02086                     if (player_ctx)
02087                         delete player_ctx;
02088                     if (frameQueue)
02089                         frameQueue->stop();
02090                     return REENCODE_STOPPED;
02091                 }
02092 
02093                 float flagFPS = 0.0;
02094                 float elapsed = flagTime.elapsed() / 1000.0;
02095                 if (elapsed)
02096                     flagFPS = curFrameNum / elapsed;
02097 
02098                 int percentage = curFrameNum * 100 / total_frame_count;
02099 
02100                 if (hls)
02101                     hls->UpdatePercentComplete(percentage);
02102 
02103                 if (jobID >= 0)
02104                     JobQueue::ChangeJobComment(jobID,
02105                               QObject::tr("%1% Completed @ %2 fps.")
02106                                           .arg(percentage).arg(flagFPS));
02107                 else
02108                     LOG(VB_GENERAL, LOG_INFO,
02109                         QString("mythtranscode: %1% Completed @ %2 fps.")
02110                             .arg(percentage).arg(flagFPS));
02111 
02112             }
02113             curtime = QDateTime::currentDateTime();
02114             curtime = curtime.addSecs(20);
02115         }
02116 
02117         curFrameNum++;
02118         frame.frameNumber = 1 + (curFrameNum << 1);
02119 
02120         player->DiscardVideoFrame(lastDecode);
02121     }
02122 
02123     sws_freeContext(scontext);
02124 
02125     if (!fifow)
02126     {
02127         if (avfw)
02128             avfw->CloseFile();
02129 
02130         if (avfw2)
02131             avfw2->CloseFile();
02132 
02133         if (!hls && m_proginfo)
02134         {
02135             m_proginfo->ClearPositionMap(MARK_KEYFRAME);
02136             m_proginfo->ClearPositionMap(MARK_GOP_START);
02137             m_proginfo->ClearPositionMap(MARK_GOP_BYFRAME);
02138         }
02139 
02140         if (nvr)
02141         {
02142             nvr->WriteSeekTable();
02143             if (!kfa_table->empty())
02144                 nvr->WriteKeyFrameAdjustTable(*kfa_table);
02145         }
02146     } else {
02147         fifow->FIFODrain();
02148     }
02149 
02150     if (cutter)
02151         delete cutter;
02152 
02153     if (avfw)
02154         delete avfw;
02155 
02156     if (avfw2)
02157         delete avfw2;
02158 
02159     if (hls)
02160     {
02161         if (!stopSignalled)
02162         {
02163             hls->UpdateStatus(kHLSStatusCompleted);
02164             hls->UpdateStatusMessage("Transcoding Completed");
02165             hls->UpdatePercentComplete(100);
02166         }
02167         else
02168         {
02169             hls->UpdateStatus(kHLSStatusStopped);
02170             hls->UpdateStatusMessage("Transcoding Stopped");
02171         }
02172         delete hls;
02173     }
02174 
02175     if (frameQueue)
02176         frameQueue->stop();
02177 
02178     delete [] newFrame;
02179     if (player_ctx)
02180         delete player_ctx;
02181     return REENCODE_OK;
02182 }
02183 
02184 /* vim: set expandtab tabstop=4 shiftwidth=4: */
02185 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends