|
MythTV
0.26-pre
|
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
1.7.6.1