Skip to content

Commit 49b6982

Browse files
cfsmp3claude
andcommitted
fix(windows): Prevent CEA-708 output file truncation on Windows
On Windows, when processing MP4/MOV files with CEA-708 captions, the output file was being truncated to only the last subtitle. This occurred because: 1. C code opened the file using open() and stored the fd in writer->fd 2. At end of processing, Rust's ccxr_flush_decoder was called 3. Rust checked writer->fhandle (a separate Windows-specific field) 4. Since fhandle was null (C only set fd), Rust called File::create() 5. File::create() truncates existing files, losing all previous content The fix checks if fd is already valid before creating a new file. If fd is valid, it converts it to a Windows handle using _get_osfhandle(), avoiding the file truncation. Fixes #1449 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 6fdfde0 commit 49b6982

File tree

1 file changed

+34
-14
lines changed

1 file changed

+34
-14
lines changed

src/rust/src/decoder/tv_screen.rs

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ use std::os::unix::prelude::IntoRawFd;
1010
use std::os::windows::io::IntoRawHandle;
1111
use std::{ffi::CStr, fs::File};
1212

13+
#[cfg(windows)]
14+
use crate::bindings::_get_osfhandle;
15+
1316
use super::output::{color_to_hex, write_char, Writer};
1417
use super::timing::{get_scc_time_str, get_time_str};
1518
use super::{CCX_DTVCC_SCREENGRID_COLUMNS, CCX_DTVCC_SCREENGRID_ROWS};
@@ -82,21 +85,38 @@ impl dtvcc_tv_screen {
8285
}
8386

8487
#[cfg(windows)]
85-
if writer.writer_ctx.filename.is_null() && writer.writer_ctx.fhandle.is_null() {
86-
return Err("Filename missing".to_owned())?;
87-
} else if writer.writer_ctx.fhandle.is_null() {
88-
let filename = unsafe {
89-
CStr::from_ptr(writer.writer_ctx.filename)
90-
.to_str()
91-
.map_err(|err| err.to_string())
92-
}?;
93-
debug!("dtvcc_writer_output: creating {}", filename);
94-
let file = File::create(filename).map_err(|err| err.to_string())?;
95-
writer.writer_ctx.fhandle = file.into_raw_handle();
88+
if writer.writer_ctx.fhandle.is_null() {
89+
// Check if fd is valid (file was already opened by C code)
90+
// If so, convert fd to fhandle to avoid creating a new file (which would truncate)
91+
if writer.writer_ctx.fd >= 0 {
92+
let handle = unsafe { _get_osfhandle(writer.writer_ctx.fd) };
93+
if handle != -1 {
94+
debug!(
95+
"dtvcc_writer_output: converting fd {} to fhandle",
96+
writer.writer_ctx.fd
97+
);
98+
writer.writer_ctx.fhandle = handle as *mut _;
99+
}
100+
}
96101

97-
if is_false(writer.no_bom) {
98-
let BOM = [0xef, 0xbb, 0xbf];
99-
writer.write_to_file(&BOM)?;
102+
// If fhandle is still null, we need to create a new file
103+
if writer.writer_ctx.fhandle.is_null() {
104+
if writer.writer_ctx.filename.is_null() {
105+
return Err("Filename missing".to_owned())?;
106+
}
107+
let filename = unsafe {
108+
CStr::from_ptr(writer.writer_ctx.filename)
109+
.to_str()
110+
.map_err(|err| err.to_string())
111+
}?;
112+
debug!("dtvcc_writer_output: creating {}", filename);
113+
let file = File::create(filename).map_err(|err| err.to_string())?;
114+
writer.writer_ctx.fhandle = file.into_raw_handle();
115+
116+
if is_false(writer.no_bom) {
117+
let BOM = [0xef, 0xbb, 0xbf];
118+
writer.write_to_file(&BOM)?;
119+
}
100120
}
101121
}
102122
self.write(writer);

0 commit comments

Comments
 (0)