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