@@ -36,6 +36,16 @@ int check_stream_id(int stream_id, int video_streams[], int num_streams)
3636 return 0 ;
3737}
3838
39+ // Check if the passed stream_id is a teletext stream
40+ int check_teletext_stream_id (int stream_id , int teletext_streams [], int num_teletext_streams )
41+ {
42+ int x ;
43+ for (x = 0 ; x < num_teletext_streams ; x ++ )
44+ if (teletext_streams [x ] == stream_id )
45+ return 1 ;
46+ return 0 ;
47+ }
48+
3949// Init passes wtv_chunked_buffer struct
4050void init_chunked_buffer (struct wtv_chunked_buffer * cb )
4151{
@@ -335,9 +345,12 @@ int read_header(struct ccx_demuxer *ctx, struct wtv_chunked_buffer *cb)
335345LLONG get_data (struct lib_ccx_ctx * ctx , struct wtv_chunked_buffer * cb , struct demuxer_data * data )
336346{
337347 static int video_streams [32 ];
348+ static int teletext_streams [32 ];
338349 static int alt_stream ; // Stream to use for timestamps if the cc stream has broken timestamps
339350 static int use_alt_stream = 0 ;
340351 static int num_streams = 0 ;
352+ static int num_teletext_streams = 0 ;
353+ static int has_teletext = 0 ; // Flag to indicate we found teletext streams
341354 int64_t result ;
342355 struct lib_cc_decode * dec_ctx = update_decoder_list (ctx );
343356
@@ -403,12 +416,18 @@ LLONG get_data(struct lib_ccx_ctx *ctx, struct wtv_chunked_buffer *cb, struct de
403416 {
404417 // The WTV_STREAM2 GUID appears near the start of the data dir
405418 // It maps stream_ids to the type of stream
419+ // Structure based on WTV file analysis:
420+ // Offset 0x0C: mediatype GUID (16 bytes)
421+ // Offset 0x4C: teletext format GUID (16 bytes) - for MSTVCAPTION streams
422+ // We read enough data (96 bytes) to get all the info we need
406423 dbg_print (CCX_DMT_PARSE , "WTV STREAM2\n" );
407- get_sized_buffer (ctx -> demux_ctx , cb , 0xc + 16 );
424+ // Read 96 bytes to get mediatype at 0x0C and format_subtype at 0x4C
425+ uint32_t read_size = (len > 96 ) ? 96 : len ;
426+ get_sized_buffer (ctx -> demux_ctx , cb , read_size );
408427 if (cb -> buffer == NULL )
409428 return CCX_EOF ;
410429 static unsigned char stream_type [16 ];
411- memcpy (& stream_type , cb -> buffer + 0xc , 16 ); // Read the stream type GUID
430+ memcpy (& stream_type , cb -> buffer + 0xc , 16 ); // Read the mediatype GUID at offset 12
412431 const void * stream_guid ;
413432 if (ccx_options .wtvmpeg2 )
414433 stream_guid = WTV_STREAM_VIDEO ; // We want mpeg2 data if the user set -wtvmpeg2
@@ -419,11 +438,40 @@ LLONG get_data(struct lib_ccx_ctx *ctx, struct wtv_chunked_buffer *cb, struct de
419438 video_streams [num_streams ] = stream_id ; // We keep a list of stream ids
420439 num_streams ++ ; // Even though there should only be 1
421440 }
441+ // For MSTVCAPTION streams, check if it's teletext by examining the format GUID
442+ // The teletext GUID appears at offset 0x4C from the start of the chunk data
443+ if (!memcmp (stream_type , WTV_STREAM_MSTVCAPTION , 16 ) && read_size >= 0x4C + 16 )
444+ {
445+ static unsigned char format_subtype [16 ];
446+ memcpy (& format_subtype , cb -> buffer + 0x4C , 16 ); // Read format GUID at offset 0x4C
447+ dbg_print (CCX_DMT_PARSE , "MSTVCAPTION format_subtype=%02X%02X%02X%02X...\n" ,
448+ format_subtype [0 ], format_subtype [1 ], format_subtype [2 ], format_subtype [3 ]);
449+ // Check for teletext
450+ if (!memcmp (format_subtype , WTV_STREAM_TELETEXT , 16 ))
451+ {
452+ dbg_print (CCX_DMT_PARSE , "Found DVB Teletext stream, stream_id: 0x%X\n" , stream_id );
453+ mprint ("WTV: Found DVB Teletext stream (stream_id=0x%X)\n" , stream_id );
454+ mprint (" Note: WTV teletext uses Microsoft VBI format which may not decode correctly.\n" );
455+ teletext_streams [num_teletext_streams ] = stream_id ;
456+ num_teletext_streams ++ ;
457+ has_teletext = 1 ;
458+ // Initialize teletext decoder context
459+ if (!dec_ctx -> private_data )
460+ {
461+ dec_ctx -> codec = CCX_CODEC_TELETEXT ;
462+ dec_ctx -> private_data = telxcc_init ();
463+ if (!dec_ctx -> private_data )
464+ {
465+ mprint ("Error: Failed to initialize teletext decoder\n" );
466+ }
467+ }
468+ }
469+ }
422470 if (memcmp (stream_type , WTV_STREAM_AUDIO , 16 ))
423471 alt_stream = stream_id ;
424- len -= 28 ;
472+ len -= read_size ;
425473 }
426- if (!memcmp (guid , WTV_TIMING , 16 ) && ((use_alt_stream < WTV_CC_TIMESTAMP_MAGIC_THRESH && check_stream_id (stream_id , video_streams , num_streams )) || (use_alt_stream == WTV_CC_TIMESTAMP_MAGIC_THRESH && stream_id == alt_stream )))
474+ if (!memcmp (guid , WTV_TIMING , 16 ) && ((use_alt_stream < WTV_CC_TIMESTAMP_MAGIC_THRESH && ( check_stream_id (stream_id , video_streams , num_streams ) || check_teletext_stream_id ( stream_id , teletext_streams , num_teletext_streams ) )) || (use_alt_stream == WTV_CC_TIMESTAMP_MAGIC_THRESH && stream_id == alt_stream )))
427475 {
428476 int64_t time ;
429477 // The WTV_TIMING GUID contains a timestamp for the given stream_id
@@ -478,6 +526,54 @@ LLONG get_data(struct lib_ccx_ctx *ctx, struct wtv_chunked_buffer *cb, struct de
478526 }
479527 return bytesread ;
480528 }
529+ // Handle DVB Teletext data
530+ // Note: WTV teletext format is Microsoft-specific and differs from DVB teletext.
531+ // The data is not in standard DVB teletext data unit format, so decoding support
532+ // is currently limited. The stream is detected and passed to the decoder which
533+ // will process what it can parse.
534+ if (!memcmp (guid , WTV_DATA , 16 ) && check_teletext_stream_id (stream_id , teletext_streams , num_teletext_streams ) && dec_ctx -> timing -> current_pts != 0 )
535+ {
536+ get_sized_buffer (ctx -> demux_ctx , cb , len );
537+ if (cb -> buffer == NULL )
538+ return CCX_EOF ;
539+
540+ // WTV teletext data is raw VBI data, not PES-encapsulated.
541+ // Wrap it in a PES header for the teletext decoder.
542+ uint16_t pes_len = len + 8 ; // payload + header fields after length
543+ int64_t pts = dec_ctx -> timing -> current_pts ;
544+
545+ // Add PES header (14 bytes)
546+ data -> buffer [data -> len ++ ] = 0x00 ; // start code
547+ data -> buffer [data -> len ++ ] = 0x00 ;
548+ data -> buffer [data -> len ++ ] = 0x01 ;
549+ data -> buffer [data -> len ++ ] = 0xBD ; // Private Stream 1
550+ data -> buffer [data -> len ++ ] = (pes_len >> 8 ) & 0xFF ;
551+ data -> buffer [data -> len ++ ] = pes_len & 0xFF ;
552+ data -> buffer [data -> len ++ ] = 0x80 ; // PES flags
553+ data -> buffer [data -> len ++ ] = 0x80 ; // PTS present
554+ data -> buffer [data -> len ++ ] = 0x05 ; // header data length
555+
556+ // Encode PTS (33-bit value in 5 bytes)
557+ data -> buffer [data -> len ++ ] = (uint8_t )(0x21 | ((pts >> 29 ) & 0x0E ));
558+ data -> buffer [data -> len ++ ] = (uint8_t )((pts >> 22 ) & 0xFF );
559+ data -> buffer [data -> len ++ ] = (uint8_t )(0x01 | ((pts >> 14 ) & 0xFE ));
560+ data -> buffer [data -> len ++ ] = (uint8_t )((pts >> 7 ) & 0xFF );
561+ data -> buffer [data -> len ++ ] = (uint8_t )(0x01 | ((pts << 1 ) & 0xFE ));
562+
563+ // Add teletext data
564+ memcpy (data -> buffer + data -> len , cb -> buffer , len );
565+ data -> len += len ;
566+ bytesread += (int )(14 + len );
567+ data -> codec = CCX_CODEC_TELETEXT ;
568+ data -> bufferdatatype = CCX_TELETEXT ;
569+ frames_since_ref_time ++ ;
570+ set_fts (dec_ctx -> timing );
571+ if (pad > 0 )
572+ {
573+ skip_sized_buffer (ctx -> demux_ctx , cb , pad );
574+ }
575+ return bytesread ;
576+ }
481577 if (len + pad > 0 )
482578 {
483579 // skip any remaining data
0 commit comments