Commit a00addff authored by Laurent Aimar's avatar Laurent Aimar

* mkv: clean up, more input infos, begin to parse Tags element (don't work

 yet).
parent 80e0bf5c
......@@ -2,7 +2,7 @@
* mkv.cpp : matroska demuxer
*****************************************************************************
* Copyright (C) 2001 VideoLAN
* $Id: mkv.cpp,v 1.8 2003/06/24 06:07:14 fenrir Exp $
* $Id: mkv.cpp,v 1.9 2003/06/24 18:42:50 fenrir Exp $
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
*
......@@ -28,6 +28,10 @@
#include <vlc/vlc.h>
#ifdef HAVE_TIME_H
# include <time.h> /* time() */
#endif
#include <vlc/input.h>
#include <codecs.h> /* BITMAPINFOHEADER, WAVEFORMATEX */
......@@ -35,6 +39,7 @@
#include <iostream>
#include <cassert>
#include <typeinfo>
#include <wchar.h>
/* libebml and matroska */
#include "ebml/EbmlHead.h"
......@@ -59,6 +64,8 @@
#include "matroska/KaxSeekHead.h"
#include "matroska/KaxSegment.h"
#include "matroska/KaxTag.h"
#include "matroska/KaxTags.h"
#include "matroska/KaxTagMulti.h"
#include "matroska/KaxTracks.h"
#include "matroska/KaxTrackAudio.h"
#include "matroska/KaxTrackVideo.h"
......@@ -80,10 +87,12 @@ static int Demux ( input_thread_t * );
* Module descriptor
*****************************************************************************/
vlc_module_begin();
#if 0
add_category_hint( N_("mkv-demuxer"), NULL, VLC_TRUE );
add_bool( "mkv-index", 0, NULL,
N_("Create index if no cues found"),
N_("Create index if no cues found"), VLC_TRUE );
#endif
set_description( _("mka/mkv stream demuxer" ) );
set_capability( "demux", 50 );
set_callbacks( Activate, Deactivate );
......@@ -99,6 +108,7 @@ class vlc_stream_io_callback: public IOCallback
{
private:
input_thread_t *p_input;
vlc_bool_t mb_eof;
public:
vlc_stream_io_callback( input_thread_t * );
......@@ -110,448 +120,184 @@ class vlc_stream_io_callback: public IOCallback
virtual void close ( void );
};
vlc_stream_io_callback::vlc_stream_io_callback( input_thread_t *p_input_ )
{
p_input = p_input_;
}
uint32_t vlc_stream_io_callback::read( void *p_buffer, size_t i_size )
/*****************************************************************************
* Ebml Stream parser
*****************************************************************************/
class EbmlParser
{
data_packet_t *p_data;
public:
EbmlParser( EbmlStream *es, EbmlElement *el_start );
~EbmlParser( void );
int i_count;
int i_read = 0;
void Up( void );
void Down( void );
EbmlElement *Get( void );
void Keep( void );
int GetLevel( void );
if( !i_size )
{
return 0;
}
private:
EbmlStream *m_es;
int mi_level;
EbmlElement *m_el[6];
do
{
i_count = input_SplitBuffer(p_input, &p_data, __MIN( i_size, 10240 ) );
if( i_count <= 0 )
{
return i_read;
}
memcpy( p_buffer, p_data->p_payload_start, i_count );
input_DeletePacket( p_input->p_method_data, p_data );
EbmlElement *m_got;
(uint8_t*)p_buffer += i_count;
i_size -= i_count;
i_read += i_count;
int mi_user_level;
vlc_bool_t mb_keep;
};
} while( i_size );
return i_read;
/*****************************************************************************
* Some functions to manipulate memory
*****************************************************************************/
#define GetWLE( p ) __GetWLE( (uint8_t*)p )
#define GetDWLE( p ) __GetDWLE( (uint8_t*)p )
#define GetFOURCC( p ) __GetFOURCC( (uint8_t*)p )
static uint16_t __GetWLE( uint8_t *p )
{
return (uint16_t)p[0] | ( ((uint16_t)p[1]) << 8 );
}
static uint32_t __GetDWLE( uint8_t *p )
{
return (uint32_t)p[0] | ( ((uint32_t)p[1]) << 8 ) |
( ((uint32_t)p[2]) << 16 ) | ( ((uint32_t)p[3]) << 24 );
}
static vlc_fourcc_t __GetFOURCC( uint8_t *p )
{
return VLC_FOURCC( p[0], p[1], p[2], p[3] );
}
void vlc_stream_io_callback::setFilePointer(int64_t i_offset, seek_mode mode )
/*****************************************************************************
* definitions of structures and functions used by this plugins
*****************************************************************************/
typedef struct
{
int64_t i_pos;
int64_t i_last;
int i_cat;
vlc_bool_t b_default;
int i_number;
i_last = getFilePointer();
int i_extra_data;
uint8_t *p_extra_data;
vlc_mutex_lock( &p_input->stream.stream_lock );
switch( mode )
{
case seek_beginning:
i_pos = i_offset;
break;
case seek_end:
i_pos = p_input->stream.p_selected_area->i_size - i_offset;
break;
default:
i_pos= i_last + i_offset;
break;
}
char *psz_language;
if( i_pos < 0 ||
( i_pos > p_input->stream.p_selected_area->i_size && p_input->stream.p_selected_area->i_size != 0 ) )
{
msg_Err( p_input, "seeking to wrong place (i_pos=%lld)", i_pos );
vlc_mutex_unlock( &p_input->stream.stream_lock );
return;
}
vlc_mutex_unlock( &p_input->stream.stream_lock );
char *psz_codec;
vlc_fourcc_t i_codec;
if( i_pos == i_last )
{
return;
}
uint64_t i_default_duration;
/* video */
int i_width;
int i_height;
int i_display_width;
int i_display_height;
float f_fps;
msg_Dbg( p_input, "####################seek new=%lld old=%lld", i_pos, getFilePointer() );
if( p_input->stream.b_seekable &&
( /*p_input->stream.i_method == INPUT_METHOD_FILE ||*/ i_pos < i_last || i_pos - i_last > p_input->i_bufsize / 4 ) )
{
input_AccessReinit( p_input );
p_input->pf_seek( p_input, i_pos );
}
else if( i_pos > i_last )
{
data_packet_t *p_data;
int i_skip = i_pos - i_last;
/* audio */
int i_channels;
int i_samplerate;
int i_bitspersample;
if( i_skip > 1024 )
{
msg_Warn( p_input, "will skip %d bytes, slow", i_skip );
}
es_descriptor_t *p_es;
while (i_skip > 0 )
{
int i_read;
i_read = input_SplitBuffer( p_input, &p_data,
__MIN( 4096, i_skip ) );
if( i_read < 0 )
{
msg_Err( p_input, "seek failed" );
return;
}
i_skip -= i_read;
input_DeletePacket( p_input->p_method_data, p_data );
if( i_read == 0 && i_skip > 0 )
{
msg_Err( p_input, "seek failed" );
return;
}
}
}
else
{
msg_Err( p_input, "cannot seek or emulate seek to %lld from %lld", i_pos, i_last );
}
}
vlc_bool_t b_inited;
/* data to be send first */
int i_data_init;
uint8_t *p_data_init;
size_t vlc_stream_io_callback::write( const void *p_buffer, size_t i_size )
{
return 0;
}
/* hack : it's for seek */
vlc_bool_t b_search_keyframe;
} mkv_track_t;
uint64_t vlc_stream_io_callback::getFilePointer( void )
typedef struct
{
uint64_t i_pos;
vlc_mutex_lock( &p_input->stream.stream_lock );
i_pos= p_input->stream.p_selected_area->i_tell;
vlc_mutex_unlock( &p_input->stream.stream_lock );
int i_track;
int i_block_number;
return i_pos;
}
int64_t i_position;
int64_t i_time;
void vlc_stream_io_callback::close( void )
{
return;
}
vlc_bool_t b_key;
} mkv_index_t;
/*****************************************************************************
* Ebml Stream parser
*****************************************************************************/
class EbmlParser
struct demux_sys_t
{
public:
EbmlParser( EbmlStream *es, EbmlElement *el_start );
~EbmlParser( void );
vlc_stream_io_callback *in;
EbmlStream *es;
EbmlParser *ep;
int SetNext( const EbmlCallbacks & ClassInfos );
/* time scale */
uint64_t i_timescale;
void Up( void );
void Down( void );
EbmlElement *Get( void );
void Keep( void );
/* duration of the segment */
float f_duration;
int GetLevel( void );
/* all tracks */
int i_track;
mkv_track_t *track;
private:
/* from seekhead */
int64_t i_cues_position;
int64_t i_chapters_position;
int64_t i_tags_position;
EbmlStream *m_es;
int mi_level;
EbmlElement *m_el[6];
/* current data */
KaxSegment *segment;
KaxCluster *cluster;
EbmlElement *m_got;
mtime_t i_pts;
int mi_user_level;
vlc_bool_t mb_keep;
vlc_bool_t b_cues;
int i_index;
int i_index_max;
mkv_index_t *index;
/* info */
char *psz_muxing_application;
char *psz_writing_application;
char *psz_segment_filename;
char *psz_title;
char *psz_date_utc;
};
EbmlParser::EbmlParser( EbmlStream *es, EbmlElement *el_start )
#define MKVD_TIMECODESCALE 1000000
static void IndexAppendCluster ( input_thread_t *p_input, KaxCluster *cluster );
static char *UTF8ToStr ( const UTFstring &u );
static void LoadCues ( input_thread_t *);
static void InformationsCreate ( input_thread_t *p_input );
/*****************************************************************************
* Activate: initializes matroska demux structures
*****************************************************************************/
static int Activate( vlc_object_t * p_this )
{
int i;
input_thread_t *p_input = (input_thread_t *)p_this;
demux_sys_t *p_sys;
uint8_t *p_peek;
m_es = es;
m_got = NULL;
m_el[0] = el_start;
int i_track;
vlc_bool_t b_audio_selected;
int i_spu_channel, i_audio_channel;
for( i = 1; i < 6; i++ )
EbmlElement *el = NULL, *el1 = NULL, *el2 = NULL, *el3 = NULL, *el4 = NULL;
/* Initialize access plug-in structures. */
if( p_input->i_mtu == 0 )
{
m_el[i] = NULL;
/* Improve speed. */
p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
}
mi_level = 1;
mi_user_level = 1;
mb_keep = VLC_FALSE;
}
EbmlParser::~EbmlParser( void )
{
int i;
/* Set the demux function */
p_input->pf_demux = Demux;
for( i = 1; i < mi_level; i++ )
{
if( !mb_keep )
{
delete m_el[i];
}
mb_keep = VLC_FALSE;
}
}
void EbmlParser::Up( void )
{
if( mi_user_level == mi_level )
{
fprintf( stderr," arrrrrrrrrrrrrg Up cannot escape itself\n" );
}
mi_user_level--;
}
void EbmlParser::Down( void )
{
mi_user_level++;
mi_level++;
}
void EbmlParser::Keep( void )
{
mb_keep = VLC_TRUE;
}
int EbmlParser::GetLevel( void )
{
return mi_user_level;
}
EbmlElement *EbmlParser::Get( void )
{
int i_ulev = 0;
if( mi_user_level != mi_level )
{
return NULL;
}
if( m_got )
{
EbmlElement *ret = m_got;
m_got = NULL;
return ret;
}
if( m_el[mi_level] )
{
m_el[mi_level]->SkipData( *m_es, m_el[mi_level]->Generic().Context );
if( !mb_keep )
{
delete m_el[mi_level];
}
mb_keep = VLC_FALSE;
}
m_el[mi_level] = m_es->FindNextElement( m_el[mi_level - 1]->Generic().Context, i_ulev, 0xFFFFFFFFL, true, 1 );
if( i_ulev > 0 )
{
while( i_ulev > 0 )
{
if( mi_level == 1 )
{
mi_level = 0;
return NULL;
}
delete m_el[mi_level - 1];
m_got = m_el[mi_level -1] = m_el[mi_level];
m_el[mi_level] = NULL;
mi_level--;
i_ulev--;
}
return NULL;
}
else if( m_el[mi_level] == NULL )
{
fprintf( stderr," m_el[mi_level] == NULL\n" );
}
return m_el[mi_level];
}
/*****************************************************************************
* Some functions to manipulate memory
*****************************************************************************/
#define GetWLE( p ) __GetWLE( (uint8_t*)p )
#define GetDWLE( p ) __GetDWLE( (uint8_t*)p )
#define GetFOURCC( p ) __GetFOURCC( (uint8_t*)p )
static uint16_t __GetWLE( uint8_t *p )
{
return (uint16_t)p[0] | ( ((uint16_t)p[1]) << 8 );
}
static uint32_t __GetDWLE( uint8_t *p )
{
return (uint32_t)p[0] | ( ((uint32_t)p[1]) << 8 ) |
( ((uint32_t)p[2]) << 16 ) | ( ((uint32_t)p[3]) << 24 );
}
static vlc_fourcc_t __GetFOURCC( uint8_t *p )
{
return VLC_FOURCC( p[0], p[1], p[2], p[3] );
}
/*****************************************************************************
* definitions of structures and functions used by this plugins
*****************************************************************************/
typedef struct
{
int i_cat;
vlc_bool_t b_default;
int i_number;
int i_extra_data;
uint8_t *p_extra_data;
char *psz_language;
char *psz_codec;
vlc_fourcc_t i_codec;
uint64_t i_default_duration;
/* video */
int i_width;
int i_height;
int i_display_width;
int i_display_height;
float f_fps;
/* audio */
int i_channels;
int i_samplerate;
int i_bitspersample;
es_descriptor_t *p_es;
vlc_bool_t b_inited;
/* data to be send first */
int i_data_init;
uint8_t *p_data_init;
/* hack : it's for seek */
vlc_bool_t b_search_keyframe;
} mkv_track_t;
typedef struct
{
int i_track;
int i_block_number;
int64_t i_position;
int64_t i_time;
vlc_bool_t b_key;
} mkv_index_t;
struct demux_sys_t
{
vlc_stream_io_callback *in;
EbmlStream *es;
EbmlParser *ep;
/* time scale */
uint64_t i_timescale;
/* duration of the segment */
float f_duration;
/* all tracks */
int i_track;
mkv_track_t *track;
/* from seekhead */
int64_t i_cues_position;
int64_t i_chapters_position;
/* current data */
KaxSegment *segment;
KaxCluster *cluster;
mtime_t i_pts;
vlc_bool_t b_cues;
int i_index;
int i_index_max;
mkv_index_t *index;
};
#define MKVD_TIMECODESCALE 1000000
static void IndexAppendCluster( input_thread_t *p_input, KaxCluster *cluster )
{
demux_sys_t *p_sys = p_input->p_demux_data;
#define idx p_sys->index[p_sys->i_index]
idx.i_track = -1;
idx.i_block_number= -1;
idx.i_position = cluster->GetElementPosition();
idx.i_time = -1;
idx.b_key = VLC_TRUE;
p_sys->i_index++;
if( p_sys->i_index >= p_sys->i_index_max )
{
p_sys->i_index_max += 1024;
p_sys->index = (mkv_index_t*)realloc( p_sys->index, sizeof( mkv_index_t ) * p_sys->i_index_max );
}
#undef idx
}
/*****************************************************************************
* Activate: initializes matroska demux structures
*****************************************************************************/
static int Activate( vlc_object_t * p_this )
{
input_thread_t *p_input = (input_thread_t *)p_this;
demux_sys_t *p_sys;
uint8_t *p_peek;
int i_track;
vlc_bool_t b_audio_selected;
int i_spu_channel, i_audio_channel;
EbmlElement *el = NULL, *el1 = NULL, *el2 = NULL, *el3 = NULL, *el4 = NULL;
input_info_category_t *p_cat;
/* Initialize access plug-in structures. */
if( p_input->i_mtu == 0 )
{
/* Improve speed. */
p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
}
/* Set the demux function */
p_input->pf_demux = Demux;
/* peek the begining */
if( input_Peek( p_input, &p_peek, 4 ) < 4 )
/* peek the begining */
if( input_Peek( p_input, &p_peek, 4 ) < 4 )
{
msg_Warn( p_input, "cannot peek" );
return VLC_EGENERIC;
......@@ -576,12 +322,19 @@ static int Activate( vlc_object_t * p_this )
p_sys->i_pts = 0;
p_sys->i_cues_position = -1;
p_sys->i_chapters_position = -1;
p_sys->i_tags_position = -1;
p_sys->b_cues = VLC_FALSE;
p_sys->i_index = 0;
p_sys->i_index_max = 1024;
p_sys->index = (mkv_index_t*)malloc( sizeof( mkv_index_t ) * p_sys->i_index_max );
p_sys->psz_muxing_application = NULL;
p_sys->psz_writing_application = NULL;
p_sys->psz_segment_filename = NULL;
p_sys->psz_title = NULL;
p_sys->psz_date_utc = NULL;;
if( p_sys->es == NULL )
{
msg_Err( p_input, "failed to create EbmlStream" );
......@@ -641,11 +394,72 @@ static int Activate( vlc_object_t * p_this )
msg_Dbg( p_input, "| | + Duration=%f", p_sys->f_duration );
}
else
else if( EbmlId( *el2 ) == KaxMuxingApp::ClassInfos.GlobalId )
{
msg_Dbg( p_input, "| | + Unknow (%s)", typeid(*el2).name() );
KaxMuxingApp &mapp = *(KaxMuxingApp*)el2;
mapp.ReadData( p_sys->es->I_O() );
p_sys->psz_muxing_application = UTF8ToStr( UTFstring( mapp ) );
msg_Dbg( p_input, "| | + Muxing Application=%s", p_sys->psz_muxing_application );
}
}
else if( EbmlId( *el2 ) == KaxWritingApp::ClassInfos.GlobalId )
{
KaxWritingApp &wapp = *(KaxWritingApp*)el2;
wapp.ReadData( p_sys->es->I_O() );
p_sys->psz_writing_application = UTF8ToStr( UTFstring( wapp ) );
msg_Dbg( p_input, "| | + Wrinting Application=%s", p_sys->psz_writing_application );
}
else if( EbmlId( *el2 ) == KaxSegmentFilename::ClassInfos.GlobalId )
{
KaxSegmentFilename &sfn = *(KaxSegmentFilename*)el2;
sfn.ReadData( p_sys->es->I_O() );
p_sys->psz_segment_filename = UTF8ToStr( UTFstring( sfn ) );
msg_Dbg( p_input, "| | + Segment Filename=%s", p_sys->psz_segment_filename );
}
else if( EbmlId( *el2 ) == KaxTitle::ClassInfos.GlobalId )
{
KaxTitle &title = *(KaxTitle*)el2;