00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include <sys/types.h>
00014 #include <sys/stat.h>
00015 #if defined(__linux__) || defined(__FreeBSD__)
00016 #include <sys/ioctl.h>
00017 #include <linux/cdrom.h>
00018 #endif
00019 #include <fcntl.h>
00020 #include <unistd.h>
00021
00022 #include <cmath>
00023 #include <climits>
00024
00025 #include "dvdprobe.h"
00026
00027 #include <mythtv/mythcontext.h>
00028 #include <mythtv/mythdbcon.h>
00029
00030 namespace
00031 {
00032
00033 uint decdt(uint8_t c)
00034 {
00035 return 10 * (c >> 4) + (c & 0xF);
00036 }
00037
00038 bool fcomp(float lhs, float rhs, float ep = 0.0001)
00039 {
00040 return std::fabs((lhs - rhs) / rhs) <= ep;
00041 }
00042
00043 QString lctola(uint16_t lang_code)
00044 {
00045 return QString("%1%2").arg(char(lang_code >> 8))
00046 .arg(char(lang_code & 0xff));
00047 }
00048 }
00049
00050 DVDSubTitle::DVDSubTitle(int subtitle_id, const QString &a_language) :
00051 id(subtitle_id), language(a_language.utf8())
00052 {
00053 if (language.isEmpty())
00054 language = "unknown";
00055 }
00056
00057 DVDAudio::DVDAudio() : audio_format(""), multichannel_extension(false),
00058 language(""), application_mode(""), quantization(""),
00059 sample_frequency(""), channels(0), language_extension("")
00060 {
00061 }
00062
00063 void DVDAudio::printYourself()
00064 {
00065
00066
00067
00068
00069 VERBOSE(VB_GENERAL, QString(" Audio track: %1 %2 %3 %4 Ch")
00070 .arg(language)
00071 .arg(audio_format)
00072 .arg(sample_frequency)
00073 .arg(channels));
00074 }
00075
00076 QString DVDAudio::getAudioString()
00077 {
00078 return QString("%1 %2 %3Ch").arg(language).arg(audio_format).arg(channels);
00079 }
00080
00081 void DVDAudio::fill(audio_attr_t *audio_attributes)
00082 {
00083
00084
00085
00086
00087
00088
00089 switch (audio_attributes->audio_format)
00090 {
00091 case 0:
00092 audio_format = "ac3";
00093 break;
00094 case 1:
00095 audio_format = "oops";
00096 break;
00097 case 2:
00098 audio_format = "mpeg1";
00099 break;
00100 case 3:
00101 audio_format = "mpeg2ext";
00102 break;
00103 case 4:
00104 audio_format = "lpcm";
00105 break;
00106 case 5:
00107 audio_format = "oops";
00108 break;
00109 case 6:
00110 audio_format = "dts";
00111 break;
00112 default:
00113 audio_format = "oops";
00114 }
00115
00116 if (audio_attributes->multichannel_extension)
00117 {
00118 multichannel_extension = true;
00119 }
00120
00121 switch (audio_attributes->lang_type)
00122 {
00123 case 0:
00124 language = "nl";
00125 break;
00126 case 1:
00127 language = lctola(audio_attributes->lang_code);
00128 break;
00129 default:
00130 language = "oops";
00131 }
00132
00133 switch (audio_attributes->application_mode)
00134 {
00135 case 0:
00136 application_mode = "unknown";
00137 break;
00138 case 1:
00139 application_mode = "karaoke";
00140 break;
00141 case 2:
00142 application_mode = "surround sound";
00143 break;
00144 default:
00145 application_mode = "oops";
00146 }
00147
00148 switch (audio_attributes->quantization)
00149 {
00150 case 0:
00151 quantization = "16bit";
00152 break;
00153 case 1:
00154 quantization = "20bit";
00155 break;
00156 case 2:
00157 quantization = "24bit";
00158 break;
00159 case 3:
00160 quantization = "drc";
00161 break;
00162 default:
00163 quantization = "oops";
00164 }
00165
00166 switch (audio_attributes->sample_frequency)
00167 {
00168 case 0:
00169 sample_frequency = "48kHz";
00170 break;
00171 default:
00172 sample_frequency = "oops";
00173 }
00174
00175 channels = audio_attributes->channels + 1;
00176
00177 switch (audio_attributes->lang_extension)
00178 {
00179 case 0:
00180 language_extension = "Unknown";
00181 break;
00182 case 1:
00183 language_extension = "Normal Caption";
00184 break;
00185 case 2:
00186 language_extension = "Audio for Visually Impaired";
00187 break;
00188 case 3:
00189 language_extension = "Directors 1";
00190 break;
00191 case 4:
00192 language_extension = "Directors 2 or Music Score";
00193 break;
00194 default:
00195 language_extension = "oops";
00196 }
00197 }
00198
00199 DVDAudio::~DVDAudio()
00200 {
00201 }
00202
00203
00204
00205
00206
00207 DVDTitle::DVDTitle() : numb_chapters(0), numb_angles(0), track_number(0),
00208 hours(0), minutes(0), seconds(0), hsize(0), vsize(0), frame_rate(0.0),
00209 fr_code(0), ar_numerator(0), ar_denominator(0), aspect_ratio(""),
00210 letterbox(false), video_format("unknown"), dvdinput_id(0)
00211 {
00212 audio_tracks.setAutoDelete(true);
00213 subtitles.setAutoDelete(true);
00214 }
00215
00216 void DVDTitle::setTime(uint h, uint m, uint s, double fr)
00217 {
00218 hours = h;
00219 minutes = m;
00220 seconds = s;
00221 frame_rate = fr;
00222
00223
00224
00225
00226
00227 if (fr > 23.0 && fr < 24.0)
00228 fr_code = 1;
00229 else if (fcomp(fr, 24.0))
00230 fr_code = 2;
00231 else if (fcomp(fr, 25.0))
00232 fr_code = 3;
00233 else if (fr > 20.0 && fr < 30.0)
00234 fr_code = 4;
00235 else if (fcomp(fr, 30.0))
00236 fr_code = 5;
00237 else if (fcomp(fr, 50.0))
00238 fr_code = 6;
00239 else if (fr > 59.0 && fr < 60.0)
00240 fr_code = 7;
00241 else if (fcomp(fr, 60.0))
00242 fr_code = 8;
00243 else if (fcomp(fr, 1.0))
00244 fr_code = 9;
00245 else if (fcomp(fr, 5.0))
00246 fr_code = 10;
00247 else if (fcomp(fr, 10.0))
00248 fr_code = 11;
00249 else if (fcomp(fr, 12.0))
00250 fr_code = 12;
00251 else if (fcomp(fr, 15.0))
00252 fr_code = 13;
00253 else
00254 {
00255 fr_code = 0;
00256 VERBOSE(VB_IMPORTANT,
00257 QString("dvdprobe.o: Could not find a frame rate code given a"
00258 " frame rate of %1").arg(fr));
00259 }
00260 }
00261
00262 void DVDTitle::setAR(uint n, uint d, const QString &ar)
00263 {
00264 ar_numerator = n;
00265 ar_denominator = d;
00266 aspect_ratio = ar;
00267 }
00268
00269 uint DVDTitle::getPlayLength()
00270 {
00271 return seconds + (60 * minutes) + (60 * 60 * hours);
00272 }
00273
00274 QString DVDTitle::getTimeString()
00275 {
00276 return QString().sprintf("%d:%02d:%02d", hours, minutes, seconds);
00277 }
00278
00279 void DVDTitle::printYourself()
00280 {
00281
00282
00283
00284
00285 VERBOSE(VB_IMPORTANT, QString("Track %1 has %2 chapters, %3 angles, "
00286 "and runs for %4 hour(s) %5 minute(s) "
00287 "and %6 seconds at %7 fps.")
00288 .arg(track_number).arg(numb_chapters).arg(numb_angles).arg(hours)
00289 .arg(minutes).arg(seconds).arg(frame_rate));
00290
00291 if (audio_tracks.count() > 0)
00292 {
00293 for (uint i = 0; i < audio_tracks.count(); ++i)
00294 {
00295 audio_tracks.at(i)->printYourself();
00296 }
00297 }
00298 else
00299 {
00300 VERBOSE(VB_IMPORTANT, " No Audio Tracks");
00301 }
00302 }
00303
00304 void DVDTitle::addAudio(DVDAudio *new_audio_track)
00305 {
00306 audio_tracks.append(new_audio_track);
00307 }
00308
00309 void DVDTitle::addSubTitle(DVDSubTitle *new_subtitle)
00310 {
00311
00312
00313
00314
00315
00316 int lang_count = 0;
00317 for (uint i = 0; i < subtitles.count(); ++i)
00318 {
00319 if (subtitles.at(i)->getLanguage() == new_subtitle->getLanguage())
00320 {
00321 ++lang_count;
00322 }
00323 }
00324 if (lang_count == 0)
00325 {
00326 new_subtitle->setName(QString("<%1>").arg(new_subtitle->getLanguage()));
00327 }
00328 else
00329 {
00330 ++lang_count;
00331 new_subtitle->setName(QString("<%1> (%2)")
00332 .arg(new_subtitle->getLanguage())
00333 .arg(lang_count));
00334 }
00335 subtitles.append(new_subtitle);
00336 }
00337
00338 void DVDTitle::determineInputID()
00339 {
00340 MSqlQuery a_query(MSqlQuery::InitCon());
00341 a_query.prepare("SELECT intid FROM dvdinput WHERE "
00342 "hsize = :HSIZE and "
00343 "vsize = :VSIZE and "
00344 "ar_num = :ARNUM and "
00345 "ar_denom = :ARDENOM and "
00346 "fr_code = :FRCODE and "
00347 "letterbox = :LETTERBOX and "
00348 "v_format = :VFORMAT;");
00349 a_query.bindValue(":HSIZE", hsize);
00350 a_query.bindValue(":VSIZE", vsize);
00351 a_query.bindValue(":ARNUM", ar_numerator);
00352 a_query.bindValue(":ARDENOM", ar_denominator);
00353 a_query.bindValue(":FRCODE", fr_code);
00354 a_query.bindValue(":LETTERBOX", letterbox);
00355 a_query.bindValue(":VFORMAT", video_format);
00356
00357 if (a_query.exec() && a_query.isActive() && a_query.size() > 0)
00358 {
00359 a_query.next();
00360 dvdinput_id = a_query.value(0).toInt();
00361 }
00362 else
00363 {
00364 QString msg =
00365 QString("You have a title on your dvd in a format myth doesn't"
00366 " understand.\n"
00367 "Either that, or you haven't installed the dvdinput"
00368 " table.\n"
00369 "You probably want to report this to a mailing list or"
00370 " something:\n"
00371 " height = %1\n"
00372 " width = %2\n"
00373 " aspect ratio = %3 (%4/%5)\n"
00374 " frame rate = %6\n"
00375 " fr_code = %7\n"
00376 " letterboxed = %8\n"
00377 " format = %9")
00378 .arg(hsize).arg(vsize)
00379 .arg(aspect_ratio).arg(ar_numerator).arg(ar_denominator)
00380 .arg(frame_rate).arg(fr_code).arg(letterbox).arg(video_format);
00381 VERBOSE(VB_IMPORTANT, msg);
00382 }
00383 }
00384
00385 DVDTitle::~DVDTitle()
00386 {
00387 }
00388
00389
00390
00391
00392
00393
00394 DVDProbe::DVDProbe(const QString &dvd_device) : device(dvd_device), dvd(0)
00395 {
00396
00397
00398
00399
00400
00401 titles.setAutoDelete(true);
00402 wipeClean();
00403 }
00404
00405 void DVDProbe::wipeClean()
00406 {
00407 first_time = true;
00408 titles.clear();
00409 volume_name = QObject::tr("Unknown");
00410 }
00411
00412 bool DVDProbe::probe()
00413 {
00414
00415
00416
00417
00418
00419
00420 struct stat fileinfo;
00421
00422 int ret = stat(device.ascii(), &fileinfo);
00423 if (ret < 0)
00424 {
00425
00426
00427
00428
00429 wipeClean();
00430 return false;
00431 }
00432
00433 #if defined(__linux__) || defined(__FreeBSD__)
00434
00435
00436
00437
00438
00439
00440 int drive_handle = open(device.ascii(), O_RDONLY | O_NONBLOCK);
00441
00442 if (drive_handle == -1)
00443 {
00444 wipeClean();
00445 return false;
00446 }
00447
00448
00449
00450
00451
00452
00453
00454
00455 ioctl(drive_handle, CDROM_DRIVE_STATUS, CDSL_CURRENT);
00456 int status = ioctl(drive_handle, CDROM_DRIVE_STATUS, CDSL_CURRENT);
00457 if (status < 4)
00458 {
00459
00460
00461
00462
00463
00464
00465 wipeClean();
00466 close(drive_handle);
00467 return false;
00468
00469 }
00470
00471 status = ioctl(drive_handle, CDROM_MEDIA_CHANGED, NULL);
00472 close(drive_handle);
00473
00474 if (!status)
00475 {
00476
00477
00478
00479
00480
00481 if (!first_time)
00482 {
00483
00484
00485
00486
00487 return titles.count();
00488 }
00489 }
00490 #endif
00491
00492
00493
00494
00495
00496
00497
00498 wipeClean();
00499 first_time = false;
00500 dvd = DVDOpen(device.ascii());
00501 if (dvd)
00502 {
00503
00504
00505
00506
00507 const int arbitrary = 1024;
00508 char volume_name_buffr[arbitrary + 1];
00509 unsigned char set_name[arbitrary + 1];
00510
00511 if (DVDUDFVolumeInfo(dvd, volume_name_buffr, arbitrary, set_name,
00512 arbitrary) > -1)
00513 {
00514 volume_name = volume_name_buffr;
00515 }
00516 else
00517 {
00518 VERBOSE(VB_IMPORTANT, "Error getting volume name, setting to"
00519 "\"Unknown\"");
00520 }
00521
00522 ifo_handle_t *ifo_file = ifoOpen(dvd, 0);
00523 if (ifo_file)
00524 {
00525
00526
00527
00528
00529
00530
00531
00532 tt_srpt_t *title_info = ifo_file->tt_srpt;
00533 for (int i = 0; i < title_info->nr_of_srpts; ++i )
00534 {
00535 DVDTitle *new_title = new DVDTitle();
00536 new_title->setTrack(i + 1);
00537 new_title->setChapters((uint) title_info->title[i].nr_of_ptts);
00538 new_title->setAngles((uint) title_info->title[i].nr_of_angles);
00539
00540 ifo_handle_t *video_transport_file = NULL;
00541
00542 video_transport_file =
00543 ifoOpen(dvd, title_info->title[i].title_set_nr);
00544 if (!video_transport_file)
00545 {
00546 VERBOSE(VB_IMPORTANT,
00547 QString("Can't get video transport for track %1")
00548 .arg(i+1));
00549 }
00550 else
00551 {
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561 int some_index = title_info->title[i].vts_ttn;
00562 int some_other_index = video_transport_file->vts_ptt_srpt->
00563 title[some_index - 1].ptt[0].pgcn;
00564 pgc_t *current_pgc = video_transport_file->vts_pgcit->
00565 pgci_srp[some_other_index - 1].pgc;
00566
00567 dvd_time_t *libdvdread_playback_time =
00568 ¤t_pgc->playback_time;
00569
00570 double framerate = 0.0;
00571
00572 switch ((libdvdread_playback_time->frame_u & 0xc0) >> 6)
00573 {
00574 case 1:
00575 framerate = 25.0;
00576 break;
00577 case 3:
00578 framerate = 24000/1001.0;
00579 break;
00580 default:
00581 VERBOSE(VB_IMPORTANT,
00582 "For some odd reason (!), I couldn't get a"
00583 " video frame rate");
00584 break;
00585 }
00586
00587
00588
00589
00590
00591
00592 new_title->setTime(decdt(libdvdread_playback_time->hour),
00593 decdt(libdvdread_playback_time->minute),
00594 decdt(libdvdread_playback_time->second),
00595 framerate);
00596
00597 vtsi_mat_t *vtsi_mat = video_transport_file->vtsi_mat;
00598 if (vtsi_mat)
00599 {
00600
00601
00602
00603
00604 for (int j = 0; j < vtsi_mat->nr_of_vts_audio_streams;
00605 j++)
00606 {
00607 audio_attr_t *audio_attributes =
00608 &vtsi_mat->vts_audio_attr[j];
00609 DVDAudio *new_audio = new DVDAudio();
00610 new_audio->fill(audio_attributes);
00611 new_title->addAudio(new_audio);
00612 }
00613
00614
00615
00616
00617
00618 for (int j = 0; j < vtsi_mat->nr_of_vts_subp_streams;
00619 j++)
00620 {
00621 subp_attr_t *sub_attributes =
00622 &vtsi_mat->vts_subp_attr[j];
00623 if (sub_attributes->type == 0 &&
00624 sub_attributes->zero1 == 0 &&
00625 sub_attributes->lang_code == 0 &&
00626 sub_attributes->lang_extension == 0 &&
00627 sub_attributes->zero2 == 0)
00628 {
00629
00630 }
00631 else
00632 {
00633
00634
00635
00636
00637
00638
00639
00640 DVDSubTitle *new_subtitle = new DVDSubTitle(j,
00641 lctola(sub_attributes->lang_code).utf8());
00642 new_title->addSubTitle(new_subtitle);
00643 }
00644 }
00645
00646
00647
00648
00649
00650 video_attr_t *video_attributes =
00651 &vtsi_mat->vts_video_attr;
00652
00653 switch (video_attributes->display_aspect_ratio)
00654 {
00655 case 0:
00656 new_title->setAR(4, 3, "4:3");
00657 break;
00658 case 3:
00659 new_title->setAR(16, 9, "16:9");
00660 break;
00661 default:
00662 VERBOSE(VB_IMPORTANT, "couldn't get aspect"
00663 " ratio for a title");
00664 }
00665
00666 switch (video_attributes->video_format)
00667 {
00668 case 0:
00669 new_title->setVFormat("ntsc");
00670 break;
00671 case 1:
00672 new_title->setVFormat("pal");
00673 break;
00674 default:
00675 VERBOSE(VB_IMPORTANT, "Could not get video"
00676 " format for a title");
00677 }
00678
00679 if (video_attributes->letterboxed)
00680 {
00681 new_title->setLBox(true);
00682 }
00683
00684 uint c_height = 480;
00685 if (video_attributes->video_format != 0)
00686 {
00687 c_height = 576;
00688 }
00689
00690 switch (video_attributes->picture_size)
00691 {
00692 case 0:
00693 new_title->setSize(720, c_height);
00694 break;
00695 case 1:
00696 new_title->setSize(704, c_height);
00697 break;
00698 case 2:
00699 new_title->setSize(352, c_height);
00700 break;
00701 case 3:
00702 new_title->setSize(352, c_height / 2);
00703 break;
00704 default:
00705 VERBOSE(VB_IMPORTANT, "Could not determine for"
00706 " video size for a title.");
00707 }
00708 ifoClose(video_transport_file);
00709 }
00710 else
00711 {
00712 VERBOSE(VB_IMPORTANT,
00713 QString("Couldn't find any audio or video"
00714 " information for track %1").arg(i+1));
00715 }
00716 }
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729 new_title->determineInputID();
00730
00731
00732
00733
00734
00735 titles.append(new_title);
00736 }
00737
00738 ifoClose(ifo_file);
00739 ifo_file = NULL;
00740
00741 DVDClose(dvd);
00742 dvd = NULL;
00743 return true;
00744 }
00745 else
00746 {
00747 DVDClose(dvd);
00748 dvd = NULL;
00749 return false;
00750 }
00751 }
00752 return false;
00753 }
00754
00755 DVDTitle *DVDProbe::getTitle(uint which_one)
00756 {
00757
00758 DVDTitle *iter;
00759 for (iter = titles.first(); iter; iter = titles.next())
00760 {
00761 if (iter->getTrack() == which_one)
00762 {
00763 return iter;
00764 }
00765 }
00766 return NULL;
00767
00768 }
00769
00770 DVDProbe::~DVDProbe()
00771 {
00772 if (dvd)
00773 {
00774 DVDClose(dvd);
00775 }
00776 wipeClean();
00777 }