00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include <sys/types.h>
00012 #include <sys/stat.h>
00013 #include <fcntl.h>
00014 #include <unistd.h>
00015
00016 #include <algorithm>
00017
00018 #include <qdatetime.h>
00019 #include <qdir.h>
00020 #include <qapplication.h>
00021 #include <qdeepcopy.h>
00022 #include <qprocess.h>
00023
00024 #include <mythtv/mythcontext.h>
00025 #include <mythtv/mythdbcon.h>
00026 #include <mythtv/compat.h>
00027
00028 #include "jobthread.h"
00029 #include "mtd.h"
00030 #include "threadevents.h"
00031 #include "dvdprobe.h"
00032
00033 namespace {
00034 struct delete_file {
00035 bool operator()(const QString &filename) {
00036 VERBOSE(VB_GENERAL, QString("Deleting file: %1").arg(filename));
00037 return QDir::current().remove(filename);
00038 }
00039 };
00040 }
00041
00042 JobThread::JobThread(MTD *owner, const QString &start_string, int nice_priority)
00043 :QThread()
00044 {
00045 problem_string = "";
00046 job_name = "";
00047 setSubName("", 1);
00048 overall_progress = 0.0;
00049 setSubProgress(0.0, 1);
00050 parent = owner;
00051 job_string = start_string;
00052 nice_level = nice_priority;
00053 sub_to_overall_multiple = 1.0;
00054 cancel_me = false;
00055 }
00056
00057 void JobThread::run()
00058 {
00059 VERBOSE(VB_IMPORTANT, "Somebody ran an actual (base class) JobThread. I"
00060 " don't think that's supposed to happen.");
00061 }
00062
00063 bool JobThread::keepGoing()
00064 {
00065 if(parent->threadsShouldContinue() && ! cancel_me)
00066 {
00067 return true;
00068 }
00069 return false;
00070 }
00071
00072 QString JobThread::getProblem()
00073 {
00074 QMutexLocker qml(&problem_string_mutex);
00075 return QDeepCopy<QString>(problem_string);
00076 }
00077
00078 QString JobThread::getJobString()
00079 {
00080 QMutexLocker qml(&job_string_mutex);
00081 return QDeepCopy<QString>(job_string);
00082 }
00083
00084 void JobThread::updateSubjobString( int seconds_elapsed,
00085 const QString & pre_string)
00086 {
00087 int estimated_job_time = 0;
00088 if(subjob_progress > 0.0)
00089 {
00090 estimated_job_time = (int) ( (double) seconds_elapsed / subjob_progress );
00091 }
00092 else
00093 {
00094 estimated_job_time = (int) ( (double) seconds_elapsed / 0.000001 );
00095 }
00096
00097 QString new_name = "";
00098 if(estimated_job_time >= 3600)
00099 {
00100 new_name.sprintf(" %d:%02d:%02d/%d:%02d:%02d",
00101 seconds_elapsed / 3600,
00102 (seconds_elapsed % 3600) / 60,
00103 (seconds_elapsed % 3600) % 60,
00104 estimated_job_time / 3600,
00105 (estimated_job_time % 3600) / 60,
00106 (estimated_job_time % 3600) % 60);
00107 }
00108 else
00109 {
00110 new_name.sprintf(" %02d:%02d/%02d:%02d",
00111 seconds_elapsed / 60,
00112 seconds_elapsed % 60,
00113 estimated_job_time / 60,
00114 estimated_job_time % 60);
00115 }
00116 new_name.prepend(pre_string);
00117 setSubName(new_name, 1);
00118 }
00119
00120 void JobThread::setSubProgress(double some_value, uint priority)
00121 {
00122 if(priority > 0)
00123 {
00124 while(!subjob_progress_mutex.tryLock())
00125 {
00126 sleep(priority);
00127 }
00128 if(!cancel_me)
00129 {
00130 subjob_progress = some_value;
00131 }
00132 }
00133 else
00134 {
00135 subjob_progress_mutex.lock();
00136 if(!cancel_me)
00137 {
00138 subjob_progress = some_value;
00139 }
00140 }
00141 subjob_progress_mutex.unlock();
00142 }
00143
00144 void JobThread::setSubName(const QString &new_name, uint priority)
00145 {
00146 if(priority > 0)
00147 {
00148 while(!subjob_name_mutex.tryLock())
00149 {
00150 sleep(1);
00151 }
00152 if(!cancel_me)
00153 {
00154 subjob_name = new_name;
00155 }
00156 }
00157 else
00158 {
00159 subjob_name_mutex.lock();
00160 if(!cancel_me)
00161 {
00162 subjob_name = new_name;
00163 }
00164 }
00165 subjob_name_mutex.unlock();
00166
00167 }
00168
00169 QString JobThread::getJobName()
00170 {
00171 QMutexLocker qml(&job_name_mutex);
00172 return QDeepCopy<QString>(job_name);
00173 }
00174
00175 QString JobThread::getSubName()
00176 {
00177 QMutexLocker qml(&subjob_name_mutex);
00178 return QDeepCopy<QString>(subjob_name);
00179 }
00180
00181 void JobThread::problem(const QString &a_problem)
00182 {
00183
00184
00185
00186
00187 ErrorEvent *ee = new ErrorEvent(a_problem);
00188 QApplication::postEvent(parent, ee);
00189
00190 setProblem(a_problem);
00191
00192 }
00193
00194
00195 void JobThread::sendLoggingEvent(const QString &event_string)
00196 {
00197
00198
00199
00200
00201
00202 LoggingEvent *le = new LoggingEvent(event_string);
00203 QApplication::postEvent(parent, le);
00204 }
00205
00206 void JobThread::setJobName(const QString &jname)
00207 {
00208 QMutexLocker qml(&job_name_mutex);
00209 job_name = jname;
00210 }
00211
00212 void JobThread::setProblem(const QString &prob)
00213 {
00214 QMutexLocker qml(&problem_string_mutex);
00215 problem_string = prob;
00216 }
00217
00218
00219
00220
00221
00222 namespace
00223 {
00224 class MutexUnlocker
00225 {
00226 public:
00227 MutexUnlocker(QMutex *mutex) : m_mutex(mutex)
00228 {
00229 if (!(m_mutex && m_mutex->locked()))
00230 {
00231 VERBOSE(VB_IMPORTANT,
00232 QString("%1: Invalid mutex passed to MutexUnlocker")
00233 .arg(__FILE__));
00234 }
00235 }
00236
00237 ~MutexUnlocker()
00238 {
00239 m_mutex->unlock();
00240 }
00241
00242 private:
00243 QMutex *m_mutex;
00244 };
00245
00246 template <typename HTYPE>
00247 struct is_handle_null
00248 {
00249 bool operator()(HTYPE handle)
00250 {
00251 return handle == NULL;
00252 }
00253 };
00254
00255 template <typename HTYPE>
00256 struct is_bad_stdio_handle
00257 {
00258 bool operator()(HTYPE handle)
00259 {
00260 return handle == -1;
00261 }
00262 };
00263
00264
00265 template <typename HTYPE, typename CLEANF_RET = void,
00266 typename handle_checker = is_handle_null<HTYPE> >
00267 class SmartHandle
00268 {
00269 private:
00270 typedef CLEANF_RET (*clean_fun_t)(HTYPE);
00271
00272 public:
00273 SmartHandle(HTYPE handle, clean_fun_t cleaner) : m_handle(handle),
00274 m_cleaner(cleaner)
00275 {
00276 }
00277
00278 ~SmartHandle() {
00279 handle_checker hc;
00280 if (!hc(m_handle))
00281 {
00282 m_cleaner(m_handle);
00283 }
00284 }
00285
00286 HTYPE get()
00287 {
00288 return m_handle;
00289 }
00290
00291 HTYPE operator->() {
00292 return m_handle;
00293 }
00294
00295 private:
00296 HTYPE m_handle;
00297 clean_fun_t m_cleaner;
00298 };
00299 }
00300
00301 DVDThread::DVDThread(MTD *owner,
00302 QMutex *drive_mutex,
00303 const QString &dvd_device,
00304 int track,
00305 const QString &dest_file,
00306 const QString &name,
00307 const QString &start_string,
00308 int nice_priority)
00309 :JobThread(owner, start_string, nice_priority)
00310 {
00311 dvd_device_access = drive_mutex;
00312 dvd_device_location = dvd_device;
00313 dvd_title = track - 1;
00314 destination_file_string = dest_file;
00315 rip_name = name;
00316 }
00317
00318 void DVDThread::run()
00319 {
00320 VERBOSE(VB_IMPORTANT, "Somebody ran an actual (base class) DVDThread. I"
00321 " don't think that's supposed to happen.");
00322 }
00323
00324
00325 bool DVDThread::ripTitle(int title_number,
00326 const QString &to_location,
00327 const QString &extension,
00328 bool multiple_files,
00329 QStringList *output_files)
00330 {
00331
00332
00333
00334
00335
00336 bool loop = true;
00337
00338 setSubName(QObject::tr("Waiting For Access to DVD"), 1);
00339
00340 while(loop)
00341 {
00342 if(dvd_device_access->tryLock())
00343 {
00344 loop = false;
00345 }
00346 else
00347 {
00348 if(keepGoing())
00349 {
00350 sleep(5);
00351 }
00352 else
00353 {
00354 problem("abandoned job because master control said we need to shut down");
00355 return false;
00356 }
00357 }
00358 }
00359
00360 MutexUnlocker qmul(dvd_device_access);
00361
00362 if(!keepGoing())
00363 {
00364 problem("abandoned job because master control said we need to shut down");
00365 return false;
00366 }
00367
00368 sendLoggingEvent("job thread beginning to rip dvd title");
00369
00370
00371
00372
00373
00374
00375
00376 RipFile ripfile(to_location, extension, true);
00377 if(!ripfile.open(IO_WriteOnly | IO_Raw | IO_Truncate, multiple_files))
00378 {
00379 problem(QString("DVDPerfectThread could not open output file: %1")
00380 .arg(ripfile.name()));
00381 return false;
00382 }
00383
00384
00385
00386
00387
00388
00389 int angle = 0;
00390
00391 SmartHandle<dvd_reader_t *> the_dvd(DVDOpen(dvd_device_location),
00392 DVDClose);
00393 if(!the_dvd.get())
00394 {
00395 problem(QString("DVDPerfectThread could not access this dvd device: %1").arg(dvd_device_location));
00396 return false;
00397 }
00398
00399
00400 SmartHandle<ifo_handle_t *> vmg_file(ifoOpen(the_dvd.get(), 0), ifoClose);
00401 if(!vmg_file.get())
00402 {
00403 problem("DVDPerfectThread could not open VMG info.");
00404 return false;
00405 }
00406 tt_srpt_t *tt_srpt = vmg_file->tt_srpt;
00407
00408
00409
00410
00411
00412 if(title_number < 0 || title_number > tt_srpt->nr_of_srpts )
00413 {
00414 problem(QString("DVDPerfectThread could not open title number %1").arg(title_number + 1));
00415 return false;
00416 }
00417
00418 SmartHandle<ifo_handle_t *> vts_file(
00419 ifoOpen(the_dvd.get(), tt_srpt->title[title_number].title_set_nr),
00420 ifoClose);
00421 if(!vts_file.get())
00422 {
00423 problem("DVDPerfectThread could not open the title's info file");
00424 return false;
00425 }
00426
00427
00428
00429
00430 int ttn = tt_srpt->title[title_number].vts_ttn;
00431 vts_ptt_srpt_t *vts_ptt_srpt = vts_file->vts_ptt_srpt;
00432 int pgc_id = vts_ptt_srpt->title[ ttn - 1 ].ptt[ 0 ].pgcn;
00433 int pgn = vts_ptt_srpt->title[ ttn - 1 ].ptt[ 0 ].pgn;
00434 pgc_t *cur_pgc = vts_file->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc;
00435 int start_cell = cur_pgc->program_map[ pgn - 1 ] - 1;
00436
00437
00438
00439
00440
00441
00442
00443
00444 int total_sectors = 0;
00445
00446 for(int i = start_cell; i < cur_pgc->nr_of_cells; i++)
00447 {
00448 total_sectors += cur_pgc->cell_playback[i].last_sector -
00449 cur_pgc->cell_playback[i].first_sector;
00450 }
00451
00452
00453
00454
00455
00456 SmartHandle<dvd_file_t *> title(
00457 DVDOpenFile(the_dvd.get(),
00458 tt_srpt->title[title_number].title_set_nr,
00459 DVD_READ_TITLE_VOBS),
00460 DVDCloseFile);
00461 if(!title.get())
00462 {
00463 problem("DVDPerfectThread could not open the title's actual VOB(s)");
00464 return false;
00465 }
00466
00467 int sector_counter = 0;
00468
00469 QTime job_time;
00470 job_time.start();
00471
00472 std::vector<unsigned char> video_data(1024 * DVD_VIDEO_LB_LEN);
00473
00474 int next_cell = start_cell;
00475 for(int cur_cell = start_cell; next_cell < cur_pgc->nr_of_cells; )
00476 {
00477 cur_cell = next_cell;
00478 if(cur_pgc->cell_playback[ cur_cell ].block_type == BLOCK_TYPE_ANGLE_BLOCK)
00479 {
00480
00481 cur_cell += angle;
00482 for(int i=0;; ++i)
00483 {
00484 if(cur_pgc->cell_playback[ cur_cell + i ].block_mode == BLOCK_MODE_LAST_CELL)
00485 {
00486 next_cell = cur_cell + i + 1;
00487 break;
00488 }
00489 }
00490 }
00491 else
00492 {
00493 next_cell = cur_cell + 1;
00494 }
00495
00496
00497
00498
00499
00500 for(uint cur_pack = cur_pgc->cell_playback[ cur_cell ].first_sector;
00501 cur_pack < cur_pgc->cell_playback[ cur_cell ].last_sector; )
00502 {
00503 dsi_t dsi_pack;
00504 unsigned int next_vobu, next_ilvu_start, cur_output_size;
00505
00506
00507
00508
00509
00510 int len = DVDReadBlocks(title.get(), (int) cur_pack, 1,
00511 &video_data[0]);
00512 if( len != 1)
00513 {
00514 problem(QString("DVDPerfectThread read failed for block %1")
00515 .arg(cur_pack));
00516 return false;
00517 }
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528 navRead_DSI(&dsi_pack, &video_data[DSI_START_BYTE]);
00529
00530
00531
00532
00533
00534
00535 next_ilvu_start = cur_pack+ dsi_pack.sml_agli.data[ angle ].address;
00536 cur_output_size = dsi_pack.dsi_gi.vobu_ea;
00537
00538 if(dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL )
00539 {
00540 next_vobu = cur_pack + ( dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
00541 }
00542 else
00543 {
00544 next_vobu = cur_pack + cur_output_size + 1;
00545 }
00546
00547
00548 cur_pack++;
00549 sector_counter++;
00550
00551
00552
00553
00554
00555 len = DVDReadBlocks(title.get(), (int)cur_pack, cur_output_size,
00556 &video_data[0]);
00557 if( len != (int) cur_output_size )
00558 {
00559 problem(QString("DVDPerfectThread read failed for %1 blocks at %2")
00560 .arg(cur_output_size)
00561 .arg(cur_pack)
00562 );
00563 return false;
00564 }
00565
00566 setSubProgress((double) (sector_counter) / (double) (total_sectors), 1);
00567 overall_progress = subjob_progress * sub_to_overall_multiple;
00568 updateSubjobString(job_time.elapsed() / 1000,
00569 QObject::tr("Ripping to file ~"));
00570 if(!ripfile.writeBlocks(&video_data[0],
00571 cur_output_size * DVD_VIDEO_LB_LEN))
00572 {
00573 problem("Couldn't write blocks during a rip. Filesystem size exceeded? Disc full?");
00574 return false;
00575 }
00576
00577 sector_counter += next_vobu - cur_pack;
00578 cur_pack = next_vobu;
00579
00580
00581
00582
00583
00584
00585
00586 if(!keepGoing())
00587 {
00588 problem("abandoned job because master control said we need to shut down");
00589 return false;
00590 }
00591 }
00592 }
00593
00594
00595
00596
00597
00598 QStringList sl = ripfile.close();
00599 if (output_files) *output_files = sl;
00600
00601 sendLoggingEvent("job thread finished ripping dvd title");
00602 return true;
00603 }
00604
00605 DVDThread::~DVDThread()
00606 {
00607 }
00608
00609
00610
00611
00612
00613 DVDISOCopyThread::DVDISOCopyThread(MTD *owner,
00614 QMutex *drive_mutex,
00615 const QString &dvd_device,
00616 int track,
00617 const QString &dest_file,
00618 const QString &name,
00619 const QString &start_string,
00620 int nice_priority)
00621 :DVDThread(owner,
00622 drive_mutex,
00623 dvd_device,
00624 track,
00625 dest_file,
00626 name,
00627 start_string,
00628 nice_priority)
00629
00630 {
00631
00632 sendLoggingEvent(QString("Using DVD source: %1").arg(dvd_device));
00633 }
00634
00635 void DVDISOCopyThread::run()
00636 {
00637
00638
00639
00640
00641 nice(nice_level);
00642 setJobName(QString(QObject::tr("ISO copy of %1")).arg(rip_name));
00643 if(keepGoing())
00644 {
00645 copyFullDisc();
00646 }
00647 }
00648
00649 bool DVDISOCopyThread::copyFullDisc(void)
00650 {
00651 bool loop = true;
00652
00653 setSubName(QObject::tr("Waiting for access to DVD"), 1);
00654
00655 while(loop)
00656 {
00657 if(dvd_device_access->tryLock())
00658 {
00659 loop = false;
00660 }
00661 else
00662 {
00663 if(keepGoing())
00664 {
00665 sleep(5);
00666 }
00667 else
00668 {
00669 problem("abandoned job because master control said we need to shut down");
00670 return false;
00671 }
00672 }
00673 }
00674
00675 MutexUnlocker qmul(dvd_device_access);
00676 if(!keepGoing())
00677 {
00678 problem("abandoned job because master control said we need to shut down");
00679 return false;
00680 }
00681
00682 RipFile ripfile(destination_file_string, ".iso", true);
00683 if(!ripfile.open(IO_WriteOnly | IO_Raw | IO_Truncate, false))
00684 {
00685 problem(QString("DVDISOCopyThread could not open output file: %1")
00686 .arg(ripfile.name()));
00687 return false;
00688 }
00689
00690 sendLoggingEvent(QString("ISO DVD image copy to: %1").arg(ripfile.name()));
00691
00692 SmartHandle<int, int, is_bad_stdio_handle<int> > file(
00693 open(dvd_device_location, O_RDONLY), close);
00694 if(file.get() == -1)
00695 {
00696 problem(QString("DVDISOCopyThread could not open dvd device: %1")
00697 .arg(dvd_device_location));
00698 return false;
00699 }
00700
00701 off_t dvd_size = lseek(file.get(), 0, SEEK_END);
00702 lseek(file.get(), 0, SEEK_SET);
00703
00704
00705 if (dvd_size < 2048)
00706 sendLoggingEvent(QString("DVDISOCopyThread: bad disk size (%1 bytes)")
00707 .arg(dvd_size));
00708
00709 const int buf_size = 1024 * 1024;
00710 std::vector<unsigned char> buffer(buf_size);
00711 long long total_bytes(0);
00712
00713 QTime job_time;
00714 job_time.start();
00715
00716 while (1)
00717 {
00718 int bytes_read = read(file.get(), &buffer[0], buf_size);
00719 if (bytes_read == -1)
00720 {
00721 perror("read");
00722 problem(QString("DVDISOCopyThread dvd device read error"));
00723 return false;
00724 }
00725 if (bytes_read == 0)
00726 {
00727 break;
00728 }
00729
00730 if (!ripfile.writeBlocks(&buffer[0], bytes_read))
00731 {
00732 problem(QString("DVDISOCopyThread rip file write error"));
00733 return false;
00734 }
00735
00736 total_bytes += bytes_read;
00737
00738 setSubProgress((double) (total_bytes) / (double) (dvd_size), 1);
00739 overall_progress = subjob_progress * sub_to_overall_multiple;
00740 updateSubjobString(job_time.elapsed() / 1000,
00741 QObject::tr("Ripping to file ~"));
00742
00743
00744
00745
00746
00747
00748 if (!keepGoing())
00749 {
00750 problem("abandoned job because master control said we need to shut down");
00751 return false;
00752 }
00753 }
00754
00755 ripfile.close();
00756 sendLoggingEvent("job thread finished copying ISO image");
00757 return true;
00758 }
00759
00760 DVDISOCopyThread::~DVDISOCopyThread()
00761 {
00762 }
00763
00764
00765
00766
00767
00768 DVDPerfectThread::DVDPerfectThread(MTD *owner,
00769 QMutex *drive_mutex,
00770 const QString &dvd_device,
00771 int track,
00772 const QString &dest_file,
00773 const QString &name,
00774 const QString &start_string,
00775 int nice_priority)
00776 :DVDThread(owner,
00777 drive_mutex,
00778 dvd_device,
00779 track,
00780 dest_file,
00781 name,
00782 start_string,
00783 nice_priority)
00784
00785 {
00786 }
00787
00788 void DVDPerfectThread::run()
00789 {
00790
00791
00792
00793
00794 nice(nice_level);
00795 setJobName(QString(QObject::tr("Perfect DVD Rip of %1")).arg(rip_name));
00796 if(keepGoing())
00797 {
00798 ripTitle(dvd_title, destination_file_string, ".vob", true);
00799 }
00800 }
00801
00802
00803 DVDPerfectThread::~DVDPerfectThread()
00804 {
00805 }
00806
00807
00808
00809
00810
00811 DVDTranscodeThread::DVDTranscodeThread(MTD *owner,
00812 QMutex *drive_mutex,
00813 const QString &dvd_device,
00814 int track,
00815 const QString &dest_file,
00816 const QString &name,
00817 const QString &start_string,
00818 int nice_priority,
00819 int quality_level,
00820 bool do_ac3,
00821 int which_audio,
00822 int numb_seconds,
00823 int subtitle_track_numb)
00824 :DVDThread(owner,
00825 drive_mutex,
00826 dvd_device,
00827 track,
00828 dest_file,
00829 name,
00830 start_string,
00831 nice_priority)
00832
00833 {
00834 quality = quality_level;
00835 ac3_flag = do_ac3;
00836 working_directory = NULL;
00837 tc_process = NULL;
00838 two_pass = false;
00839 audio_track = which_audio;
00840 length_in_seconds = numb_seconds;
00841 if(length_in_seconds == 0)
00842 {
00843 length_in_seconds = 1;
00844 }
00845 subtitle_track = subtitle_track_numb;
00846 used_transcode_slot = false;
00847 }
00848
00849 void DVDTranscodeThread::run()
00850 {
00851
00852
00853
00854
00855 nice(nice_level);
00856
00857
00858
00859
00860
00861 if(keepGoing())
00862 {
00863 if(!makeWorkingDirectory())
00864 {
00865 return;
00866 }
00867 }
00868
00869
00870
00871
00872
00873
00874
00875 if(keepGoing())
00876 {
00877 if(!buildTranscodeCommandLine(1))
00878 {
00879 cleanUp();
00880 return;
00881 }
00882 if(two_pass)
00883 {
00884 setJobName(QString(QObject::tr("Transcode of %1")).arg(rip_name));
00885 sub_to_overall_multiple = 0.333333333;
00886 }
00887 else
00888 {
00889 setJobName(QString(QObject::tr("Transcode of %1")).arg(rip_name));
00890 sub_to_overall_multiple = 0.50;
00891 }
00892 }
00893
00894
00895
00896
00897
00898 QStringList output_files;
00899 if(keepGoing())
00900 {
00901 QString rip_file_string = QString("%1/vob/%2").arg(working_directory->path()).arg(rip_name);
00902 if(!ripTitle(dvd_title, rip_file_string, ".vob", true, &output_files))
00903 {
00904 cleanUp();
00905 return;
00906 }
00907 }
00908
00909
00910
00911
00912
00913
00914
00915 setSubName(QObject::tr("Waiting for Permission to Start Transcoding"), 1);
00916
00917 bool loop = true;
00918
00919 while(loop)
00920 {
00921 if(parent->isItOkToStartTranscoding() && keepGoing())
00922 {
00923 used_transcode_slot = true;
00924 loop = false;
00925 }
00926 else
00927 {
00928 if(keepGoing())
00929 {
00930 sleep(5);
00931 }
00932 else
00933 {
00934 problem("abandoned job because master control said we need to shut down");
00935 return;
00936 }
00937 }
00938 }
00939
00940
00941
00942
00943
00944
00945
00946 if(keepGoing())
00947 {
00948 if(!runTranscode(1))
00949 {
00950 wipeClean();
00951 return;
00952 }
00953 }
00954
00955 if(two_pass)
00956 {
00957 if(keepGoing())
00958 {
00959 if(!runTranscode(2))
00960 {
00961 wipeClean();
00962 }
00963 }
00964 }
00965
00966 if (!gContext->GetNumSetting("mythdvd.mtd.SaveTranscodeIntermediates", 0)) {
00967
00968 std::for_each(output_files.begin(), output_files.end(), delete_file());
00969 }
00970
00971 cleanUp();
00972 }
00973
00974 bool DVDTranscodeThread::makeWorkingDirectory()
00975 {
00976 QString dir_name = gContext->GetSetting("DVDRipLocation");
00977 if( dir_name.length() < 1)
00978 {
00979 problem("could not find rip directory in settings");
00980 return false;
00981 }
00982
00983 working_directory = new QDir (dir_name);
00984 if(!working_directory->exists())
00985 {
00986 problem("rip directory does not seem to exist");
00987 return false;
00988 }
00989 if(!working_directory->mkdir(rip_name))
00990 {
00991 problem(QString("could not create directory called \"%1\" in rip directory").arg(rip_name));
00992 return false;
00993 }
00994 if(!working_directory->cd(rip_name))
00995 {
00996 problem(QString("could not cd into \"%1\"").arg(rip_name));
00997 return false;
00998 }
00999 if(!working_directory->mkdir("vob"))
01000 {
01001 problem("could not create a vob subdirectory in the working directory");
01002 return false;
01003 }
01004
01005 return true;
01006 }
01007
01008 bool DVDTranscodeThread::buildTranscodeCommandLine(int which_run)
01009 {
01010
01011
01012
01013
01014 QFile a_file(QString("%1.avi").arg(destination_file_string));
01015 if(a_file.exists())
01016 {
01017 problem("transcode cannot run, destination file already exists");
01018 return false;
01019 }
01020
01021
01022
01023 QString tc_command = gContext->GetSetting("TranscodeCommand");
01024 if(tc_command.length() < 1)
01025 {
01026 problem("there is no TranscodeCommand setting for this system");
01027 return false;
01028 }
01029
01030 tc_arguments.clear();
01031 tc_arguments.append(tc_command);
01032
01033
01034
01035
01036
01037
01038 QString q_string = QString("SELECT sync_mode, "
01039 " use_yv12, "
01040 " cliptop, "
01041 " clipbottom, "
01042 " clipleft, "
01043 " clipright, "
01044 " f_resize_h, "
01045 " f_resize_w, "
01046 " hq_resize_h, "
01047 " hq_resize_w, "
01048 " grow_h, "
01049 " grow_w, "
01050 " clip2top, "
01051 " clip2bottom, "
01052 " clip2left, "
01053 " clip2right, "
01054 " codec, "
01055 " codec_param, "
01056 " bitrate, "
01057 " a_sample_r, "
01058 " a_bitrate, "
01059 " input, "
01060 " name, "
01061 " two_pass, "
01062 " tc_param "
01063
01064 " FROM dvdtranscode WHERE intid = %1 ;")
01065 .arg(quality);
01066
01067 MSqlQuery a_query(MSqlQuery::InitCon());
01068 a_query.exec(q_string);
01069
01070 if(a_query.isActive() && a_query.size() > 0)
01071 {
01072 a_query.next();
01073 }
01074 else
01075 {
01076 problem(QString("sql query failed: %1").arg(q_string));
01077 return false;
01078 }
01079
01080
01081
01082
01083
01084 int sync_mode = a_query.value(0).toInt();
01085 bool use_yv12 = a_query.value(1).toBool();
01086 int cliptop = a_query.value(2).toInt();
01087 int clipbottom = a_query.value(3).toInt();
01088 int clipleft = a_query.value(4).toInt();
01089 int clipright = a_query.value(5).toInt();
01090 int f_resize_h = a_query.value(6).toInt();
01091 int f_resize_w = a_query.value(7).toInt();
01092 int hq_resize_h = a_query.value(8).toInt();
01093 int hq_resize_w = a_query.value(9).toInt();
01094 int grow_h = a_query.value(10).toInt();
01095 int grow_w = a_query.value(11).toInt();
01096 int clipttop = a_query.value(12).toInt();
01097 int cliptbottom = a_query.value(13).toInt();
01098 int cliptleft = a_query.value(14).toInt();
01099 int cliptright = a_query.value(15).toInt();
01100 QString codec = a_query.value(16).toString();
01101 QString codec_param = a_query.value(17).toString();
01102 int bitrate = a_query.value(18).toInt();
01103 int a_sample_r = a_query.value(19).toInt();
01104 int a_bitrate = a_query.value(20).toInt();
01105 int input_setting = a_query.value(21).toInt();
01106 QString name = a_query.value(22).toString();
01107 two_pass = a_query.value(23).toBool();
01108 QString tc_param = a_query.value(24).toString();
01109
01110
01111
01112
01113
01114
01115 q_string = QString("SELECT hsize, vsize, fr_code FROM dvdinput WHERE intid = %1 ;").arg(input_setting);
01116 a_query.exec(q_string);
01117
01118 if(a_query.isActive() && a_query.size() > 0)
01119 {
01120 a_query.next();
01121 }
01122 else
01123 {
01124 problem(QString("couldn't get dvdinput tuple for an intid of %1").arg(input_setting));
01125 return false;
01126 }
01127
01128 int input_hsize = a_query.value(0).toInt();
01129 int input_vsize = a_query.value(1).toInt();
01130 int fr_code = a_query.value(2).toInt();
01131
01132
01133
01134
01135
01136 if(subtitle_track > -1)
01137 {
01138
01139 QString subtitle_arguments = QString("extsub=track=%1")
01140 .arg(subtitle_track);
01141 if(cliptbottom > 0)
01142 {
01143 subtitle_arguments.append(QString(":vershift=%1")
01144 .arg(cliptbottom));
01145 }
01146 tc_arguments.append("-x");
01147 tc_arguments.append("vob");
01148 tc_arguments.append("-J");
01149
01150 tc_arguments.append(subtitle_arguments);
01151 }
01152
01153
01154 tc_arguments.append("-i");
01155 tc_arguments.append(QString("%1/vob/").arg(working_directory->path()));
01156 tc_arguments.append("-g");
01157 tc_arguments.append(QString("%1x%2").arg(input_hsize).arg(input_vsize));
01158
01159 if (!gContext->GetNumSetting("mythvideo.TrustTranscodeFRDetect"))
01160 {
01161 tc_arguments.append("-f");
01162 tc_arguments.append(QString("0,%1").arg(fr_code));
01163 }
01164
01165 tc_arguments.append("-M");
01166 tc_arguments.append(QString("%1").arg(sync_mode));
01167
01168 if(use_yv12)
01169 {
01170 tc_arguments.append("-V");
01171 }
01172
01173
01174
01175
01176 if(clipbottom != 0 ||
01177 cliptop != 0 ||
01178 clipleft != 0 ||
01179 clipright != 0)
01180 {
01181 tc_arguments.append("-j");
01182 tc_arguments.append(QString("%1,%2,%3,%4")
01183 .arg(cliptop)
01184 .arg(clipleft)
01185 .arg(clipbottom)
01186 .arg(clipright));
01187 }
01188 if(grow_h > 0 || grow_w > 0)
01189 {
01190 tc_arguments.append("-X");
01191 tc_arguments.append(QString("%1,%2")
01192 .arg(grow_h)
01193 .arg(grow_w));
01194 }
01195 if(f_resize_h > 0 || f_resize_w > 0)
01196 {
01197 tc_arguments.append("-B");
01198 tc_arguments.append(QString("%1,%2")
01199 .arg(f_resize_h)
01200 .arg(f_resize_w));
01201 }
01202 if(hq_resize_h > 0 && hq_resize_w > 0)
01203 {
01204 tc_arguments.append("-Z");
01205 tc_arguments.append(QString("%1x%2")
01206 .arg(hq_resize_w)
01207 .arg(hq_resize_h));
01208 }
01209 if(cliptbottom != 0 ||
01210 clipttop != 0 ||
01211 cliptleft != 0 ||
01212 cliptright != 0)
01213 {
01214 tc_arguments.append("-Y");
01215 tc_arguments.append(QString("%1,%2,%3,%4")
01216 .arg(clipttop)
01217 .arg(cliptleft)
01218 .arg(cliptbottom)
01219 .arg(cliptright));
01220 }
01221
01222 if(codec.length() < 1)
01223 {
01224 problem("Yo! Kaka-brain! Can't transcode without a codec");
01225 return false;
01226 }
01227 if(codec.contains("divx") && gContext->GetNumSetting("MTDxvidFlag"))
01228 {
01229 codec = "xvid";
01230 }
01231
01232 tc_arguments.append("-y");
01233
01234 if (two_pass && which_run == 1)
01235 tc_arguments.append(QString("%1,null").arg(codec));
01236 else
01237 tc_arguments.append(codec);
01238
01239 if(codec_param.length())
01240 {
01241 tc_arguments.append("-F");
01242 tc_arguments.append(codec_param);
01243 }
01244
01245 if(tc_param.length())
01246 {
01247 tc_arguments += QStringList::split(" ", tc_param);
01248 }
01249
01250 if(bitrate > 0)
01251 {
01252 tc_arguments.append("-w");
01253 tc_arguments.append(QString("%1").arg(bitrate));
01254 }
01255
01256
01257 if(ac3_flag && !name.contains("VCD", false))
01258 {
01259 tc_arguments.append("-A");
01260 tc_arguments.append("-N");
01261 tc_arguments.append("0x2000");
01262 }
01263 else
01264 {
01265 if(a_sample_r > 0)
01266 {
01267 tc_arguments.append("-E");
01268 tc_arguments.append(QString("%1").arg(a_sample_r));
01269 }
01270 if(a_bitrate > 0)
01271 {
01272 tc_arguments.append("-b");
01273 tc_arguments.append(QString("%1").arg(a_bitrate));
01274 }
01275 }
01276 if(audio_track > 1)
01277 {
01278 tc_arguments.append("-a");
01279 tc_arguments.append(QString("%1").arg(audio_track - 1));
01280 }
01281
01282 tc_arguments.append("-o");
01283
01284 if (two_pass && which_run == 1)
01285 tc_arguments.append(QString("/dev/null"));
01286 else
01287 tc_arguments.append(QString("%1.avi").arg(destination_file_string));
01288
01289 tc_arguments.append("--print_status");
01290 tc_arguments.append("20");
01291 tc_arguments.append("--color");
01292 tc_arguments.append("0");
01293
01294
01295 if(two_pass)
01296 {
01297 tc_arguments.append("-R");
01298 tc_arguments.append(QString("%1,twopass.log").arg(which_run));
01299 }
01300
01301
01302 QString transcode_command_string = "transcode command will be: " + tc_arguments.join(" ");
01303 sendLoggingEvent(transcode_command_string);
01304
01305 tc_process = new QProcess(tc_arguments);
01306 tc_process->setWorkingDirectory(*working_directory);
01307 return true;
01308 }
01309
01310 bool DVDTranscodeThread::runTranscode(int which_run)
01311 {
01312
01313
01314
01315
01316
01317 setSubName("Transcode is thinking ...", 1);
01318 setSubProgress(0.0, 1);
01319 uint tick_tock = 3;
01320 uint seconds_so_far = 0;
01321 double percent_transcoded = 0.0;
01322 QTime job_time;
01323 bool finally = true;
01324
01325 if(which_run > 1)
01326 {
01327
01328
01329
01330
01331 if(tc_process)
01332 {
01333 delete tc_process;
01334 tc_process = NULL;
01335 }
01336
01337 if (!buildTranscodeCommandLine(which_run))
01338 {
01339 problem( "Problem building second pass command line." );
01340 return false;
01341 }
01342 }
01343
01344
01345
01346
01347
01348
01349 if(!tc_process->start())
01350 {
01351 problem("Could not start transcode");
01352 return false;
01353 }
01354 while(true)
01355 {
01356 if(!keepGoing())
01357 {
01358
01359
01360
01361
01362 problem("was transcoding, but the mtd shut me down");
01363 tc_process->tryTerminate();
01364 sleep(3);
01365 tc_process->kill();
01366 delete tc_process;
01367 tc_process = NULL;
01368 return FALSE;
01369 }
01370 if(tc_process->isRunning())
01371 {
01372 while(tc_process->canReadLineStdout())
01373 {
01374
01375
01376
01377
01378
01379
01380
01381 QString status_line = tc_process->readLineStdout();
01382 status_line = status_line.section("EMT: ", 1, 1);
01383 status_line = status_line.section(",",0,0);
01384 QString h_string = status_line.section(":",0,0);
01385 QString m_string = status_line.section(":",1,1);
01386 QString s_string = status_line.section(":", 2,2);
01387
01388 seconds_so_far = s_string.toUInt() +
01389 (60 * m_string.toUInt()) +
01390 (60 * 60 * h_string.toUInt());
01391
01392 percent_transcoded = (double) ( (double) seconds_so_far / (double) length_in_seconds);
01393 }
01394 if(seconds_so_far > 0)
01395 {
01396 if(finally)
01397 {
01398 finally = false;
01399 job_time.start();
01400 }
01401 if(two_pass)
01402 {
01403 if(which_run == 1)
01404 {
01405 setSubProgress(percent_transcoded, 1);
01406 overall_progress = 0.333333 + (0.333333 * percent_transcoded);
01407 updateSubjobString(job_time.elapsed() / 1000,
01408 QObject::tr("Transcoding Pass 1 of 2 ~"));
01409 }
01410 else if(which_run == 2)
01411 {
01412 setSubProgress(percent_transcoded, 1);
01413 overall_progress = 0.666666 + (0.333333 * percent_transcoded);
01414 updateSubjobString(job_time.elapsed() / 1000,
01415 QObject::tr("Transcoding Pass 2 of 2 ~"));
01416 }
01417 }
01418 else
01419 {
01420
01421
01422
01423
01424
01425 setSubProgress(percent_transcoded, 1);
01426 overall_progress = 0.50 + (0.50 * percent_transcoded);
01427 updateSubjobString(job_time.elapsed() / 1000,
01428 QObject::tr("Transcoding ~"));
01429 }
01430 }
01431 else
01432 {
01433 ++tick_tock;
01434 if (tick_tock > 3)
01435 {
01436 tick_tock = 1;
01437 }
01438 QString a_string = QObject::tr("Transcode is thinking ");
01439 for(uint i = 0; i < tick_tock; i++)
01440 {
01441 a_string += ".";
01442 }
01443 setSubName(a_string, 1);
01444 }
01445 sleep(2);
01446 }
01447 else
01448 {
01449 bool flag = tc_process->normalExit();
01450 delete tc_process;
01451 tc_process = NULL;
01452 return flag;
01453 }
01454 }
01455
01456
01457
01458
01459
01460 delete tc_process;
01461 tc_process = NULL;
01462 return false;
01463 }
01464
01465 void DVDTranscodeThread::cleanUp()
01466 {
01467
01468
01469
01470
01471 if(working_directory)
01472 {
01473 if(two_pass)
01474 {
01475 working_directory->remove("twopass.log");
01476 }
01477 working_directory->rmdir("vob");
01478 working_directory->cd("..");
01479 working_directory->rmdir(rip_name);
01480 delete working_directory;
01481 working_directory = NULL;
01482 }
01483 }
01484
01485 void DVDTranscodeThread::wipeClean()
01486 {
01487
01488
01489
01490
01491
01492 cleanUp();
01493 QDir::current().remove(QString("%1.avi").arg(destination_file_string));
01494 }
01495
01496 DVDTranscodeThread::~DVDTranscodeThread()
01497 {
01498 if(working_directory)
01499 {
01500 delete working_directory;
01501 }
01502 if(tc_process)
01503 {
01504 delete tc_process;
01505 }
01506 }