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
00074
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
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
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
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
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
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
00295 }
00296 virtual void Drain(void)
00297 {
00298
00299 }
00300
00301 virtual int64_t GetAudiotime(void)
00302 {
00303 return m_last_audiotime;
00304 }
00305
00306 virtual int GetVolumeChannel(int) const
00307 {
00308
00309 return 100;
00310 }
00311 virtual void SetVolumeChannel(int, int)
00312 {
00313
00314 }
00315 virtual void SetVolumeAll(int)
00316 {
00317
00318 }
00319 virtual uint GetCurrentVolume(void) const
00320 {
00321
00322 return 100;
00323 }
00324 virtual void SetCurrentVolume(int)
00325 {
00326
00327 }
00328 virtual void AdjustCurrentVolume(int)
00329 {
00330
00331 }
00332 virtual void SetMute(bool)
00333 {
00334
00335 }
00336 virtual void ToggleMute(void)
00337 {
00338
00339 }
00340 virtual MuteState GetMuteState(void) const
00341 {
00342
00343 return kMuteOff;
00344 }
00345 virtual MuteState IterateMutedChannels(void)
00346 {
00347
00348 return kMuteOff;
00349 }
00350
00351 virtual bool IsUpmixing(void)
00352 {
00353
00354 return false;
00355 }
00356
00357 virtual bool ToggleUpmix(void)
00358 {
00359
00360 return false;
00361 }
00362
00363 virtual bool CanUpmix(void)
00364 {
00365
00366 return false;
00367 }
00368
00369 virtual void SetSWVolume(int new_volume, bool save)
00370 {
00371
00372 return;
00373 }
00374 virtual int GetSWVolume(void)
00375 {
00376
00377 return 100;
00378 }
00379
00380
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
00405
00406
00407
00408
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
00432
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
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
00499
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
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
00544
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
00555
00556
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
00571
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
00586
00587
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
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
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
01049
01050
01051
01052
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
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
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
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
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
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
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
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
01306 player->SetVideoFilters(vidfilters);
01307
01308
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
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
01454
01455
01456 cutter = new Cutter();
01457 cutter->SetCutList(deleteMap);
01458
01459 player->SetCutList(cutter->AdjustedCutList());
01460 }
01461 else
01462 {
01463
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
01481
01482
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
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
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
01560
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
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
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
01775
01776 if (!player->GetRawAudioState())
01777 {
01778
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
01799
01800
01801
01802
01803
01804
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
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
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
02185