You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
845 lines
25 KiB
845 lines
25 KiB
/*
|
|
*
|
|
* $Id: k3bmpeginfo.cpp 619556 2007-01-03 17:38:12Z trueg $
|
|
* Copyright (C) 2003-2004 Christian Kvasny <chris@k3b.org>
|
|
*
|
|
* This file is part of the K3b project.
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
*/
|
|
|
|
// kde includes
|
|
#include <tdelocale.h>
|
|
|
|
// k3b includes
|
|
#include "k3bmpeginfo.h"
|
|
|
|
static const double frame_rates[ 16 ] =
|
|
{
|
|
0.0, 24000.0 / 1001, 24.0, 25.0,
|
|
30000.0 / 1001, 30.0, 50.0, 60000.0 / 1001,
|
|
60.0, 0.0,
|
|
};
|
|
|
|
K3bMpegInfo::K3bMpegInfo( const char* filename )
|
|
: m_mpegfile( 0 ),
|
|
m_filename( filename ),
|
|
m_done( false ),
|
|
m_buffstart( 0 ),
|
|
m_buffend( 0 ),
|
|
m_buffer( 0 ),
|
|
m_initial_TS( 0.0 )
|
|
{
|
|
|
|
mpeg_info = new Mpeginfo();
|
|
|
|
m_mpegfile = fopen( filename, "rb" );
|
|
|
|
if ( m_mpegfile == 0 ) {
|
|
kdDebug() << TQString( "Unable to open %1" ).arg( m_filename ) << endl;
|
|
return ;
|
|
}
|
|
|
|
if ( fseeko( m_mpegfile, 0, SEEK_END ) ) {
|
|
kdDebug() << TQString( "Unable to seek in file %1" ).arg( m_filename ) << endl;
|
|
return ;
|
|
}
|
|
|
|
llong lof = ftello( m_mpegfile );
|
|
|
|
if ( lof == -1 ) {
|
|
kdDebug() << TQString( "Seeking to end of input file %1 failed." ).arg( m_filename ) << endl;
|
|
//give up..
|
|
return ;
|
|
} else
|
|
m_filesize = lof;
|
|
|
|
// nothing to do on an empty file
|
|
if ( !m_filesize ) {
|
|
kdDebug() << TQString( "File %1 is empty." ).arg( m_filename ) << endl;
|
|
m_error_string = i18n( "File %1 is empty." ).arg( m_filename );
|
|
return ;
|
|
}
|
|
|
|
m_buffer = new byte[ BUFFERSIZE ];
|
|
|
|
MpegParsePacket ( );
|
|
|
|
}
|
|
|
|
K3bMpegInfo::~K3bMpegInfo()
|
|
{
|
|
if ( m_buffer ) {
|
|
delete[] m_buffer;
|
|
}
|
|
if ( m_mpegfile ) {
|
|
fclose( m_mpegfile );
|
|
}
|
|
|
|
delete mpeg_info;
|
|
}
|
|
bool K3bMpegInfo::MpegParsePacket ()
|
|
{
|
|
|
|
/* verify the packet begins with a pack header */
|
|
if ( !EnsureMPEG( 0, MPEG_PACK_HEADER_CODE ) ) {
|
|
llong code = GetNBytes( 0, 4 );
|
|
|
|
kdDebug() << TQString( "(K3bMpegInfo::mpeg_parse_packet ()) pack header code 0x%1 expected, but 0x%2 found" ).arg( 0x00000100 + MPEG_PACK_HEADER_CODE, 0, 16 ).arg( code, 0, 16 ) << endl;
|
|
|
|
if ( code == 0x00000100 + MPEG_SEQUENCE_CODE ) {
|
|
kdDebug() << "...this looks like a elementary video stream but a multiplexed program stream was required." << endl;
|
|
m_error_string = i18n( "This looks like a elementary video stream but a multiplexed program stream was required." );
|
|
}
|
|
|
|
if ( ( 0xfff00000 & code ) == 0xfff00000 ) {
|
|
kdDebug() << "...this looks like a elementary audio stream but a multiplexed program stream was required." << endl;
|
|
m_error_string = i18n( "This looks like a elementary audio stream but a multiplexed program stream was required." );
|
|
}
|
|
|
|
if ( code == 0x52494646 ) {
|
|
kdDebug() << "...this looks like a RIFF header but a plain multiplexed program stream was required." << endl;
|
|
m_error_string = i18n( "This looks like a RIFF header but a plain multiplexed program stream was required." );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/* take a look at the pack header */
|
|
int offset = 0;
|
|
while ( GetByte( offset ) == 0x00 )
|
|
offset ++;
|
|
//here we're on the first non null byte let's get back to leave two zeros (packet start code)
|
|
offset -= 2;
|
|
|
|
if ( offset != 0 ) {
|
|
// we actually skipped some zeroes
|
|
kdDebug() << TQString( "Skipped %1 zeroes at start of file" ).arg( offset ) << endl;
|
|
}
|
|
|
|
// here while schleife
|
|
while ( offset != -1 ) {
|
|
offset = MpegParsePacket( offset );
|
|
}
|
|
|
|
/*
|
|
int pkt = 0;
|
|
offset = FindNextMarker( 0, MPEG_PACK_HEADER_CODE );
|
|
|
|
while ( offset != -1 ) {
|
|
pkt++;
|
|
offset = FindNextMarker( offset+1, MPEG_PACK_HEADER_CODE );
|
|
}
|
|
|
|
kdDebug() << "Pkt found: " << pkt << endl;
|
|
*/
|
|
|
|
//seek the file duration by fetching the last PACK
|
|
//and reading its timestamp
|
|
llong last_pack = bdFindNextMarker( m_filesize - 13, MPEG_PACK_HEADER_CODE );
|
|
// -12 because a PACK is at least 12 bytes
|
|
double duration;
|
|
last_pack += 4;
|
|
int bits = GetByte( last_pack ) >> 4;
|
|
|
|
if ( bits == 0x2 ) /* %0010 ISO11172-1 */
|
|
{
|
|
duration = ReadTS( last_pack );
|
|
} else if ( bits >> 2 == 0x1 ) /* %01xx ISO13818-1 */
|
|
{
|
|
duration = ReadTSMpeg2( last_pack );
|
|
} else {
|
|
kdDebug() << TQString( "no timestamp found" ) << endl;
|
|
duration = ReadTS( last_pack );
|
|
}
|
|
|
|
mpeg_info->playing_time = duration - m_initial_TS;
|
|
|
|
|
|
if ( !mpeg_info->has_video )
|
|
for ( int i = 0; i < 2; i++ )
|
|
if ( mpeg_info->video[ i ].seen )
|
|
mpeg_info->has_video = true;
|
|
|
|
if ( !mpeg_info->has_audio )
|
|
for ( int i = 0; i < 2; i++ )
|
|
if ( mpeg_info->audio[ i ].seen )
|
|
mpeg_info->has_audio = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
llong K3bMpegInfo::MpegParsePacket ( llong offset )
|
|
{
|
|
byte mark = 0;
|
|
uint size = 0;
|
|
|
|
/* continue until start code seen */
|
|
offset = FindNextMarker( offset, &mark );
|
|
|
|
if ( offset < 0 )
|
|
return offset;
|
|
|
|
switch ( mark ) {
|
|
int bits;
|
|
|
|
case MPEG_PACK_HEADER_CODE:
|
|
// kdDebug() << TQString( "MPEG_PACK_HEADER_CODE @ %1" ).arg( offset ) << endl;
|
|
|
|
offset += 4;
|
|
|
|
if ( mpeg_info->version != MPEG_VERS_INVALID )
|
|
break;
|
|
|
|
bits = GetByte( offset ) >> 4;
|
|
|
|
if ( bits == 0x2 ) /* %0010 ISO11172-1 */
|
|
{
|
|
mpeg_info->version = MPEG_VERS_MPEG1;
|
|
|
|
unsigned long muxrate = 0;
|
|
|
|
muxrate = ( GetByte( offset + 5 ) & 0x7F ) << 15;
|
|
muxrate |= ( GetByte( offset + 6 ) << 7 );
|
|
muxrate |= ( GetByte( offset + 7 ) >> 1 );
|
|
|
|
mpeg_info->muxrate = muxrate * 50 * 8;
|
|
|
|
if ( m_initial_TS == 0.0 )
|
|
{
|
|
m_initial_TS = ReadTS( offset );
|
|
kdDebug() << TQString( "Initial TS = %1" ).arg( m_initial_TS ) << endl;
|
|
}
|
|
|
|
} else if ( bits >> 2 == 0x1 ) /* %01xx ISO13818-1 */
|
|
{
|
|
mpeg_info->version = MPEG_VERS_MPEG2;
|
|
|
|
unsigned long muxrate = 0;
|
|
muxrate = GetByte( offset + 6 ) << 14;
|
|
muxrate |= GetByte( offset + 7 ) << 6;
|
|
muxrate |= GetByte( offset + 8 ) >> 2;
|
|
|
|
mpeg_info->muxrate = muxrate * 50 * 8;
|
|
|
|
if ( m_initial_TS == 0.0 )
|
|
{
|
|
m_initial_TS = ReadTSMpeg2( offset );
|
|
kdDebug() << TQString( "Initial TS = %1" ).arg( m_initial_TS ) << endl;
|
|
}
|
|
|
|
} else {
|
|
kdDebug() << TQString( "packet not recognized as either version 1 or 2 (%1)" ).arg( bits ) << endl;
|
|
mpeg_info->version = MPEG_VERS_INVALID;
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case MPEG_SYSTEM_HEADER_CODE:
|
|
case MPEG_PAD_CODE:
|
|
case MPEG_PRIVATE_1_CODE:
|
|
case MPEG_VIDEO_E0_CODE:
|
|
case MPEG_VIDEO_E1_CODE:
|
|
case MPEG_VIDEO_E2_CODE:
|
|
case MPEG_AUDIO_C0_CODE:
|
|
case MPEG_AUDIO_C1_CODE:
|
|
case MPEG_AUDIO_C2_CODE:
|
|
|
|
offset += 4;
|
|
size = GetSize( offset );
|
|
offset += 2;
|
|
// kdDebug() << TQString( "offset = %1, size = %2" ).arg( offset ).arg( size ) << endl;
|
|
|
|
switch ( mark ) {
|
|
case MPEG_SYSTEM_HEADER_CODE:
|
|
// kdDebug() << TQString( "Systemheader: %1" ).arg( m_code, 0, 16 ) << endl;
|
|
break;
|
|
|
|
case MPEG_VIDEO_E0_CODE:
|
|
case MPEG_VIDEO_E1_CODE:
|
|
case MPEG_VIDEO_E2_CODE:
|
|
ParseVideo( offset, mark );
|
|
// _analyze_video_pes (code & 0xff, buf + pos, size, !parse_pes, ctx);
|
|
if ( mpeg_info->has_video && mpeg_info->has_audio ) {
|
|
return -1;
|
|
} else if ( mark == MPEG_VIDEO_E0_CODE || mpeg_info->version == MPEG_VERS_MPEG2 && mark == MPEG_VIDEO_E1_CODE || mpeg_info->version == MPEG_VERS_MPEG1 && mark == MPEG_VIDEO_E2_CODE ) {
|
|
mpeg_info->has_video = true;
|
|
offset = FindNextAudio( offset );
|
|
}
|
|
break;
|
|
case MPEG_AUDIO_C0_CODE:
|
|
case MPEG_AUDIO_C1_CODE:
|
|
case MPEG_AUDIO_C2_CODE:
|
|
offset = SkipPacketHeader( offset - 6 );
|
|
ParseAudio( offset, mark );
|
|
// audio packet doesn't begin with 0xFFF
|
|
if ( !mpeg_info->audio[ GetAudioIdx( mark ) ].seen ) {
|
|
int a_idx = GetAudioIdx( mark );
|
|
while ( ( offset < m_filesize - 10 ) && !mpeg_info->audio[ a_idx ].seen ) {
|
|
if ( ( GetByte( offset ) == 0xFF ) && ( GetByte( offset + 1 ) & 0xF0 ) == 0xF0 )
|
|
ParseAudio( offset, mark );
|
|
offset++;
|
|
}
|
|
}
|
|
|
|
mpeg_info->has_audio = true;
|
|
if ( mpeg_info->has_video )
|
|
return -1;
|
|
|
|
offset = FindNextVideo( offset );
|
|
break;
|
|
|
|
case MPEG_PRIVATE_1_CODE:
|
|
kdDebug() << TQString( "PrivateCode: %1" ).arg( mark, 0, 16 ) << endl;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MPEG_PROGRAM_END_CODE:
|
|
kdDebug() << TQString( "ProgramEndCode: %1" ).arg( mark, 0, 16 ) << endl;
|
|
offset += 4;
|
|
break;
|
|
|
|
case MPEG_PICTURE_CODE:
|
|
kdDebug() << TQString( "PictureCode: %1" ).arg( mark, 0, 16 ) << endl;
|
|
offset += 3;
|
|
break;
|
|
|
|
default:
|
|
offset += 4;
|
|
break;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
byte K3bMpegInfo::GetByte( llong offset )
|
|
{
|
|
unsigned long nread;
|
|
if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
|
|
|
|
if ( fseeko( m_mpegfile, offset, SEEK_SET ) ) {
|
|
kdDebug() << TQString( "could not get seek to offset (%1) in file %2 (size:%3)" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl;
|
|
return 0x11;
|
|
}
|
|
nread = fread( m_buffer, 1, BUFFERSIZE, m_mpegfile );
|
|
m_buffstart = offset;
|
|
m_buffend = offset + nread;
|
|
if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
|
|
// weird
|
|
kdDebug() << TQString( "could not get offset %1 in file %2 [%3]" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl;
|
|
return 0x11;
|
|
}
|
|
}
|
|
return m_buffer[ offset - m_buffstart ];
|
|
}
|
|
|
|
// same as above but improved for backward search
|
|
byte K3bMpegInfo::bdGetByte( llong offset )
|
|
{
|
|
unsigned long nread;
|
|
if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
|
|
llong start = offset - BUFFERSIZE + 1 ;
|
|
start = start >= 0 ? start : 0;
|
|
|
|
fseeko( m_mpegfile, start, SEEK_SET );
|
|
|
|
nread = fread( m_buffer, 1, BUFFERSIZE, m_mpegfile );
|
|
m_buffstart = start;
|
|
m_buffend = start + nread;
|
|
if ( ( offset >= m_buffend ) || ( offset < m_buffstart ) ) {
|
|
// weird
|
|
kdDebug() << TQString( "could not get offset %1 in file %2 [%3]" ).arg( offset ).arg( m_filename ).arg( m_filesize ) << endl;
|
|
|
|
return 0x11;
|
|
}
|
|
}
|
|
return m_buffer[ offset - m_buffstart ];
|
|
}
|
|
|
|
|
|
llong K3bMpegInfo::GetNBytes( llong offset, int n )
|
|
{
|
|
llong nbytes = 0;
|
|
n--;
|
|
for ( int i = 0; i < n; i++ )
|
|
( ( char* ) & nbytes ) [ n - i ] = GetByte( offset + i );
|
|
|
|
return nbytes;
|
|
|
|
}
|
|
|
|
// get a two byte size
|
|
unsigned short int K3bMpegInfo::GetSize( llong offset )
|
|
{
|
|
return GetByte( offset ) * 256 + GetByte( offset + 1 );
|
|
// return GetNBytes( offset, 2 );
|
|
|
|
}
|
|
|
|
bool K3bMpegInfo::EnsureMPEG( llong offset, byte mark )
|
|
{
|
|
if ( ( GetByte( offset ) == 0x00 ) &&
|
|
( GetByte( offset + 1 ) == 0x00 ) &&
|
|
( GetByte( offset + 2 ) == 0x01 ) &&
|
|
( GetByte( offset + 3 ) == mark ) )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
// find next 0x 00 00 01 xx sequence, returns offset or -1 on err
|
|
llong K3bMpegInfo::FindNextMarker( llong from )
|
|
{
|
|
llong offset;
|
|
for ( offset = from; offset < ( m_filesize - 4 ); offset++ ) {
|
|
if (
|
|
( GetByte( offset + 0 ) == 0x00 ) &&
|
|
( GetByte( offset + 1 ) == 0x00 ) &&
|
|
( GetByte( offset + 2 ) == 0x01 ) ) {
|
|
return offset;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// find next 0x 00 00 01 xx sequence, returns offset or -1 on err and
|
|
// change mark to xx
|
|
llong K3bMpegInfo::FindNextMarker( llong from, byte* mark )
|
|
{
|
|
llong offset = FindNextMarker( from );
|
|
if ( offset >= 0 ) {
|
|
*mark = GetByte( offset + 3 );
|
|
return offset;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// find next 0X00 00 01 mark
|
|
llong K3bMpegInfo::FindNextMarker( llong from, byte mark )
|
|
{
|
|
llong offset = from;
|
|
while ( offset >= 0 ) {
|
|
offset = FindNextMarker( offset );
|
|
if ( offset < 0 ) {
|
|
return -1;
|
|
}
|
|
if ( EnsureMPEG( offset, mark ) ) {
|
|
return offset;
|
|
} else
|
|
offset++;
|
|
}
|
|
|
|
//shouldn't be here
|
|
return -1;
|
|
}
|
|
|
|
llong K3bMpegInfo::bdFindNextMarker( llong from, byte mark )
|
|
{
|
|
llong offset;
|
|
for ( offset = from; offset >= 0; offset-- ) {
|
|
if (
|
|
( bdGetByte( offset ) == 0x00 ) &&
|
|
( bdGetByte( offset + 1 ) == 0x00 ) &&
|
|
( bdGetByte( offset + 2 ) == 0x01 ) &&
|
|
( bdGetByte( offset + 3 ) == mark ) ) {
|
|
return offset;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
llong K3bMpegInfo::bdFindNextMarker( llong from, byte* mark )
|
|
{
|
|
llong offset;
|
|
for ( offset = from; offset >= 0; offset-- ) {
|
|
if ( ( bdGetByte( offset ) == 0x00 ) &&
|
|
( bdGetByte( offset + 1 ) == 0x00 ) &&
|
|
( bdGetByte( offset + 2 ) == 0x01 ) ) {
|
|
*mark = bdGetByte( offset + 3 );
|
|
return offset;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
llong K3bMpegInfo::FindNextVideo( llong from )
|
|
{
|
|
llong offset = from;
|
|
while ( offset >= 0 ) {
|
|
offset = FindNextMarker( offset );
|
|
if ( offset < 0 ) {
|
|
return -1;
|
|
}
|
|
if ( EnsureMPEG( offset, MPEG_VIDEO_E0_CODE ) || EnsureMPEG( offset, MPEG_VIDEO_E1_CODE ) || EnsureMPEG( offset, MPEG_VIDEO_E2_CODE ) ) {
|
|
return offset;
|
|
} else
|
|
offset++;
|
|
}
|
|
|
|
//shouldn't be here
|
|
return -1;
|
|
}
|
|
|
|
llong K3bMpegInfo::FindNextAudio( llong from )
|
|
{
|
|
llong offset = from;
|
|
while ( offset >= 0 ) {
|
|
offset = FindNextMarker( offset );
|
|
if ( offset < 0 ) {
|
|
return -1;
|
|
}
|
|
if ( EnsureMPEG( offset, MPEG_AUDIO_C0_CODE ) || EnsureMPEG( offset, MPEG_AUDIO_C1_CODE ) || EnsureMPEG( offset, MPEG_AUDIO_C2_CODE ) ) {
|
|
return offset;
|
|
} else
|
|
offset++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int K3bMpegInfo::GetVideoIdx ( byte marker )
|
|
{
|
|
switch ( marker ) {
|
|
case MPEG_VIDEO_E0_CODE:
|
|
return 0;
|
|
break;
|
|
|
|
case MPEG_VIDEO_E1_CODE:
|
|
return 1;
|
|
break;
|
|
|
|
case MPEG_VIDEO_E2_CODE:
|
|
return 2;
|
|
break;
|
|
|
|
default:
|
|
kdDebug() << "VideoCode not reached" << endl;
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int K3bMpegInfo::GetAudioIdx ( byte marker )
|
|
{
|
|
switch ( marker ) {
|
|
case MPEG_AUDIO_C0_CODE:
|
|
return 0;
|
|
break;
|
|
|
|
case MPEG_AUDIO_C1_CODE:
|
|
return 1;
|
|
break;
|
|
|
|
case MPEG_AUDIO_C2_CODE:
|
|
return 2;
|
|
break;
|
|
|
|
default:
|
|
kdDebug() << "VideoCode not reached" << endl;
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
llong K3bMpegInfo::SkipPacketHeader( llong offset )
|
|
{
|
|
byte tmp_byte;
|
|
if ( mpeg_info->version == MPEG_VERS_MPEG1 ) {
|
|
// skip startcode and packet size
|
|
offset += 6;
|
|
//remove stuffing bytes
|
|
tmp_byte = GetByte( offset );
|
|
while ( tmp_byte & 0x80 )
|
|
tmp_byte = GetByte( ++offset );
|
|
|
|
if ( ( tmp_byte & 0xC0 ) == 0x40 ) // next two bits are 01
|
|
offset += 2;
|
|
|
|
tmp_byte = GetByte( offset );
|
|
if ( ( tmp_byte & 0xF0 ) == 0x20 )
|
|
offset += 5;
|
|
else if ( ( tmp_byte & 0xF0 ) == 0x30 )
|
|
offset += 10;
|
|
else
|
|
offset++;
|
|
|
|
return offset;
|
|
} else if ( mpeg_info->version == MPEG_VERS_MPEG2 ) {
|
|
return ( offset + 9 + GetByte( offset + 8 ) );
|
|
} else
|
|
return ( offset + 10 );
|
|
}
|
|
|
|
void K3bMpegInfo::ParseAudio ( llong offset, byte marker )
|
|
{
|
|
unsigned brate, srate;
|
|
bool mpeg2_5 = false;
|
|
|
|
const int a_idx = GetAudioIdx( marker );
|
|
|
|
if ( mpeg_info->audio[ a_idx ].seen ) /* we have it already */
|
|
return ;
|
|
|
|
if ( ( GetByte( offset ) != 0xFF ) || ( ( GetByte( offset + 1 ) & 0xF0 ) != 0xF0 ) ) {
|
|
// doesn't start with 12 bits set
|
|
if ( ( GetByte( offset ) != 0xFF ) || ( ( GetByte( offset + 1 ) & 0xE0 ) != 0xE0 ) ) {
|
|
// doesn't start with 11 bits set
|
|
return ;
|
|
} else {
|
|
// start with 11 bits set
|
|
mpeg2_5 = true;
|
|
}
|
|
}
|
|
|
|
// Find mpeg version 1.0 or 2.0
|
|
if ( GetByte( offset + 1 ) & 0x08 ) {
|
|
if ( !mpeg2_5 )
|
|
mpeg_info->audio[ a_idx ].version = 1;
|
|
else
|
|
return ; // invalid 01 encountered
|
|
} else {
|
|
if ( !mpeg2_5 )
|
|
mpeg_info->audio[ a_idx ].version = 2;
|
|
else
|
|
mpeg_info->audio[ a_idx ].version = 3; //for mpeg 2.5
|
|
}
|
|
|
|
// Find Layer
|
|
mpeg_info->audio[ a_idx ].layer = ( GetByte( offset + 1 ) & 0x06 ) >> 1;
|
|
switch ( mpeg_info->audio[ a_idx ].layer ) {
|
|
case 0:
|
|
mpeg_info->audio[ a_idx ].layer = 0;
|
|
break;
|
|
case 1:
|
|
mpeg_info->audio[ a_idx ].layer = 3;
|
|
break;
|
|
case 2:
|
|
mpeg_info->audio[ a_idx ].layer = 2;
|
|
break;
|
|
case 3:
|
|
mpeg_info->audio[ a_idx ].layer = 1;
|
|
break;
|
|
}
|
|
|
|
// Protection Bit
|
|
mpeg_info->audio[ a_idx ].protect = GetByte( offset + 1 ) & 0x01;
|
|
if ( mpeg_info->audio[ a_idx ].protect )
|
|
mpeg_info->audio[ a_idx ].protect = 0;
|
|
else
|
|
mpeg_info->audio[ a_idx ].protect = 1;
|
|
|
|
const unsigned bit_rates[ 4 ][ 16 ] = {
|
|
{
|
|
0,
|
|
},
|
|
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
|
|
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
|
|
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}
|
|
};
|
|
|
|
|
|
/* const unsigned bit_rates [ 3 ][ 3 ][ 16 ] = {
|
|
{
|
|
{0, },
|
|
{0, },
|
|
{0, },
|
|
},
|
|
{
|
|
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
|
|
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
|
|
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}
|
|
},
|
|
{
|
|
{0, 32, 48, 56, 64, 80 , 96 , 112, 128, 144, 160, 176, 192, 224, 256, 0},
|
|
{0, 8, 16, 24, 32, 40, 48, 56, 64 , 80 , 96 , 112, 128, 144, 160, 0},
|
|
{0, 8, 16, 24, 32, 40, 48, 56, 64 , 80 , 96 , 112, 128, 144, 160, 0}
|
|
}
|
|
};
|
|
*/
|
|
|
|
const unsigned sampling_rates[ 4 ][ 4 ] = {
|
|
{
|
|
0,
|
|
},
|
|
{44100, 48000, 32000, 0}, //mpeg 1
|
|
{22050, 24000, 16000, 0}, //mpeg 2
|
|
{11025, 12000, 8000, 0} //mpeg 2.5
|
|
};
|
|
|
|
|
|
// Bitrate index and sampling index to pass through the array
|
|
brate = GetByte( offset + 2 ) >> 4;
|
|
srate = ( GetByte( offset + 2 ) & 0x0f ) >> 2;
|
|
|
|
mpeg_info->audio[ a_idx ].bitrate = 1024 * bit_rates[ mpeg_info->audio[ a_idx ].layer ][ brate ];
|
|
mpeg_info->audio[ a_idx ].byterate = ( float ) ( mpeg_info->audio[ a_idx ].bitrate / 8.0 );
|
|
mpeg_info->audio[ a_idx ].sampfreq = sampling_rates[ mpeg_info->audio[ a_idx ].version ][ srate ];
|
|
|
|
// Audio mode
|
|
mpeg_info->audio[ a_idx ].mode = 1 + ( GetByte( offset + 3 ) >> 6 ) ;
|
|
|
|
// Copyright bit
|
|
if ( GetByte( offset + 3 ) & 0x08 )
|
|
mpeg_info->audio[ a_idx ].copyright = true;
|
|
else
|
|
mpeg_info->audio[ a_idx ].copyright = false;
|
|
|
|
// Original/Copy bit
|
|
if ( GetByte( offset + 3 ) & 0x04 )
|
|
mpeg_info->audio[ a_idx ].original = true;
|
|
else
|
|
mpeg_info->audio[ a_idx ].original = false;
|
|
|
|
|
|
mpeg_info->audio[ a_idx ].seen = true;
|
|
}
|
|
|
|
void K3bMpegInfo::ParseVideo ( llong offset, byte marker )
|
|
{
|
|
unsigned long aratio, frate, brate;
|
|
|
|
const int v_idx = GetVideoIdx( marker );
|
|
|
|
const double aspect_ratios[ 16 ] = {
|
|
0.0000, 1.0000, 0.6735, 0.7031,
|
|
0.7615, 0.8055, 0.8437, 0.8935,
|
|
0.9375, 0.9815, 1.0255, 1.0695,
|
|
1.1250, 1.1575, 1.2015, 0.0000
|
|
};
|
|
|
|
if ( mpeg_info->video[ v_idx ].seen ) /* we have it already */
|
|
return ;
|
|
|
|
offset = FindNextMarker( offset + 1, MPEG_SEQUENCE_CODE );
|
|
|
|
if ( !offset )
|
|
return ;
|
|
|
|
offset += 4;
|
|
|
|
mpeg_info->video[ v_idx ].hsize = GetSize( offset ) >> 4;
|
|
mpeg_info->video[ v_idx ].vsize = GetSize( offset + 1 ) & 0x0FFF;
|
|
|
|
// Get picture rate
|
|
offset += 3; // after picture sizes
|
|
|
|
aratio = ( GetByte( offset ) & 0x0F ) >> 4;
|
|
mpeg_info->video[ v_idx ].aratio = aspect_ratios[ aratio ];
|
|
|
|
// offset += 3; // after picture sizes
|
|
frate = GetByte( offset ) & 0x0F;
|
|
mpeg_info->video[ v_idx ].frate = frame_rates[ frate ];
|
|
|
|
offset += 1; // after picture rate
|
|
|
|
// 18 following bytes are the bitrate /400
|
|
|
|
//read first 16 bytes
|
|
brate = GetSize( offset );
|
|
// scale
|
|
brate <<= 2;
|
|
byte lasttwo = GetByte( offset + 2 );
|
|
lasttwo >>= 6;
|
|
brate |= lasttwo;
|
|
|
|
mpeg_info->video[ v_idx ].bitrate = 400 * brate;
|
|
|
|
byte mark;
|
|
while ( true ) {
|
|
offset = FindNextMarker( offset, &mark );
|
|
if ( mark == MPEG_GOP_CODE )
|
|
break;
|
|
switch ( GetByte( offset + 3 ) ) {
|
|
case MPEG_EXT_CODE :
|
|
// Extension
|
|
offset += 4;
|
|
switch ( GetByte( offset ) >> 4 ) {
|
|
case 1:
|
|
//SequenceExt
|
|
if ( GetByte( offset + 1 ) & 0x08 )
|
|
mpeg_info->video[ v_idx ].progressive = true;
|
|
mpeg_info->video[ v_idx ].chroma_format = ( GetByte( offset + 1 ) & 0x06 ) >> 1;
|
|
break;
|
|
case 2:
|
|
// SequenceDisplayExt
|
|
mpeg_info->video[ v_idx ].video_format = ( GetByte( offset ) & 0x0E ) >> 1;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case MPEG_USER_CODE :
|
|
// UserData
|
|
break;
|
|
}
|
|
offset++;
|
|
}
|
|
|
|
mpeg_info->video[ v_idx ].seen = true;
|
|
}
|
|
|
|
double K3bMpegInfo::ReadTS( llong offset )
|
|
{
|
|
byte highbit;
|
|
unsigned long low4Bytes;
|
|
double TS;
|
|
|
|
highbit = ( GetByte( offset ) >> 3 ) & 0x01;
|
|
|
|
low4Bytes = ( ( GetByte( offset ) >> 1 ) & 0x03 ) << 30;
|
|
low4Bytes |= GetByte( offset + 1 ) << 22;
|
|
low4Bytes |= ( GetByte( offset + 2 ) >> 1 ) << 15;
|
|
low4Bytes |= GetByte( offset + 3 ) << 7;
|
|
low4Bytes |= GetByte( offset + 4 ) >> 1;
|
|
|
|
|
|
TS = ( double ) ( highbit * FLOAT_0x10000 * FLOAT_0x10000 );
|
|
TS += ( double ) ( low4Bytes );
|
|
TS /= ( double ) ( STD_SYSTEM_CLOCK_FREQ );
|
|
|
|
return TS;
|
|
}
|
|
|
|
double K3bMpegInfo::ReadTSMpeg2( llong offset )
|
|
{
|
|
byte highbit;
|
|
unsigned long low4Bytes;
|
|
unsigned long sys_clock_ref;
|
|
double TS;
|
|
|
|
highbit = ( GetByte( offset ) & 0x20 ) >> 5;
|
|
|
|
low4Bytes = ( ( GetByte( offset ) & 0x18 ) >> 3 ) << 30;
|
|
low4Bytes |= ( GetByte( offset ) & 0x03 ) << 28;
|
|
low4Bytes |= GetByte( offset + 1 ) << 20;
|
|
low4Bytes |= ( GetByte( offset + 2 ) & 0xF8 ) << 12;
|
|
low4Bytes |= ( GetByte( offset + 2 ) & 0x03 ) << 13;
|
|
low4Bytes |= GetByte( offset + 3 ) << 5;
|
|
low4Bytes |= ( GetByte( offset + 4 ) ) >> 3;
|
|
|
|
sys_clock_ref = ( GetByte( offset + 4 ) & 0x3 ) << 7;
|
|
sys_clock_ref |= ( GetByte( offset + 5 ) >> 1 );
|
|
|
|
TS = ( double ) ( highbit * FLOAT_0x10000 * FLOAT_0x10000 );
|
|
TS += ( double ) ( low4Bytes );
|
|
if ( sys_clock_ref == 0 )
|
|
TS /= ( double ) ( STD_SYSTEM_CLOCK_FREQ );
|
|
else {
|
|
TS /= ( double ) ( 27000000 / sys_clock_ref );
|
|
}
|
|
|
|
return TS;
|
|
}
|