File indexing completed on 2024-04-28 04:49:54

0001 /*
0002     SPDX-FileCopyrightText: 2006-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2009 Michal Malek <michalm@jabster.pl>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 
0010 #ifndef __STDC_LIMIT_MACROS
0011 #define __STDC_LIMIT_MACROS // needed for *_MAX macros in dvdread headers
0012 #endif
0013 
0014 #include "k3bvideodvd.h"
0015 #include "k3bdevice.h"
0016 #include "k3b_i18n.h"
0017 
0018 #include <QFile>
0019 
0020 #include <inttypes.h> // needed by dvdreads headers
0021 #include <dvdread/dvd_reader.h>
0022 #include <dvdread/ifo_types.h>
0023 #include <dvdread/ifo_read.h>
0024 
0025 namespace {
0026     
0027     unsigned short convertTime( uint8_t time )
0028     {
0029         // I don't get this stuff, I should read something about VideoDVD some day...
0030         return (((time & 0xf0) >> 3) * 5 + (time & 0x0f));
0031     }
0032     
0033     unsigned short convertFrame( uint8_t frame_u )
0034     {
0035         // lower 6 bits store frame number
0036         return frame_u & 0x3f;
0037     }
0038     
0039     double convertFrameRate( uint8_t frame_u )
0040     {
0041         //
0042         // higher 2 bits store frame rate
0043         // This is how it is done in libdvdread
0044         // I don't really understand it, though... :(
0045         //
0046         switch( (frame_u & 0xc0) >> 6 ) {
0047         case 1:
0048             // PAL?
0049             return 25.0;
0050         case 3:
0051             // NTSC?
0052             return 29.97;
0053         default:
0054             // should only happen for time == 0?
0055             return 0.0;
0056         }
0057     }
0058     
0059 } // namespace
0060 
0061 
0062 K3b::VideoDVD::VideoDVD::VideoDVD() :
0063  m_device( 0 )
0064 {
0065 }
0066 
0067 
0068 K3b::VideoDVD::VideoDVD::~VideoDVD()
0069 {
0070 }
0071 
0072 
0073 bool K3b::VideoDVD::VideoDVD::valid() const
0074 {
0075     return ( m_device != 0 );
0076 }
0077 
0078 
0079 bool K3b::VideoDVD::VideoDVD::open( K3b::Device::Device* dev )
0080 {
0081     m_device = 0;
0082     m_titles.clear();
0083 
0084     //
0085     // Initialize libdvdread
0086     //
0087     dvd_reader_t* dvdReaderT = DVDOpen( QFile::encodeName(dev->blockDeviceName()) );
0088     if( !dvdReaderT ) {
0089         qDebug() << "(K3b::VideoDVD) Could not open device " << dev->blockDeviceName();
0090         return false;
0091     }
0092 
0093     //
0094     // Read volume id
0095     //
0096     char v[33];
0097     if( DVDUDFVolumeInfo( dvdReaderT, v, 32, 0, 0 ) == 0 ) {
0098         m_volumeIdentifier = QString::fromLatin1( v, 31 );
0099     }
0100     else if ( DVDISOVolumeInfo( dvdReaderT, v, 33, 0, 0 ) == 0 ) {
0101         m_volumeIdentifier = QString::fromLatin1( v, 32 );
0102     }
0103     else {
0104         qDebug() << "(K3b::VideoDVD) Could not read volume info.";
0105         DVDClose( dvdReaderT );
0106         return false;
0107     }
0108 
0109     //
0110     // Open the VMG info
0111     //
0112     ifo_handle_t* vmg = ifoOpen( dvdReaderT, 0 );
0113     if( !vmg ) {
0114         qDebug() << "(K3b::VideoDVD) Can't open VMG info.";
0115         DVDClose( dvdReaderT );
0116         return false;
0117     }
0118 
0119     //
0120     // parse titles
0121     //
0122     m_titles.resize( vmg->tt_srpt->nr_of_srpts );
0123     for( unsigned int i = 0; i < vmg->tt_srpt->nr_of_srpts; ++i ) {
0124         title_info_t& title = vmg->tt_srpt->title[i];
0125 
0126         //    m_titles[i].m_videoDVD = this;
0127 
0128         //
0129         // general title info
0130         //
0131         m_titles[i].m_titleNum   = i+1;
0132         m_titles[i].m_numPTTs    = title.nr_of_ptts;
0133         m_titles[i].m_numAngles  = title.nr_of_angles;
0134         m_titles[i].m_titleSet   = title.title_set_nr;
0135         m_titles[i].m_ttn        = title.vts_ttn;
0136 
0137         //
0138         // Open the title set the current title is a part of
0139         //
0140         ifo_handle_t* titleIfo = ifoOpen( dvdReaderT, vmg->tt_srpt->title[i].title_set_nr );
0141         if( !titleIfo ) {
0142             qDebug() << "(K3b::VideoDVD) Can't open Title ifo.";
0143             ifoClose( vmg );
0144             DVDClose( dvdReaderT );
0145             return false;
0146         }
0147 
0148         //
0149         // Length of this title
0150         //
0151         // the program chain number of the first partoftitle of the current title (FIXME: but a title may contain multiple pgcs)
0152         int pgc_id = titleIfo->vts_ptt_srpt->title[ m_titles[i].ttn() - 1 ].ptt[0].pgcn;
0153         // (first?) program chain of the first partoftitle of the current title
0154         pgc_t* cur_pgc = titleIfo->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc;
0155 
0156         m_titles[i].m_playbackTime = Time( convertTime(cur_pgc->playback_time.hour),
0157                                            convertTime(cur_pgc->playback_time.minute),
0158                                            convertTime(cur_pgc->playback_time.second),
0159                                            convertFrame(cur_pgc->playback_time.frame_u),
0160                                            convertFrameRate(cur_pgc->playback_time.frame_u) );
0161 
0162         //
0163         // Video stream information
0164         //
0165         m_titles[i].m_videoStream.m_permittedDf = titleIfo->vtsi_mat->vts_video_attr.permitted_df;
0166         m_titles[i].m_videoStream.m_displayAspectRatio = titleIfo->vtsi_mat->vts_video_attr.display_aspect_ratio;
0167         m_titles[i].m_videoStream.m_videoFormat = titleIfo->vtsi_mat->vts_video_attr.video_format;
0168         m_titles[i].m_videoStream.m_mpegVersion = titleIfo->vtsi_mat->vts_video_attr.mpeg_version;
0169         m_titles[i].m_videoStream.m_filmMode = titleIfo->vtsi_mat->vts_video_attr.film_mode;
0170         m_titles[i].m_videoStream.m_letterboxed = titleIfo->vtsi_mat->vts_video_attr.letterboxed;
0171         m_titles[i].m_videoStream.m_pictureSize = titleIfo->vtsi_mat->vts_video_attr.picture_size;
0172         m_titles[i].m_videoStream.m_bitRate = titleIfo->vtsi_mat->vts_video_attr.bit_rate;
0173 
0174         //
0175         // Audio stream information
0176         //
0177         m_titles[i].m_audioStreams.resize( titleIfo->vtsi_mat->nr_of_vts_audio_streams );
0178         for( unsigned int j = 0; j < titleIfo->vtsi_mat->nr_of_vts_audio_streams; ++j ) {
0179             m_titles[i].m_audioStreams[j].m_format = titleIfo->vtsi_mat->vts_audio_attr[j].audio_format;
0180             m_titles[i].m_audioStreams[j].m_applicationMode = titleIfo->vtsi_mat->vts_audio_attr[j].application_mode;
0181             m_titles[i].m_audioStreams[j].m_quantization = titleIfo->vtsi_mat->vts_audio_attr[j].quantization;
0182             m_titles[i].m_audioStreams[j].m_sampleFrequency = titleIfo->vtsi_mat->vts_audio_attr[j].sample_frequency;
0183             m_titles[i].m_audioStreams[j].m_codeExtension = titleIfo->vtsi_mat->vts_audio_attr[j].code_extension;
0184             m_titles[i].m_audioStreams[j].m_multiChannelExt = titleIfo->vtsi_mat->vts_audio_attr[j].multichannel_extension;
0185             m_titles[i].m_audioStreams[j].m_channels = titleIfo->vtsi_mat->vts_audio_attr[j].channels+1;
0186             if( titleIfo->vtsi_mat->vts_audio_attr[j].lang_code>>8 )
0187                 m_titles[i].m_audioStreams[j].m_langCode = QString::asprintf( "%c%c",
0188                                                                   titleIfo->vtsi_mat->vts_audio_attr[j].lang_code>>8,
0189                                                                   titleIfo->vtsi_mat->vts_audio_attr[j].lang_code & 0xff );
0190             else
0191                 m_titles[i].m_audioStreams[j].m_langCode = QString();
0192         }
0193 
0194         //
0195         // SubPicture stream information
0196         //
0197         m_titles[i].m_subPictureStreams.resize( titleIfo->vtsi_mat->nr_of_vts_subp_streams );
0198         for( unsigned int j = 0; j < titleIfo->vtsi_mat->nr_of_vts_subp_streams; ++j ) {
0199             m_titles[i].m_subPictureStreams[j].m_codeMode = titleIfo->vtsi_mat->vts_subp_attr[j].code_mode;
0200             m_titles[i].m_subPictureStreams[j].m_codeExtension = titleIfo->vtsi_mat->vts_subp_attr[j].code_extension;
0201             if( titleIfo->vtsi_mat->vts_subp_attr[j].lang_code>>8 )
0202                 m_titles[i].m_subPictureStreams[j].m_langCode = QString::asprintf( "%c%c",
0203                                                                        titleIfo->vtsi_mat->vts_subp_attr[j].lang_code>>8,
0204                                                                        titleIfo->vtsi_mat->vts_subp_attr[j].lang_code & 0xff );
0205             else
0206                 m_titles[i].m_subPictureStreams[j].m_langCode = QString();
0207         }
0208 
0209         //
0210         // add chapter info
0211         //
0212         if ( !cur_pgc->cell_playback ) {
0213             m_titles[i].m_numPTTs = 0;
0214         }
0215         else {
0216             m_titles[i].m_ptts.resize( m_titles[i].numPTTs() );
0217             for( unsigned int j = 0; j < m_titles[i].numPTTs(); ++j ) {
0218                 m_titles[i].m_ptts[j].m_pttNum = j+1;
0219                 m_titles[i].m_ptts[j].m_playbackTime = Time( convertTime(cur_pgc->cell_playback[j].playback_time.hour),
0220                                                              convertTime(cur_pgc->cell_playback[j].playback_time.minute),
0221                                                              convertTime(cur_pgc->cell_playback[j].playback_time.second),
0222                                                              convertFrame(cur_pgc->cell_playback[j].playback_time.frame_u),
0223                                                              convertFrameRate(cur_pgc->cell_playback[j].playback_time.frame_u) );
0224                 m_titles[i].m_ptts[j].m_firstSector = cur_pgc->cell_playback[j].first_sector;
0225                 m_titles[i].m_ptts[j].m_lastSector = cur_pgc->cell_playback[j].last_sector;
0226             }
0227         }
0228 
0229         ifoClose( titleIfo );
0230     }
0231 
0232     ifoClose( vmg );
0233     DVDClose( dvdReaderT );
0234 
0235     // discard titles where we could not get info from any chapter
0236     for (int i = 0; i < m_titles.count(); i++) {
0237         if ( m_titles[i].numChapters() == 0 ) {
0238             m_titles.remove(i--);
0239         }
0240     }
0241 
0242     //
0243     // Setting the device makes this a valid instance
0244     //
0245     m_device = dev;
0246 
0247     return true;
0248 }
0249 
0250 
0251 const K3b::VideoDVD::Title& K3b::VideoDVD::VideoDVD::title( unsigned int num ) const
0252 {
0253     return m_titles[num];
0254 }
0255 
0256 
0257 const K3b::VideoDVD::Title& K3b::VideoDVD::VideoDVD::operator[]( unsigned int num ) const
0258 {
0259     return title( num );
0260 }
0261 
0262 
0263 void K3b::VideoDVD::VideoDVD::debug() const
0264 {
0265     qDebug() << "VideoDVD information:" << Qt::endl
0266              << "=====================" << Qt::endl
0267              << "Volume ID: " << volumeIdentifier() << Qt::endl << Qt::endl;
0268 
0269     for( unsigned int i = 0; i < numTitles(); ++i ) {
0270         qDebug() << "Title " << title(i).titleNumber() << " (" << title(i).playbackTime().toString() << ")" << Qt::endl
0271                  << "   Chapters: " << title(i).numPTTs() << Qt::endl
0272                  << "   Angles:   " << title(i).numAngles() << Qt::endl
0273                  << "   VTS,TTN:  " << title(i).titleSet() << "," << title(i).ttn() << Qt::endl
0274                  << "   Audio Streams:" << Qt::endl;
0275         for( unsigned int j = 0; j < title(i).numAudioStreams(); ++j )
0276             qDebug() << "      " << title(i).audioStream(j).langCode() << ": "
0277                      << audioFormatString( title(i).audioStream(j).format() ) << ", "
0278                      << audioCodeExtensionString( title(i).audioStream(j).codeExtension() ) << Qt::endl;
0279         qDebug() << "   SubPicture Streams:";
0280         for( unsigned int j = 0; j < title(i).numSubPictureStreams(); ++j )
0281             qDebug() << "      " << title(i).subPictureStream(j).langCode() << ": "
0282                      << subPictureCodeModeString( title(i).subPictureStream(j).codeMode() ) << ", "
0283                      << subPictureCodeExtensionString( title(i).subPictureStream(j).codeExtension() ) << Qt::endl;
0284     }
0285 }
0286 
0287 
0288 QString K3b::VideoDVD::audioFormatString( int format )
0289 {
0290     switch( format ) {
0291     case AUDIO_FORMAT_AC3:
0292         return i18n("AC3");
0293     case AUDIO_FORMAT_MPEG1:
0294         return i18n("MPEG1");
0295     case AUDIO_FORMAT_MPEG2EXT:
0296         return i18n("MPEG2 Extended");
0297     case AUDIO_FORMAT_LPCM:
0298         return i18n("LPCM");
0299     case AUDIO_FORMAT_DTS:
0300         return i18n("DTS");
0301     default:
0302         return i18n("unknown audio format");
0303     }
0304 }
0305 
0306 
0307 QString K3b::VideoDVD::audioCodeExtensionString( int ext )
0308 {
0309     switch( ext ) {
0310     case AUDIO_CODE_EXT_UNSPECIFIED:
0311         return i18n("Unspecified");
0312     case AUDIO_CODE_EXT_NORMAL:
0313         return i18n("Normal");
0314     case AUDIO_CODE_EXT_VISUALLY_IMPAIRED:
0315         return i18n("For the visually impaired");
0316     case AUDIO_CODE_EXT_DIR_COMMENTS_1:
0317         return i18n("Director's comments 1");
0318     case AUDIO_CODE_EXT_DIR_COMMENTS_2:
0319         return i18n("Director's comments 2");
0320     default:
0321         return i18n("unknown audio code extension");
0322     }
0323 }
0324 
0325 
0326 QString K3b::VideoDVD::subPictureCodeModeString( int mode )
0327 {
0328     switch( mode ) {
0329     case SUBPIC_CODE_MODE_RLE:
0330         return i18n("RLE");
0331     case SUBPIC_CODE_MODE_EXT:
0332         return i18n("Extended");
0333     default:
0334         return i18n("unknown coding mode");
0335     }
0336 }
0337 
0338 
0339 QString K3b::VideoDVD::subPictureCodeExtensionString( int ext )
0340 {
0341     switch( ext ) {
0342     case SUBPIC_CODE_EXT_UNSPECIFIED:
0343         return i18n("Unspecified");
0344     case SUBPIC_CODE_EXT_CAPTION_NORMAL_SIZE:
0345         return i18n("Caption with normal size characters");
0346     case SUBPIC_CODE_EXT_CAPTION_BIGGER_SIZE:
0347         return i18n("Caption with bigger size characters");
0348     case SUBPIC_CODE_EXT_CAPTION_FOR_CHILDREN:
0349         return i18n("Caption for children");
0350     case SUBPIC_CODE_EXT_CLOSED_CAPTION_NORMAL_SIZE:
0351         return i18n("Closed caption with normal size characters");
0352     case SUBPIC_CODE_EXT_CLOSED_CAPTION_BIGGER_SIZE:
0353         return i18n("Closed caption with bigger size characters");
0354     case SUBPIC_CODE_EXT_CLOSED_CAPTION_FOR_CHILDREN:
0355         return i18n("Closed caption for children");
0356     case SUBPIC_CODE_EXT_FORCED_CAPTION:
0357         return i18n("Forced caption");
0358     case SUBPIC_CODE_EXT_DIR_COMMENTS_NORMAL_SIZE:
0359         return i18n("Director's comments with normal size characters");
0360     case SUBPIC_CODE_EXT_DIR_COMMENTS_BIGGER_SIZE:
0361         return i18n("Director's comments with bigger size characters");
0362     case SUBPIC_CODE_EXT_DIR_COMMENTS_FOR_CHILDREN:
0363         return i18n("Director's comments for children");
0364     default:
0365         return i18n("unknown code extension");
0366     }
0367 }
0368