Skip to content

Commit 0b5f13e

Browse files
authored
feat(wtv): Add DVB teletext stream detection in WTV files
2 parents d758f31 + 60cec9e commit 0b5f13e

File tree

2 files changed

+102
-4
lines changed

2 files changed

+102
-4
lines changed

src/lib_ccx/wtv_constants.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#define WTV_STREAM_VIDEO "\x76\x69\x64\x73\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71"
77
#define WTV_STREAM_AUDIO "\x61\x75\x64\x73\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71"
88
#define WTV_STREAM_MSTVCAPTION "\x89\x8A\x8B\xB8\x49\xB0\x80\x4C\xAD\xCF\x58\x98\x98\x5E\x22\xC1"
9+
// DVB Teletext stream type (VBI teletext data in PES format)
10+
#define WTV_STREAM_TELETEXT "\xE3\x76\x2A\xF7\x0A\xEB\xD0\x11\xAC\xE4\x00\x00\xC0\xCC\x16\xBA"
911
#define WTV_EOF "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1012
#define WTV_TIMING "\x5B\x05\xE6\x1B\x97\xA9\x49\x43\x88\x17\x1A\x65\x5A\x29\x8A\x97"
1113

src/lib_ccx/wtv_functions.c

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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
4050
void 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)
335345
LLONG 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

Comments
 (0)