// Convert from an "absolute" (not cutlist-adjusted) value to its // "relative" (cutlist-adjusted) mapped value. Usually the position // argument is a frame number, the map argument maps frames to // milliseconds, the fallback_ratio is 1000/framerate_fps, and the // return value is in milliseconds. // // If the map and fallback_ratio arguments are omitted, it simply // converts from an absolute frame number to a relative // (cutlist-adjusted) frame number. uint64_t DecoderBase::TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap, uint64_t absPosition, // frames const frm_pos_map_t &map, // frame->ms float fallback_ratio) { uint64_t subtraction = 0; uint64_t startOfCutRegion = 0; bool withinCut = false; bool first = true; for (frm_dir_map_t::const_iterator i = deleteMap.begin(); i != deleteMap.end(); ++i) { if (first) withinCut = (i.value() == MARK_CUT_END); first = false; if (i.key() > absPosition) break; uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio); if (i.value() == MARK_CUT_START && !withinCut) { withinCut = true; startOfCutRegion = mappedKey; } else if (i.value() == MARK_CUT_END && withinCut) { withinCut = false; subtraction += (mappedKey - startOfCutRegion); } } uint64_t mappedPos = TranslatePosition(map, absPosition, fallback_ratio); if (withinCut) subtraction += (mappedPos - startOfCutRegion); return mappedPos - subtraction; }
// Convert from a "relative" (cutlist-adjusted) value to its // "absolute" (not cutlist-adjusted) mapped value. Usually the // position argument is in milliseconds, the map argument maps frames // to milliseconds, the fallback_ratio is 1000/framerate_fps, and the // return value is also in milliseconds. Upon return, if necessary, // the result may need a separate, non-cutlist adjusted conversion // from milliseconds to frame number, using the inverse // millisecond-to-frame map and the inverse fallback_ratio; see for // example TranslatePositionMsToFrame(). // // If the map and fallback_ratio arguments are omitted, it simply // converts from a relatve (cutlist-adjusted) frame number to an // absolute frame number. uint64_t DecoderBase::TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap, uint64_t relPosition, // ms const frm_pos_map_t &map, // frame->ms float fallback_ratio) { uint64_t addition = 0; uint64_t startOfCutRegion = 0; bool withinCut = false; bool first = true; for (frm_dir_map_t::const_iterator i = deleteMap.begin(); i != deleteMap.end(); ++i) { if (first) withinCut = (i.value() == MARK_CUT_END); first = false; uint64_t mappedKey = TranslatePosition(map, i.key(), fallback_ratio); if (i.value() == MARK_CUT_START && !withinCut) { withinCut = true; startOfCutRegion = mappedKey; if (relPosition + addition <= startOfCutRegion) break; } else if (i.value() == MARK_CUT_END && withinCut) { withinCut = false; addition += (mappedKey - startOfCutRegion); } } return relPosition + addition; }
/// Loads the given commercial break map into the deleteMap. void DeleteMap::LoadCommBreakMap(frm_dir_map_t &map) { Clear(); frm_dir_map_t::Iterator it = map.begin(); for ( ; it != map.end(); ++it) Add(it.key(), it.value() == MARK_COMM_START ? MARK_CUT_START : MARK_CUT_END); CleanMap(); Push(tr("Load Detected Commercials")); }
static void streamOutCommercialBreakList( ostream &output, const frm_dir_map_t &commercialBreakList) { if (progress) output << "----------------------------" << endl; if (commercialBreakList.empty()) { if (progress) output << "No breaks" << endl; } else { frm_dir_map_t::const_iterator it = commercialBreakList.begin(); for (; it != commercialBreakList.end(); ++it) { output << "framenum: " << it.key() << "\tmarktype: " << *it << endl; } } if (progress) output << "----------------------------" << endl; }
int Transcode::TranscodeFile( const QString &inputname, const QString &outputname, const QString &profileName, bool honorCutList, bool framecontrol, int jobID, QString fifodir, frm_dir_map_t &deleteMap, int AudioTrackNo, bool passthru) { QDateTime curtime = QDateTime::currentDateTime(); QDateTime statustime = curtime; int audioframesize; int audioFrame = 0; if (jobID >= 0) JobQueue::ChangeJobComment(jobID, "0% " + QObject::tr("Completed")); nvr = new NuppelVideoRecorder(NULL, NULL); // Input setup inRingBuffer = RingBuffer::Create(inputname, false, false); player = new MythPlayer(); player_ctx = new PlayerContext(kTranscoderInUseID); player_ctx->SetPlayingInfo(m_proginfo); player_ctx->SetRingBuffer(inRingBuffer); player_ctx->SetPlayer(player); player->SetNullVideo(); player->SetPlayerInfo(NULL, NULL, true, player_ctx); if (showprogress) { statustime = statustime.addSecs(5); } AudioOutput *audioOutput = new AudioReencodeBuffer(FORMAT_NONE, 0, passthru); AudioReencodeBuffer *arb = ((AudioReencodeBuffer*)audioOutput); player->GetAudio()->SetAudioOutput(audioOutput); player->SetTranscoding(true); if (player->OpenFile(false) < 0) { VERBOSE(VB_IMPORTANT, "Transcoding aborted, error opening file."); if (player_ctx) delete player_ctx; return REENCODE_ERROR; } if (AudioTrackNo > -1) { VERBOSE(VB_GENERAL, QString("Set audiotrack number to %1").arg(AudioTrackNo)); player->GetDecoder()->SetTrack(kTrackTypeAudio, AudioTrackNo); } long long total_frame_count = player->GetTotalFrameCount(); long long new_frame_count = total_frame_count; if (honorCutList && m_proginfo) { VERBOSE(VB_GENERAL, "Honoring the cutlist while transcoding"); frm_dir_map_t::const_iterator it; QString cutStr; long long lastStart = 0; if (deleteMap.size() == 0) m_proginfo->QueryCutList(deleteMap); for (it = deleteMap.begin(); it != deleteMap.end(); ++it) { if (*it) { if (!cutStr.isEmpty()) cutStr += ","; cutStr += QString("%1-").arg((long)it.key()); lastStart = it.key(); } else { if (cutStr.isEmpty()) cutStr += "0-"; cutStr += QString("%1").arg((long)it.key()); new_frame_count -= (it.key() - lastStart); } } if (cutStr.isEmpty()) cutStr = "Is Empty"; else if (cutStr.endsWith('-') && (total_frame_count > lastStart)) { new_frame_count -= (total_frame_count - lastStart); cutStr += QString("%1").arg(total_frame_count); } VERBOSE(VB_GENERAL, QString("Cutlist : %1").arg(cutStr)); VERBOSE(VB_GENERAL, QString("Original Length: %1 frames") .arg((long)total_frame_count)); VERBOSE(VB_GENERAL, QString("New Length : %1 frames") .arg((long)new_frame_count)); if ((m_proginfo->QueryIsEditing()) || (JobQueue::IsJobRunning(JOB_COMMFLAG, *m_proginfo))) { VERBOSE(VB_IMPORTANT, "Transcoding aborted, cutlist changed"); if (player_ctx) delete player_ctx; return REENCODE_CUTLIST_CHANGE; } m_proginfo->ClearMarkupFlag(MARK_UPDATED_CUT); curtime = curtime.addSecs(60); } player->GetAudio()->ReinitAudio(); QString encodingType = player->GetEncodingType(); bool copyvideo = false, copyaudio = false; QString vidsetting = NULL, audsetting = NULL, vidfilters = NULL; QSize buf_size = player->GetVideoBufferSize(); int video_width = buf_size.width(); int video_height = buf_size.height(); if (video_height == 1088) { VERBOSE(VB_IMPORTANT, "Found video height of 1088. This is unusual and " "more than likely the video is actually 1080 so mythtranscode " "will treat it as such."); } float video_aspect = player->GetVideoAspect(); float video_frame_rate = player->GetFrameRate(); int newWidth = video_width; int newHeight = video_height; kfa_table = new vector<struct kfatable_entry>; if (fifodir.isEmpty()) { if (!GetProfile(profileName, encodingType, video_height, (int)round(video_frame_rate))) { VERBOSE(VB_IMPORTANT, "Transcoding aborted, no profile found."); if (player_ctx) delete player_ctx; return REENCODE_ERROR; } // For overriding settings on the command line QMap<QString, QString> recorderOptionsMap; if (!recorderOptions.isEmpty()) { QStringList options = recorderOptions .split(",", QString::SkipEmptyParts); int loop = 0; while (loop < options.size()) { QStringList tokens = options[loop].split("="); recorderOptionsMap[tokens[0]] = tokens[1]; loop++; } } vidsetting = get_str_option(profile, "videocodec"); audsetting = get_str_option(profile, "audiocodec"); vidfilters = get_str_option(profile, "transcodefilters"); if (encodingType == "MPEG-2" && get_int_option(profile, "transcodelossless")) { VERBOSE(VB_IMPORTANT, "Switching to MPEG-2 transcoder."); if (player_ctx) delete player_ctx; return REENCODE_MPEG2TRANS; } // Recorder setup if (get_int_option(profile, "transcodelossless")) { vidsetting = encodingType; audsetting = "MP3"; } else if (get_int_option(profile, "transcoderesize")) { int actualHeight = (video_height == 1088 ? 1080 : video_height); player->SetVideoFilters(vidfilters); newWidth = get_int_option(profile, "width"); newHeight = get_int_option(profile, "height"); // If height or width are 0, then we need to calculate them if (newHeight == 0 && newWidth > 0) newHeight = (int)(1.0 * newWidth * actualHeight / video_width); else if (newWidth == 0 && newHeight > 0) newWidth = (int)(1.0 * newHeight * video_width / actualHeight); else if (newWidth == 0 && newHeight == 0) { newHeight = 480; newWidth = (int)(1.0 * 480 * video_width / actualHeight); if (newWidth > 640) { newWidth = 640; newHeight = (int)(1.0 * 640 * actualHeight / video_width); } } if (encodingType.left(4).toLower() == "mpeg") { // make sure dimensions are valid for MPEG codecs newHeight = (newHeight + 15) & ~0xF; newWidth = (newWidth + 15) & ~0xF; } VERBOSE(VB_IMPORTANT, QString("Resizing from %1x%2 to %3x%4") .arg(video_width).arg(video_height) .arg(newWidth).arg(newHeight)); } else // lossy and no resize player->SetVideoFilters(vidfilters); // this is ripped from tv_rec SetupRecording. It'd be nice to merge nvr->SetOption("inpixfmt", FMT_YV12); nvr->SetOption("width", newWidth); nvr->SetOption("height", newHeight); nvr->SetOption("tvformat", gCoreContext->GetSetting("TVFormat")); nvr->SetOption("vbiformat", gCoreContext->GetSetting("VbiFormat")); nvr->SetFrameRate(video_frame_rate); nvr->SetVideoAspect(video_aspect); nvr->SetTranscoding(true); if ((vidsetting == "MPEG-4") || (recorderOptionsMap["videocodec"] == "mpeg4")) { nvr->SetOption("videocodec", "mpeg4"); nvr->SetIntOption(&profile, "mpeg4bitrate"); nvr->SetIntOption(&profile, "scalebitrate"); nvr->SetIntOption(&profile, "mpeg4maxquality"); nvr->SetIntOption(&profile, "mpeg4minquality"); nvr->SetIntOption(&profile, "mpeg4qualdiff"); nvr->SetIntOption(&profile, "mpeg4optionvhq"); nvr->SetIntOption(&profile, "mpeg4option4mv"); #ifdef USING_FFMPEG_THREADS nvr->SetIntOption(profile, "encodingthreadcount"); #endif } else if ((vidsetting == "MPEG-2") || (recorderOptionsMap["videocodec"] == "mpeg2video")) { nvr->SetOption("videocodec", "mpeg2video"); nvr->SetIntOption(&profile, "mpeg2bitrate"); nvr->SetIntOption(&profile, "scalebitrate"); #ifdef USING_FFMPEG_THREADS nvr->SetIntOption(&profile, "encodingthreadcount"); #endif } else if ((vidsetting == "RTjpeg") || (recorderOptionsMap["videocodec"] == "rtjpeg")) { nvr->SetOption("videocodec", "rtjpeg"); nvr->SetIntOption(&profile, "rtjpegquality"); nvr->SetIntOption(&profile, "rtjpegchromafilter"); nvr->SetIntOption(&profile, "rtjpeglumafilter"); } else if (vidsetting.isEmpty()) { VERBOSE(VB_IMPORTANT, "No video information found!"); VERBOSE(VB_IMPORTANT, "Please ensure that recording profiles " "for the transcoder are set"); if (player_ctx) delete player_ctx; return REENCODE_ERROR; } else { VERBOSE(VB_IMPORTANT, QString("Unknown video codec: %1").arg(vidsetting)); if (player_ctx) delete player_ctx; return REENCODE_ERROR; } nvr->SetOption("samplerate", arb->eff_audiorate); if (audsetting == "MP3") { nvr->SetOption("audiocompression", 1); nvr->SetIntOption(&profile, "mp3quality"); copyaudio = true; } else if (audsetting == "Uncompressed") { nvr->SetOption("audiocompression", 0); } else { VERBOSE(VB_IMPORTANT, QString("Unknown audio codec: %1").arg(audsetting)); } nvr->AudioInit(true); // For overriding settings on the command line if (recorderOptionsMap.size() > 0) { QMap<QString, QString>::Iterator it; QString key, value; for (it = recorderOptionsMap.begin(); it != recorderOptionsMap.end(); ++it) { key = it.key(); value = *it; VERBOSE(VB_IMPORTANT, QString("Forcing Recorder option '%1' to '%2'") .arg(key).arg(value)); if (value.contains(QRegExp("[^0-9]"))) nvr->SetOption(key, value); else nvr->SetOption(key, value.toInt()); if (key == "width") newWidth = (value.toInt() + 15) & ~0xF; else if (key == "height") newHeight = (value.toInt() + 15) & ~0xF; else if (key == "videocodec") { if (value == "mpeg4") vidsetting = "MPEG-4"; else if (value == "mpeg2video") vidsetting = "MPEG-2"; else if (value == "rtjpeg") vidsetting = "RTjpeg"; } } } if ((vidsetting == "MPEG-4") || (vidsetting == "MPEG-2")) nvr->SetupAVCodecVideo(); else if (vidsetting == "RTjpeg") nvr->SetupRTjpeg(); outRingBuffer = RingBuffer::Create(outputname, true, false); nvr->SetRingBuffer(outRingBuffer); nvr->WriteHeader(); nvr->StreamAllocate(); } if (vidsetting == encodingType && !framecontrol && fifodir.isEmpty() && honorCutList && video_width == newWidth && video_height == newHeight) { copyvideo = true; VERBOSE(VB_GENERAL, "Reencoding video in 'raw' mode"); } if (deleteMap.size() > 0) player->SetCutList(deleteMap); keyframedist = 30; player->InitForTranscode(copyaudio, copyvideo); if (player->IsErrored()) { VERBOSE(VB_IMPORTANT, "Unable to initialize MythPlayer " "for Transcode"); if (player_ctx) delete player_ctx; return REENCODE_ERROR; } int vidSize = 0; // 1920x1080 video is actually 1920x1088 because of the 16x16 blocks so // we have to fudge the output size here. nuvexport knows how to handle // this and as of right now it is the only app that uses the fifo ability. if (video_height == 1080 && video_width == 1920) vidSize = (1088 * 1920) * 3 / 2; else vidSize = (video_height * video_width) * 3 / 2; VideoFrame frame; frame.codec = FMT_YV12; frame.width = newWidth; frame.height = newHeight; frame.size = newWidth * newHeight * 3 / 2; if (!fifodir.isEmpty()) { QString audfifo = fifodir + QString("/audout"); QString vidfifo = fifodir + QString("/vidout"); int audio_size = arb->eff_audiorate * arb->bytes_per_frame; // framecontrol is true if we want to enforce fifo sync. if (framecontrol) VERBOSE(VB_GENERAL, "Enforcing sync on fifos"); fifow = new FIFOWriter(2, framecontrol); if (!fifow->FIFOInit(0, QString("video"), vidfifo, vidSize, 50) || !fifow->FIFOInit(1, QString("audio"), audfifo, audio_size, 25)) { VERBOSE(VB_IMPORTANT, "Error initializing fifo writer. Aborting"); unlink(outputname.toLocal8Bit().constData()); if (player_ctx) delete player_ctx; return REENCODE_ERROR; } VERBOSE(VB_GENERAL, QString("Video %1x%2@%3fps Audio rate: %4") .arg(video_width).arg(video_height) .arg(video_frame_rate) .arg(arb->eff_audiorate)); VERBOSE(VB_GENERAL, "Created fifos. Waiting for connection."); } bool forceKeyFrames = (fifow == NULL) ? framecontrol : false; frm_dir_map_t::iterator dm_iter; bool writekeyframe = true; int num_keyframes = 0; int did_ff = 0; long curFrameNum = 0; frame.frameNumber = 1; long lastKeyFrame = 0; long totalAudio = 0; int dropvideo = 0; long long lasttimecode = 0; long long timecodeOffset = 0; float rateTimeConv = arb->eff_audiorate / 1000.0f; float vidFrameTime = 1000.0f / video_frame_rate; int wait_recover = 0; VideoOutput *videoOutput = player->getVideoOutput(); bool is_key = 0; bool first_loop = true; unsigned char *newFrame = new unsigned char[frame.size]; frame.buf = newFrame; AVPicture imageIn, imageOut; struct SwsContext *scontext = NULL; if (fifow) VERBOSE(VB_GENERAL, "Dumping Video and Audio data to fifos"); else if (copyaudio) VERBOSE(VB_GENERAL, "Copying Audio while transcoding Video"); else VERBOSE(VB_GENERAL, "Transcoding Video and Audio"); QTime flagTime; flagTime.start(); while (player->TranscodeGetNextFrame(dm_iter, did_ff, is_key, honorCutList)) { if (first_loop) { copyaudio = player->GetRawAudioState(); first_loop = false; } VideoFrame *lastDecode = videoOutput->GetLastDecodedFrame(); frame.timecode = lastDecode->timecode; if (frame.timecode < lasttimecode) frame.timecode = (long long)(lasttimecode + vidFrameTime); if (fifow) { frame.buf = lastDecode->buf; totalAudio += arb->audiobuffer_frames; int audbufTime = (int)(totalAudio / rateTimeConv); int auddelta = arb->last_audiotime - audbufTime; int vidTime = (int)(curFrameNum * vidFrameTime + 0.5); int viddelta = frame.timecode - vidTime; int delta = viddelta - auddelta; if (abs(delta) < 500 && abs(delta) >= vidFrameTime) { QString msg = QString("Audio is %1ms %2 video at # %3: " "auddelta=%4, viddelta=%5") .arg(abs(delta)) .arg(((delta > 0) ? "ahead of" : "behind")) .arg((int)curFrameNum) .arg(auddelta) .arg(viddelta); VERBOSE(VB_GENERAL, msg); dropvideo = (delta > 0) ? 1 : -1; wait_recover = 0; } else if (delta >= 500 && delta < 10000) { if (wait_recover == 0) { dropvideo = 5; wait_recover = 6; } else if (wait_recover == 1) { // Video is badly lagging. Try to catch up. int count = 0; while (delta > vidFrameTime) { fifow->FIFOWrite(0, frame.buf, vidSize); count++; delta -= (int)vidFrameTime; } QString msg = QString("Added %1 blank video frames") .arg(count); curFrameNum += count; dropvideo = 0; wait_recover = 0; } else wait_recover--; } else { dropvideo = 0; wait_recover = 0; } #if 0 int buflen = (int)(arb->audiobuffer_len / rateTimeConv); VERBOSE(VB_GENERAL, QString("%1: video time: %2 audio time: %3 " "buf: %4 exp: %5 delta: %6") .arg(curFrameNum) .arg(frame.timecode) .arg(arb->last_audiotime) .arg(buflen) .arg(audbufTime) .arg(delta)); #endif if (arb->audiobuffer_len) fifow->FIFOWrite(1, arb->audiobuffer, arb->audiobuffer_len); if (dropvideo < 0) { dropvideo++; curFrameNum--; } else { fifow->FIFOWrite(0, frame.buf, vidSize); if (dropvideo) { fifow->FIFOWrite(0, frame.buf, vidSize); curFrameNum++; dropvideo--; } } videoOutput->DoneDisplayingFrame(lastDecode); audioOutput->Reset(); player->GetCC608Reader()->FlushTxtBuffers(); lasttimecode = frame.timecode; } else if (copyaudio) { // Encoding from NuppelVideo to NuppelVideo with MP3 audio // So let's not decode/reencode audio if (!player->GetRawAudioState()) { // The Raw state changed during decode. This is not good VERBOSE(VB_IMPORTANT, "Transcoding aborted, MythPlayer " "is not in raw audio mode."); unlink(outputname.toLocal8Bit().constData()); delete [] newFrame; if (player_ctx) delete player_ctx; return REENCODE_ERROR; } if (forceKeyFrames) writekeyframe = true; else { writekeyframe = is_key; if (writekeyframe) { // Currently, we don't create new sync frames, // (though we do create new 'I' frames), so we mark // the key-frames before deciding whether we need a // new 'I' frame. //need to correct the frame# and timecode here // Question: Is it necessary to change the timecodes? long sync_offset = player->UpdateStoredFrameNum(curFrameNum); nvr->UpdateSeekTable(num_keyframes, sync_offset); ReencoderAddKFA(curFrameNum, lastKeyFrame, num_keyframes); num_keyframes++; lastKeyFrame = curFrameNum; if (did_ff) did_ff = 0; } } if (did_ff == 1) { timecodeOffset += (frame.timecode - lasttimecode - (int)vidFrameTime); } lasttimecode = frame.timecode; frame.timecode -= timecodeOffset; if (! player->WriteStoredData(outRingBuffer, (did_ff == 0), timecodeOffset)) { if (video_aspect != player->GetVideoAspect()) { video_aspect = player->GetVideoAspect(); nvr->SetNewVideoParams(video_aspect); } QSize buf_size = player->GetVideoBufferSize(); if (video_width != buf_size.width() || video_height != buf_size.height()) { video_width = buf_size.width(); video_height = buf_size.height(); VERBOSE(VB_IMPORTANT, QString("Resizing from %1x%2 to %3x%4") .arg(video_width).arg(video_height) .arg(newWidth).arg(newHeight)); } if (did_ff == 1) { // Create a new 'I' frame if we just processed a cut. did_ff = 2; writekeyframe = true; } if ((video_width == newWidth) && (video_height == newHeight)) { frame.buf = lastDecode->buf; } else { frame.buf = newFrame; avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P, video_width, video_height); avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P, newWidth, newHeight); int bottomBand = (video_height == 1088) ? 8 : 0; scontext = sws_getCachedContext(scontext, video_width, video_height, PIX_FMT_YUV420P, newWidth, newHeight, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL); sws_scale(scontext, imageIn.data, imageIn.linesize, 0, video_height - bottomBand, imageOut.data, imageOut.linesize); } nvr->WriteVideo(&frame, true, writekeyframe); } audioOutput->Reset(); player->GetCC608Reader()->FlushTxtBuffers(); } else { if (did_ff == 1) { did_ff = 2; timecodeOffset += (frame.timecode - lasttimecode - (int)vidFrameTime); } if (video_aspect != player->GetVideoAspect()) { video_aspect = player->GetVideoAspect(); nvr->SetNewVideoParams(video_aspect); } QSize buf_size = player->GetVideoBufferSize(); if (video_width != buf_size.width() || video_height != buf_size.height()) { video_width = buf_size.width(); video_height = buf_size.height(); VERBOSE(VB_IMPORTANT, QString("Resizing from %1x%2 to %3x%4") .arg(video_width).arg(video_height) .arg(newWidth).arg(newHeight)); } if ((video_width == newWidth) && (video_height == newHeight)) { frame.buf = lastDecode->buf; } else { frame.buf = newFrame; avpicture_fill(&imageIn, lastDecode->buf, PIX_FMT_YUV420P, video_width, video_height); avpicture_fill(&imageOut, frame.buf, PIX_FMT_YUV420P, newWidth, newHeight); int bottomBand = (video_height == 1088) ? 8 : 0; scontext = sws_getCachedContext(scontext, video_width, video_height, PIX_FMT_YUV420P, newWidth, newHeight, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL); sws_scale(scontext, imageIn.data, imageIn.linesize, 0, video_height - bottomBand, imageOut.data, imageOut.linesize); } // audio is fully decoded, so we need to reencode it audioframesize = arb->audiobuffer_len; if (arb->ab_count) { for (int loop = 0; loop < arb->ab_count; loop++) { nvr->SetOption("audioframesize", arb->ab_len[loop]); nvr->WriteAudio(arb->audiobuffer + arb->ab_offset[loop], audioFrame++, arb->ab_time[loop] - timecodeOffset); if (nvr->IsErrored()) { VERBOSE(VB_IMPORTANT, "Transcode: Encountered " "irrecoverable error in NVR::WriteAudio"); delete [] newFrame; if (player_ctx) delete player_ctx; return REENCODE_ERROR; } } arb->ab_count = 0; arb->audiobuffer_len = 0; } player->GetCC608Reader()->TranscodeWriteText(&TranscodeWriteText, (void *)(nvr)); lasttimecode = frame.timecode; frame.timecode -= timecodeOffset; if (forceKeyFrames) nvr->WriteVideo(&frame, true, true); else nvr->WriteVideo(&frame); } if (showprogress && QDateTime::currentDateTime() > statustime) { VERBOSE(VB_IMPORTANT, QString("Processed: %1 of %2 frames(%3 seconds)"). arg((long)curFrameNum).arg((long)total_frame_count). arg((long)(curFrameNum / video_frame_rate))); statustime = QDateTime::currentDateTime(); statustime = statustime.addSecs(5); } if (QDateTime::currentDateTime() > curtime) { if (honorCutList && m_proginfo && m_proginfo->QueryMarkupFlag(MARK_UPDATED_CUT)) { VERBOSE(VB_IMPORTANT, "Transcoding aborted, cutlist updated"); unlink(outputname.toLocal8Bit().constData()); delete [] newFrame; if (player_ctx) delete player_ctx; return REENCODE_CUTLIST_CHANGE; } if ((jobID >= 0) || (VERBOSE_LEVEL_CHECK(VB_IMPORTANT))) { if (JobQueue::GetJobCmd(jobID) == JOB_STOP) { VERBOSE(VB_IMPORTANT, "Transcoding STOPped by JobQueue"); unlink(outputname.toLocal8Bit().constData()); delete [] newFrame; if (player_ctx) delete player_ctx; return REENCODE_STOPPED; } float flagFPS = 0.0; float elapsed = flagTime.elapsed() / 1000.0; if (elapsed) flagFPS = curFrameNum / elapsed; int percentage = curFrameNum * 100 / total_frame_count; if (jobID >= 0) JobQueue::ChangeJobComment(jobID, QObject::tr("%1% Completed @ %2 fps.") .arg(percentage).arg(flagFPS)); else VERBOSE(VB_IMPORTANT, QString( "mythtranscode: %1% Completed @ %2 fps.") .arg(percentage).arg(flagFPS)); } curtime = QDateTime::currentDateTime(); curtime = curtime.addSecs(20); } curFrameNum++; frame.frameNumber = 1 + (curFrameNum << 1); } sws_freeContext(scontext); if (! fifow) { if (m_proginfo) { m_proginfo->ClearPositionMap(MARK_KEYFRAME); m_proginfo->ClearPositionMap(MARK_GOP_START); m_proginfo->ClearPositionMap(MARK_GOP_BYFRAME); } nvr->WriteSeekTable(); if (!kfa_table->empty()) nvr->WriteKeyFrameAdjustTable(*kfa_table); } else { fifow->FIFODrain(); } delete [] newFrame; if (player_ctx) delete player_ctx; return REENCODE_OK; }