00001 #include <cstdio>
00002 #include <cstdlib>
00003 #include <unistd.h>
00004 #include <fcntl.h>
00005 #include <cassert>
00006 #include <cerrno>
00007 #include <sys/time.h>
00008 #include <ctime>
00009 #include <qstringlist.h>
00010 #include <qsqldatabase.h>
00011 #include <qmap.h>
00012 #include <math.h>
00013
00014 #include <iostream>
00015 using namespace std;
00016
00017 #include "osdtypes.h"
00018 #include "transcode.h"
00019 #include "audiooutput.h"
00020 #include "recordingprofile.h"
00021 #include "remoteutil.h"
00022 #include "mythcontext.h"
00023 #include "jobqueue.h"
00024
00025 extern "C" {
00026 #include "libavcodec/avcodec.h"
00027 }
00028
00029 #define LOC QString("Transcode: ")
00030 #define LOC_ERR QString("Transcode, Error: ")
00031
00032
00033
00034
00035 class AudioReencodeBuffer : public AudioOutput
00036 {
00037 public:
00038 AudioReencodeBuffer(int audio_bits, int audio_channels)
00039 {
00040 Reset();
00041 Reconfigure(audio_bits, audio_channels, 0, 0);
00042 bufsize = 512000;
00043 audiobuffer = new unsigned char[bufsize];
00044
00045 ab_count = 0;
00046 memset(ab_len, 0, sizeof(ab_len));
00047 memset(ab_offset, 0, sizeof(ab_offset));
00048 memset(ab_time, 0, sizeof(ab_time));
00049 }
00050
00051 ~AudioReencodeBuffer()
00052 {
00053 delete [] audiobuffer;
00054 }
00055
00056
00057 virtual void Reconfigure(int audio_bits, int audio_channels,
00058 int audio_samplerate, bool audio_passthru,
00059 void *audio_codec = NULL)
00060 {
00061 (void)audio_samplerate;
00062 (void)audio_passthru;
00063 (void)audio_codec;
00064
00065 ClearError();
00066 bits = audio_bits;
00067 channels = audio_channels;
00068 bytes_per_sample = bits * channels / 8;
00069 if ((uint)audio_channels > 2)
00070 Error(QString("Invalid channel count %1").arg(channels));
00071 }
00072
00073
00074 virtual void SetEffDsp(int dsprate)
00075 {
00076 eff_audiorate = (dsprate / 100);
00077 }
00078
00079 virtual void SetBlocking(bool block) { (void)block; }
00080 virtual void Reset(void)
00081 {
00082 audiobuffer_len = 0;
00083 ab_count = 0;
00084 }
00085
00086
00087 virtual bool AddSamples(char *buffer, int samples, long long timecode)
00088 {
00089 int freebuf = bufsize - audiobuffer_len;
00090
00091 if (samples * bytes_per_sample > freebuf)
00092 {
00093 bufsize += samples * bytes_per_sample - freebuf;
00094 unsigned char *tmpbuf = new unsigned char[bufsize];
00095 memcpy(tmpbuf, audiobuffer, audiobuffer_len);
00096 delete [] audiobuffer;
00097 audiobuffer = tmpbuf;
00098 }
00099
00100 ab_len[ab_count] = samples * bytes_per_sample;
00101 ab_offset[ab_count] = audiobuffer_len;
00102
00103 memcpy(audiobuffer + audiobuffer_len, buffer,
00104 samples * bytes_per_sample);
00105 audiobuffer_len += samples * bytes_per_sample;
00106
00107
00108 last_audiotime = timecode + samples * 1000 / eff_audiorate;
00109
00110 ab_time[ab_count] = last_audiotime;
00111 ab_count++;
00112
00113 return true;
00114 }
00115
00116 virtual bool AddSamples(char *buffers[], int samples, long long timecode)
00117 {
00118 int audio_bytes = bits / 8;
00119 int freebuf = bufsize - audiobuffer_len;
00120
00121 if (samples * bytes_per_sample > freebuf)
00122 {
00123 bufsize += samples * bytes_per_sample - freebuf;
00124 unsigned char *tmpbuf = new unsigned char[bufsize];
00125 memcpy(tmpbuf, audiobuffer, audiobuffer_len);
00126 delete [] audiobuffer;
00127 audiobuffer = tmpbuf;
00128 }
00129
00130 ab_len[ab_count] = samples * bytes_per_sample;
00131 ab_offset[ab_count] = audiobuffer_len;
00132
00133 for (int itemp = 0; itemp < samples*audio_bytes; itemp+=audio_bytes)
00134 {
00135 for(int chan = 0; chan < channels; chan++)
00136 {
00137 audiobuffer[audiobuffer_len++] = buffers[chan][itemp];
00138 if (bits == 16)
00139 audiobuffer[audiobuffer_len++] = buffers[chan][itemp+1];
00140 }
00141 }
00142
00143
00144 last_audiotime = timecode + samples * 1000 / eff_audiorate;
00145
00146 ab_time[ab_count] = last_audiotime;
00147 ab_count++;
00148
00149 return true;
00150 }
00151
00152 virtual void SetTimecode(long long timecode)
00153 {
00154 last_audiotime = timecode;
00155 }
00156 virtual bool GetPause(void)
00157 {
00158 return false;
00159 }
00160 virtual void Pause(bool paused)
00161 {
00162 (void)paused;
00163 }
00164 virtual void Drain(void)
00165 {
00166
00167 return;
00168 }
00169
00170 virtual int GetAudiotime(void)
00171 {
00172 return last_audiotime;
00173 }
00174
00175 virtual int GetVolumeChannel(int)
00176 {
00177
00178 return 100;
00179 }
00180 virtual void SetVolumeChannel(int, int)
00181 {
00182
00183 }
00184 virtual void SetVolumeAll(int)
00185 {
00186
00187 }
00188
00189
00190 virtual int GetCurrentVolume(void)
00191 {
00192
00193 return 100;
00194 }
00195 virtual void SetCurrentVolume(int)
00196 {
00197
00198 }
00199 virtual void AdjustCurrentVolume(int)
00200 {
00201
00202 }
00203 virtual void SetMute(bool)
00204 {
00205
00206 }
00207 virtual void ToggleMute(void)
00208 {
00209
00210 }
00211 virtual kMuteState GetMute(void)
00212 {
00213
00214 return MUTE_OFF;
00215 }
00216 virtual kMuteState IterateMutedChannels(void)
00217 {
00218
00219 return MUTE_OFF;
00220 }
00221
00222
00223 virtual void bufferOutputData(bool){ return; }
00224 virtual int readOutputData(unsigned char*, int ){ return 0; }
00225
00226 int bufsize;
00227 int ab_count;
00228 int ab_len[128];
00229 int ab_offset[128];
00230 long long ab_time[128];
00231 unsigned char *audiobuffer;
00232 int audiobuffer_len, channels, bits, bytes_per_sample, eff_audiorate;
00233 long long last_audiotime;
00234 };
00235
00236 Transcode::Transcode(ProgramInfo *pginfo)
00237 {
00238 m_proginfo = pginfo;
00239 nvr = NULL;
00240 nvp = NULL;
00241 inRingBuffer = NULL;
00242 outRingBuffer = NULL;
00243 fifow = NULL;
00244 kfa_table = NULL;
00245 showprogress = false;
00246 }
00247 Transcode::~Transcode()
00248 {
00249 if (nvr)
00250 delete nvr;
00251 if (nvp)
00252 delete nvp;
00253 if (inRingBuffer)
00254 delete inRingBuffer;
00255 if (outRingBuffer)
00256 delete outRingBuffer;
00257 if (fifow)
00258 delete fifow;
00259 if (kfa_table)
00260 {
00261 while(! kfa_table->isEmpty())
00262 {
00263 delete kfa_table->last();
00264 kfa_table->removeLast();
00265 }
00266 delete kfa_table;
00267 }
00268 }
00269 void Transcode::ReencoderAddKFA(long curframe, long lastkey, long num_keyframes)
00270 {
00271 long delta = curframe - lastkey;
00272 if (delta != 0 && delta != keyframedist)
00273 {
00274 struct kfatable_entry *kfate = new struct kfatable_entry;
00275 kfate->adjust = keyframedist - delta;
00276 kfate->keyframe_number = num_keyframes;
00277 kfa_table->append(kfate);
00278 }
00279 }
00280
00281 bool Transcode::GetProfile(QString profileName, QString encodingType,
00282 int height, int frameRate)
00283 {
00284 if (profileName.lower() == "autodetect")
00285 {
00286 if (height == 1088)
00287 height = 1080;
00288
00289 QString autoProfileName = QObject::tr("Autodetect from %1").arg(height);
00290 if (frameRate == 25 || frameRate == 30)
00291 autoProfileName += "i";
00292 if (frameRate == 50 || frameRate == 60)
00293 autoProfileName += "p";
00294
00295 bool result = false;
00296 VERBOSE(VB_IMPORTANT,
00297 QString("Transcode: Looking for autodetect profile: %1").
00298 arg(autoProfileName));
00299 result = profile.loadByGroup(autoProfileName, "Transcoders");
00300
00301 if (!result && encodingType == "MPEG-2")
00302 {
00303 result = profile.loadByGroup("MPEG2", "Transcoders");
00304 autoProfileName = "MPEG2";
00305 }
00306 if (!result && (encodingType == "MPEG-4" || encodingType == "RTjpeg"))
00307 {
00308 result = profile.loadByGroup("RTjpeg/MPEG4",
00309 "Transcoders");
00310 autoProfileName = "RTjpeg/MPEG4";
00311 }
00312 if (!result)
00313 {
00314 VERBOSE(VB_IMPORTANT,
00315 QString("Transcode: Couldn't find profile for : %1").
00316 arg(encodingType));
00317
00318 return false;
00319 }
00320
00321 VERBOSE(VB_IMPORTANT,
00322 QString("Transcode: Using autodetect profile: %1").
00323 arg(autoProfileName));
00324 }
00325 else
00326 {
00327 bool isNum;
00328 int profileID;
00329 profileID = profileName.toInt(&isNum);
00330
00331 if (isNum && profileID > 0)
00332 profile.loadByID(profileID);
00333 else
00334 {
00335 VERBOSE(VB_IMPORTANT, QString("Couldn't find profile #: %1").
00336 arg(profileName));
00337 return false;
00338 }
00339 }
00340 return true;
00341 }
00342
00343 static QString get_str_option(RecordingProfile &profile, const QString &name)
00344 {
00345 const Setting *setting = profile.byName(name);
00346 if (setting)
00347 return setting->getValue();
00348
00349 VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
00350 "get_str_option(...%1): Option not in profile.").arg(name));
00351
00352 return QString::null;
00353 }
00354
00355 static int get_int_option(RecordingProfile &profile, const QString &name)
00356 {
00357 QString ret_str = get_str_option(profile, name);
00358 if (ret_str.isEmpty())
00359 return 0;
00360
00361 bool ok = false;
00362 int ret_int = ret_str.toInt(&ok);
00363
00364 if (!ok)
00365 {
00366 VERBOSE(VB_IMPORTANT, LOC_ERR + QString(
00367 "get_int_option(...%1): Option is not an int.").arg(name));
00368 }
00369
00370 return ret_int;
00371 }
00372
00373 void TranscodeWriteText(void *ptr, unsigned char *buf, int len, int timecode, int pagenr)
00374 {
00375 NuppelVideoRecorder *nvr = (NuppelVideoRecorder *)ptr;
00376 nvr->WriteText(buf, len, timecode, pagenr);
00377 }
00378
00379 int Transcode::TranscodeFile(char *inputname, char *outputname,
00380 QString profileName,
00381 bool honorCutList, bool framecontrol,
00382 int jobID, QString fifodir,
00383 QMap<long long, int> deleteMap)
00384 {
00385 QDateTime curtime = QDateTime::currentDateTime();
00386 QDateTime statustime = curtime;
00387 int audioframesize;
00388 int audioFrame = 0;
00389
00390 if (jobID >= 0)
00391 JobQueue::ChangeJobComment(jobID, "0% " + QObject::tr("Completed"));
00392
00393 nvp = new NuppelVideoPlayer("transcoder", m_proginfo);
00394 nvp->SetNullVideo();
00395
00396 if (showprogress)
00397 {
00398 statustime = statustime.addSecs(5);
00399 }
00400
00401 nvr = new NuppelVideoRecorder(NULL, NULL);
00402 inRingBuffer = new RingBuffer(inputname, false, false);
00403 nvp->SetRingBuffer(inRingBuffer);
00404
00405 AudioOutput *audioOutput = new AudioReencodeBuffer(0, 0);
00406 AudioReencodeBuffer *arb = ((AudioReencodeBuffer*)audioOutput);
00407 nvp->SetAudioOutput(audioOutput);
00408 nvp->SetTranscoding(true);
00409
00410 if (nvp->OpenFile(false) < 0)
00411 {
00412 VERBOSE(VB_IMPORTANT, "Transcoding aborted, error opening file.");
00413 return REENCODE_ERROR;
00414 }
00415
00416 long long total_frame_count = nvp->GetTotalFrameCount();
00417 long long new_frame_count = total_frame_count;
00418 if (honorCutList && m_proginfo)
00419 {
00420 VERBOSE(VB_GENERAL, "Honoring the cutlist while transcoding");
00421
00422 QMap<long long, int> delMap;
00423 QMap<long long, int>::Iterator it;
00424 QString cutStr = "";
00425 long long lastStart = 0;
00426
00427 if (deleteMap.size() > 0)
00428 delMap = deleteMap;
00429 else
00430 m_proginfo->GetCutList(delMap);
00431
00432 for (it = delMap.begin(); it != delMap.end(); ++it)
00433 {
00434 if (it.data())
00435 {
00436 if (cutStr != "")
00437 cutStr += ",";
00438 cutStr += QString("%1-").arg((long)it.key());
00439 lastStart = it.key();
00440 }
00441 else
00442 {
00443 cutStr += QString("%1").arg((long)it.key());
00444 new_frame_count -= (it.key() - lastStart);
00445 }
00446 }
00447 if (cutStr == "")
00448 cutStr = "Is Empty";
00449 VERBOSE(VB_GENERAL, QString("Cutlist : %1").arg(cutStr));
00450 VERBOSE(VB_GENERAL, QString("Original Length: %1 frames")
00451 .arg((long)total_frame_count));
00452 VERBOSE(VB_GENERAL, QString("New Length : %1 frames")
00453 .arg((long)new_frame_count));
00454
00455 if ((m_proginfo->IsEditing()) ||
00456 (JobQueue::IsJobRunning(JOB_COMMFLAG, m_proginfo)))
00457 {
00458 VERBOSE(VB_IMPORTANT, "Transcoding aborted, cutlist changed");
00459 return REENCODE_CUTLIST_CHANGE;
00460 }
00461 m_proginfo->SetMarkupFlag(MARK_UPDATED_CUT, false);
00462 curtime = curtime.addSecs(60);
00463 }
00464
00465 nvp->ReinitAudio();
00466 QString encodingType = nvp->GetEncodingType();
00467 bool copyvideo = false, copyaudio = false;
00468
00469 QString vidsetting = NULL, audsetting = NULL, vidfilters = NULL;
00470
00471 QSize buf_size = nvp->GetVideoBufferSize();
00472 int video_width = buf_size.width();
00473 int video_height = buf_size.height();
00474
00475 if (video_height == 1088) {
00476 VERBOSE(VB_IMPORTANT, "Found video height of 1088. This is unusual and "
00477 "more than likely the video is actually 1080 so mythtranscode "
00478 "will treat it as such.");
00479 }
00480
00481 float video_aspect = nvp->GetVideoAspect();
00482 float video_frame_rate = nvp->GetFrameRate();
00483 int newWidth = video_width;
00484 int newHeight = video_height;
00485
00486 kfa_table = new QPtrList<struct kfatable_entry>;
00487
00488 if (fifodir == NULL)
00489 {
00490 if (!GetProfile(profileName, encodingType, video_height,
00491 (int)round(video_frame_rate))) {
00492 VERBOSE(VB_IMPORTANT, "Transcoding aborted, no profile found.");
00493 return REENCODE_ERROR;
00494 }
00495 vidsetting = get_str_option(profile, "videocodec");
00496 audsetting = get_str_option(profile, "audiocodec");
00497 vidfilters = get_str_option(profile, "transcodefilters");
00498
00499 if (encodingType == "MPEG-2" &&
00500 get_int_option(profile, "transcodelossless"))
00501 {
00502 VERBOSE(VB_IMPORTANT, "Switching to MPEG-2 transcoder.");
00503 return REENCODE_MPEG2TRANS;
00504 }
00505
00506
00507 if (get_int_option(profile, "transcodelossless"))
00508 {
00509 vidsetting = encodingType;
00510 audsetting = "MP3";
00511 }
00512 else if (get_int_option(profile, "transcoderesize"))
00513 {
00514 int actualHeight = (video_height == 1088 ? 1080 : video_height);
00515
00516 nvp->SetVideoFilters(vidfilters);
00517 newWidth = get_int_option(profile, "width");
00518 newHeight = get_int_option(profile, "height");
00519
00520
00521 if (newHeight == 0 && newWidth > 0)
00522 newHeight = (int)(1.0 * newWidth * actualHeight / video_width);
00523 else if (newWidth == 0 && newHeight > 0)
00524 newWidth = (int)(1.0 * newHeight * video_width / actualHeight);
00525 else if (newWidth == 0 && newHeight == 0)
00526 {
00527 newHeight = 480;
00528 newWidth = (int)(1.0 * 480 * video_width / actualHeight);
00529 if (newWidth > 640)
00530 {
00531 newWidth = 640;
00532 newHeight = (int)(1.0 * 640 * actualHeight / video_width);
00533 }
00534 }
00535
00536 if (encodingType.left(4).lower() == "mpeg")
00537 {
00538
00539 newHeight = (newHeight + 15) & ~0xF;
00540 newWidth = (newWidth + 15) & ~0xF;
00541 }
00542
00543 VERBOSE(VB_IMPORTANT, QString("Resizing from %1x%2 to %3x%4")
00544 .arg(video_width).arg(video_height)
00545 .arg(newWidth).arg(newHeight));
00546 }
00547 else
00548 nvp->SetVideoFilters(vidfilters);
00549
00550
00551 nvr->SetOption("inpixfmt", FMT_YV12);
00552
00553 nvr->SetOption("width", newWidth);
00554 nvr->SetOption("height", newHeight);
00555
00556 nvr->SetOption("tvformat", gContext->GetSetting("TVFormat"));
00557 nvr->SetOption("vbiformat", gContext->GetSetting("VbiFormat"));
00558
00559 nvr->SetFrameRate(video_frame_rate);
00560 nvr->SetVideoAspect(video_aspect);
00561 nvr->SetTranscoding(true);
00562
00563 if (vidsetting == "MPEG-4")
00564 {
00565 nvr->SetOption("videocodec", "mpeg4");
00566
00567 nvr->SetIntOption(&profile, "mpeg4bitrate");
00568 nvr->SetIntOption(&profile, "scalebitrate");
00569 nvr->SetIntOption(&profile, "mpeg4maxquality");
00570 nvr->SetIntOption(&profile, "mpeg4minquality");
00571 nvr->SetIntOption(&profile, "mpeg4qualdiff");
00572 nvr->SetIntOption(&profile, "mpeg4optionvhq");
00573 nvr->SetIntOption(&profile, "mpeg4option4mv");
00574 nvr->SetupAVCodecVideo();
00575 }
00576 else if (vidsetting == "RTjpeg")
00577 {
00578 nvr->SetOption("videocodec", "rtjpeg");
00579 nvr->SetIntOption(&profile, "rtjpegquality");
00580 nvr->SetIntOption(&profile, "rtjpegchromafilter");
00581 nvr->SetIntOption(&profile, "rtjpeglumafilter");
00582 nvr->SetupRTjpeg();
00583 }
00584 else if (vidsetting == "")
00585 {
00586 VERBOSE(VB_IMPORTANT, "No video information found!");
00587 VERBOSE(VB_IMPORTANT, "Please ensure that recording profiles "
00588 "for the transcoder are set");
00589 return REENCODE_ERROR;
00590 }
00591 else
00592 {
00593 VERBOSE(VB_IMPORTANT, QString("Unknown video codec: %1").arg(vidsetting));
00594 return REENCODE_ERROR;
00595 }
00596
00597 nvr->SetOption("samplerate", arb->eff_audiorate);
00598 if (audsetting == "MP3")
00599 {
00600 nvr->SetOption("audiocompression", 1);
00601 nvr->SetIntOption(&profile, "mp3quality");
00602 copyaudio = true;
00603 }
00604 else if (audsetting == "Uncompressed")
00605 {
00606 nvr->SetOption("audiocompression", 0);
00607 }
00608 else
00609 {
00610 VERBOSE(VB_IMPORTANT, QString("Unknown audio codec: %1").arg(audsetting));
00611 }
00612
00613 nvr->AudioInit(true);
00614
00615 outRingBuffer = new RingBuffer(outputname, true, false);
00616 nvr->SetRingBuffer(outRingBuffer);
00617 nvr->WriteHeader();
00618 nvr->StreamAllocate();
00619 }
00620
00621 if (vidsetting == encodingType && !framecontrol &&
00622 fifodir == NULL && honorCutList &&
00623 video_width == newWidth && video_height == newHeight)
00624 {
00625 copyvideo = true;
00626 VERBOSE(VB_GENERAL, "Reencoding video in 'raw' mode");
00627 }
00628
00629 if (deleteMap.size() > 0)
00630 nvp->SetCutList(deleteMap);
00631
00632 keyframedist = 30;
00633 nvp->InitForTranscode(copyaudio, copyvideo);
00634 if (nvp->IsErrored())
00635 {
00636 VERBOSE(VB_IMPORTANT, "Unable to initialize NuppelVideoPlayer for Transcode");
00637 delete nvp;
00638 return REENCODE_ERROR;
00639 }
00640
00641 int vidSize = 0;
00642
00643
00644
00645
00646 if (video_height == 1080 && video_width == 1920)
00647 vidSize = (1088 * 1920) * 3 / 2;
00648 else
00649 vidSize = (video_height * video_width) * 3 / 2;
00650
00651 VideoFrame frame;
00652 frame.codec = FMT_YV12;
00653 frame.width = newWidth;
00654 frame.height = newHeight;
00655 frame.size = newWidth * newHeight * 3 / 2;
00656
00657 if (fifodir != NULL)
00658 {
00659 QString audfifo = fifodir + QString("/audout");
00660 QString vidfifo = fifodir + QString("/vidout");
00661 int audio_size = arb->eff_audiorate * arb->bytes_per_sample;
00662
00663 if (framecontrol)
00664 VERBOSE(VB_GENERAL, "Enforcing sync on fifos");
00665 fifow = new FIFOWriter::FIFOWriter(2, framecontrol);
00666
00667 if (!fifow->FIFOInit(0, QString("video"), vidfifo, vidSize, 50) ||
00668 !fifow->FIFOInit(1, QString("audio"), audfifo, audio_size, 25))
00669 {
00670 VERBOSE(VB_IMPORTANT, "Error initializing fifo writer. Aborting");
00671 unlink(outputname);
00672 return REENCODE_ERROR;
00673 }
00674 VERBOSE(VB_GENERAL, QString("Video %1x%2@%3fps Audio rate: %4")
00675 .arg(video_width).arg(video_height)
00676 .arg(video_frame_rate)
00677 .arg(arb->eff_audiorate));
00678 VERBOSE(VB_GENERAL, "Created fifos. Waiting for connection.");
00679 }
00680
00681 bool forceKeyFrames = (fifow == NULL) ? framecontrol : false;
00682
00683 QMap<long long, int>::Iterator dm_iter = NULL;
00684 bool writekeyframe = true;
00685
00686 int num_keyframes = 0;
00687
00688 int did_ff = 0;
00689
00690 long curFrameNum = 0;
00691 frame.frameNumber = 1;
00692 long lastKeyFrame = 0;
00693 long totalAudio = 0;
00694 int dropvideo = 0;
00695 long long lasttimecode = 0;
00696 long long timecodeOffset = 0;
00697
00698 float rateTimeConv = arb->eff_audiorate * arb->bytes_per_sample / 1000.0;
00699 float vidFrameTime = 1000.0 / video_frame_rate;
00700 int wait_recover = 0;
00701 VideoOutput *videoOutput = nvp->getVideoOutput();
00702 bool is_key = 0;
00703 bool first_loop = true;
00704 unsigned char *newFrame = new unsigned char[frame.size];
00705
00706 frame.buf = newFrame;
00707 AVPicture imageIn, imageOut;
00708 ImgReSampleContext *scontext;
00709
00710 if (fifow)
00711 VERBOSE(VB_GENERAL, "Dumping Video and Audio data to fifos");
00712 else if (copyaudio)
00713 VERBOSE(VB_GENERAL, "Copying Audio while transcoding Video");
00714 else
00715 VERBOSE(VB_GENERAL, "Transcoding Video and Audio");
00716
00717 QTime flagTime;
00718 flagTime.start();
00719
00720 while (nvp->TranscodeGetNextFrame(dm_iter, &did_ff, &is_key, honorCutList))
00721 {
00722 if (first_loop)
00723 {
00724 copyaudio = nvp->GetRawAudioState();
00725 first_loop = false;
00726 }
00727 VideoFrame *lastDecode = videoOutput->GetLastDecodedFrame();
00728
00729 frame.timecode = lastDecode->timecode;
00730
00731 if (frame.timecode < lasttimecode)
00732 frame.timecode = (long long)(lasttimecode + vidFrameTime);
00733
00734 if (fifow)
00735 {
00736 frame.buf = lastDecode->buf;
00737 totalAudio += arb->audiobuffer_len;
00738 int audbufTime = (int)(totalAudio / rateTimeConv);
00739 int auddelta = arb->last_audiotime - audbufTime;
00740 int vidTime = (int)(curFrameNum * vidFrameTime + 0.5);
00741 int viddelta = frame.timecode - vidTime;
00742 int delta = viddelta - auddelta;
00743 if (abs(delta) < 500 && abs(delta) >= vidFrameTime)
00744 {
00745 QString msg = QString("Audio is %1ms %2 video at # %3")
00746 .arg(abs(delta))
00747 .arg(((delta > 0) ? "ahead of" : "behind"))
00748 .arg((int)curFrameNum);
00749 VERBOSE(VB_GENERAL, msg);
00750 dropvideo = (delta > 0) ? 1 : -1;
00751 wait_recover = 0;
00752 }
00753 else if (delta >= 500 && delta < 10000)
00754 {
00755 if (wait_recover == 0)
00756 {
00757 dropvideo = 5;
00758 wait_recover = 6;
00759 }
00760 else if (wait_recover == 1)
00761 {
00762
00763 int count = 0;
00764 while (delta > vidFrameTime)
00765 {
00766 fifow->FIFOWrite(0, frame.buf, vidSize);
00767 count++;
00768 delta -= (int)vidFrameTime;
00769 }
00770 QString msg = QString("Added %1 blank video frames")
00771 .arg(count);
00772 curFrameNum += count;
00773 dropvideo = 0;
00774 wait_recover = 0;
00775 }
00776 else
00777 wait_recover--;
00778 }
00779 else
00780 {
00781 dropvideo = 0;
00782 wait_recover = 0;
00783 }
00784
00785
00786
00787
00788
00789
00790 if (arb->audiobuffer_len)
00791 fifow->FIFOWrite(1, arb->audiobuffer, arb->audiobuffer_len);
00792 if (dropvideo < 0)
00793 {
00794 dropvideo++;
00795 curFrameNum--;
00796 }
00797 else
00798 {
00799 fifow->FIFOWrite(0, frame.buf, vidSize);
00800 if (dropvideo)
00801 {
00802 fifow->FIFOWrite(0, frame.buf, vidSize);
00803 curFrameNum++;
00804 dropvideo--;
00805 }
00806 }
00807 videoOutput->DoneDisplayingFrame();
00808 audioOutput->Reset();
00809 nvp->FlushTxtBuffers();
00810 lasttimecode = frame.timecode;
00811 }
00812 else if (copyaudio)
00813 {
00814
00815
00816 if (!nvp->GetRawAudioState())
00817 {
00818
00819 unlink(outputname);
00820 delete newFrame;
00821 VERBOSE(VB_IMPORTANT, "Transcoding aborted, NuppelVideoPlayer "
00822 "is not in raw audio mode.");
00823 return REENCODE_ERROR;
00824 }
00825
00826 if (forceKeyFrames)
00827 writekeyframe = true;
00828 else
00829 {
00830 writekeyframe = is_key;
00831 if (writekeyframe)
00832 {
00833
00834
00835
00836
00837
00838
00839
00840 long sync_offset = nvp->UpdateStoredFrameNum(curFrameNum);
00841 nvr->UpdateSeekTable(num_keyframes, sync_offset);
00842 ReencoderAddKFA(curFrameNum, lastKeyFrame, num_keyframes);
00843 num_keyframes++;
00844 lastKeyFrame = curFrameNum;
00845
00846 if (did_ff)
00847 did_ff = 0;
00848 }
00849 }
00850
00851 if (did_ff == 1)
00852 {
00853 timecodeOffset +=
00854 (frame.timecode - lasttimecode - (int)vidFrameTime);
00855 }
00856 lasttimecode = frame.timecode;
00857 frame.timecode -= timecodeOffset;
00858
00859 if (! nvp->WriteStoredData(outRingBuffer, (did_ff == 0),
00860 timecodeOffset))
00861 {
00862 if (video_aspect != nvp->GetVideoAspect())
00863 {
00864 video_aspect = nvp->GetVideoAspect();
00865 nvr->SetNewVideoParams(video_aspect);
00866 }
00867
00868 QSize buf_size = nvp->GetVideoBufferSize();
00869
00870 if (video_width != buf_size.width() ||
00871 video_height != buf_size.height())
00872 {
00873 video_width = buf_size.width();
00874 video_height = buf_size.height();
00875
00876 VERBOSE(VB_IMPORTANT, QString("Resizing from %1x%2 to %3x%4")
00877 .arg(video_width).arg(video_height)
00878 .arg(newWidth).arg(newHeight));
00879
00880 }
00881
00882 if (did_ff == 1)
00883 {
00884
00885 did_ff = 2;
00886 writekeyframe = true;
00887 }
00888
00889 if ((video_width == newWidth) && (video_height == newHeight))
00890 {
00891 frame.buf = lastDecode->buf;
00892 }
00893 else
00894 {
00895 frame.buf = newFrame;
00896 avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
00897 video_width, video_height);
00898 avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
00899 newWidth, newHeight);
00900 if (video_height != 1088) {
00901 scontext = img_resample_init(newWidth, newHeight,
00902 video_width, video_height);
00903 } else {
00904 scontext = img_resample_full_init(newWidth, newHeight,
00905 video_width, video_height,
00906 0,8,0,0,0,0,0,0);
00907 }
00908 img_resample(scontext, &imageOut, &imageIn);
00909 img_resample_close(scontext);
00910 }
00911
00912 nvr->WriteVideo(&frame, true, writekeyframe);
00913 }
00914 audioOutput->Reset();
00915 nvp->FlushTxtBuffers();
00916 }
00917 else
00918 {
00919 if (did_ff == 1)
00920 {
00921 did_ff = 2;
00922 timecodeOffset +=
00923 (frame.timecode - lasttimecode - (int)vidFrameTime);
00924 }
00925
00926 if (video_aspect != nvp->GetVideoAspect())
00927 {
00928 video_aspect = nvp->GetVideoAspect();
00929 nvr->SetNewVideoParams(video_aspect);
00930 }
00931
00932
00933 QSize buf_size = nvp->GetVideoBufferSize();
00934
00935 if (video_width != buf_size.width() ||
00936 video_height != buf_size.height())
00937 {
00938 video_width = buf_size.width();
00939 video_height = buf_size.height();
00940
00941 VERBOSE(VB_IMPORTANT, QString("Resizing from %1x%2 to %3x%4")
00942 .arg(video_width).arg(video_height)
00943 .arg(newWidth).arg(newHeight));
00944 }
00945
00946 if ((video_width == newWidth) && (video_height == newHeight))
00947 {
00948 frame.buf = lastDecode->buf;
00949 }
00950 else
00951 {
00952 frame.buf = newFrame;
00953 avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P,
00954 video_width, video_height);
00955 avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P,
00956 newWidth, newHeight);
00957 if (video_height != 1088) {
00958 scontext = img_resample_init(newWidth, newHeight,
00959 video_width, video_height);
00960 }
00961 else
00962 {
00963 scontext = img_resample_full_init(newWidth, newHeight,
00964 video_width, video_height,
00965 0,8,0,0,0,0,0,0);
00966 }
00967
00968 img_resample(scontext, &imageOut, &imageIn);
00969 img_resample_close(scontext);
00970 }
00971
00972
00973 audioframesize = arb->audiobuffer_len;
00974 if (arb->ab_count)
00975 {
00976 for (int loop = 0; loop < arb->ab_count; loop++)
00977 {
00978 nvr->SetOption("audioframesize", arb->ab_len[loop]);
00979 nvr->WriteAudio(arb->audiobuffer + arb->ab_offset[loop],
00980 audioFrame++,
00981 arb->ab_time[loop] - timecodeOffset);
00982 if (nvr->IsErrored()) {
00983 VERBOSE(VB_IMPORTANT, "Transcode: Encountered "
00984 "irrecoverable error in NVR::WriteAudio");
00985 delete newFrame;
00986 return REENCODE_ERROR;
00987 }
00988 }
00989 arb->ab_count = 0;
00990 arb->audiobuffer_len = 0;
00991 }
00992
00993 nvp->TranscodeWriteText(&TranscodeWriteText, (void *)(nvr));
00994
00995 lasttimecode = frame.timecode;
00996 frame.timecode -= timecodeOffset;
00997
00998 if (forceKeyFrames)
00999 nvr->WriteVideo(&frame, true, true);
01000 else
01001 nvr->WriteVideo(&frame);
01002 }
01003 if (showprogress && QDateTime::currentDateTime() > statustime)
01004 {
01005 VERBOSE(VB_IMPORTANT, QString("Processed: %1 of %2 frames(%3 seconds)").
01006 arg((long)curFrameNum).arg((long)total_frame_count).
01007 arg((long)(curFrameNum / video_frame_rate)));
01008 statustime = QDateTime::currentDateTime();
01009 statustime = statustime.addSecs(5);
01010 }
01011 if (QDateTime::currentDateTime() > curtime)
01012 {
01013 if (honorCutList && m_proginfo &&
01014 m_proginfo->CheckMarkupFlag(MARK_UPDATED_CUT))
01015 {
01016 unlink(outputname);
01017 delete newFrame;
01018 VERBOSE(VB_IMPORTANT, "Transcoding aborted, cutlist updated");
01019 return REENCODE_CUTLIST_CHANGE;
01020 }
01021
01022 if ((jobID >= 0) || (print_verbose_messages & VB_IMPORTANT))
01023 {
01024 if (JobQueue::GetJobCmd(jobID) == JOB_STOP)
01025 {
01026 unlink(outputname);
01027 delete newFrame;
01028 VERBOSE(VB_IMPORTANT, "Transcoding STOPped by JobQueue");
01029 return REENCODE_STOPPED;
01030 }
01031
01032 float flagFPS = 0.0;
01033 float elapsed = flagTime.elapsed() / 1000.0;
01034 if (elapsed)
01035 flagFPS = curFrameNum / elapsed;
01036
01037 int percentage = curFrameNum * 100 / new_frame_count;
01038
01039 if (jobID >= 0)
01040 JobQueue::ChangeJobComment(jobID,
01041 QObject::tr("%1% Completed @ %2 fps.")
01042 .arg(percentage).arg(flagFPS));
01043 else
01044 VERBOSE(VB_IMPORTANT, QString(
01045 "mythtranscode: %1% Completed @ %2 fps.")
01046 .arg(percentage).arg(flagFPS));
01047
01048 }
01049 curtime = QDateTime::currentDateTime();
01050 curtime = curtime.addSecs(20);
01051 }
01052
01053 curFrameNum++;
01054 frame.frameNumber = 1 + (curFrameNum << 1);
01055 }
01056
01057 if (! fifow)
01058 {
01059 if (m_proginfo)
01060 {
01061 m_proginfo->ClearPositionMap(MARK_KEYFRAME);
01062 m_proginfo->ClearPositionMap(MARK_GOP_START);
01063 m_proginfo->ClearPositionMap(MARK_GOP_BYFRAME);
01064 }
01065
01066 nvr->WriteSeekTable();
01067 if (!kfa_table->isEmpty())
01068 nvr->WriteKeyFrameAdjustTable(kfa_table);
01069 } else {
01070 fifow->FIFODrain();
01071 }
01072 delete newFrame;
01073 return REENCODE_OK;
01074 }
01075
01076
01077