Fix mobile black-screen: declared video dimensions didn't match the file
ProcessedMedia.width/height for downloaded videos were always VK's original source dimensions (e.g. 3840x2160 for a 4K upload), passed straight through to Telegram's sendVideo width/height params - even though the actual file sent is capped to video_max_height (e.g. 720p) by _select_format. A client declaring 4K but receiving a 720p stream is a known cause of black-screen playback on strict mobile decoders, which can allocate the render surface from the declared size before the real stream is probed - matches the reported symptom exactly (fine on desktop and in PiP, black in Telegram mobile's fullscreen decoder path). Neither the faststart remux nor the negative-DTS fix addressed this, since both were about the file's own internal structure, not the metadata describing it externally. Adds _probe_video_dims (ffprobe on the actual downloaded/remuxed file) and uses its real width/height/duration instead of VK's pre-download values. Verified on the actual failing video: now reports the real 1280x720 instead of VK's original 3840x2160. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+36
-3
@@ -207,6 +207,38 @@ class MediaProcessor:
|
|||||||
await unregister_active_path(path)
|
await unregister_active_path(path)
|
||||||
return fixed_path
|
return fixed_path
|
||||||
|
|
||||||
|
async def _probe_video_dims(self, path: Path) -> tuple[Optional[int], Optional[int], Optional[float]]:
|
||||||
|
"""Reads the ACTUAL encoded width/height/duration of a downloaded file via
|
||||||
|
ffprobe. Telegram was declared VK's original video dimensions (e.g. a 4K
|
||||||
|
source) while the file we actually send is capped to video_max_height (e.g.
|
||||||
|
720p) - that mismatch between declared and real dimensions is a known cause of
|
||||||
|
black-screen playback on strict mobile decoders, since some clients allocate
|
||||||
|
the render surface from the declared size before the real stream is probed."""
|
||||||
|
try:
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
"ffprobe", "-v", "error", "-select_streams", "v:0",
|
||||||
|
"-show_entries", "stream=width,height:format=duration",
|
||||||
|
"-of", "csv=p=0",
|
||||||
|
str(path),
|
||||||
|
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
out, _ = await proc.communicate()
|
||||||
|
except OSError:
|
||||||
|
return None, None, None
|
||||||
|
lines = [l for l in out.decode().strip().splitlines() if l]
|
||||||
|
width = height = None
|
||||||
|
duration = None
|
||||||
|
for line in lines:
|
||||||
|
parts = line.split(",")
|
||||||
|
if len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit():
|
||||||
|
width, height = int(parts[0]), int(parts[1])
|
||||||
|
elif len(parts) == 1:
|
||||||
|
try:
|
||||||
|
duration = float(parts[0])
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return width, height, duration
|
||||||
|
|
||||||
async def _download_video_ytdlp_attempt(
|
async def _download_video_ytdlp_attempt(
|
||||||
self, url: str, prefix: str = "video_"
|
self, url: str, prefix: str = "video_"
|
||||||
) -> tuple[Optional[Path], Optional[str], bool]:
|
) -> tuple[Optional[Path], Optional[str], bool]:
|
||||||
@@ -373,6 +405,7 @@ class MediaProcessor:
|
|||||||
|
|
||||||
p, err, perm = await self.download_video_ytdlp(url, prefix=f"v_{att_id}_")
|
p, err, perm = await self.download_video_ytdlp(url, prefix=f"v_{att_id}_")
|
||||||
if p:
|
if p:
|
||||||
|
real_width, real_height, real_duration = await self._probe_video_dims(p)
|
||||||
results.append(
|
results.append(
|
||||||
ProcessedMedia(
|
ProcessedMedia(
|
||||||
media_type="video",
|
media_type="video",
|
||||||
@@ -380,9 +413,9 @@ class MediaProcessor:
|
|||||||
original_url=url,
|
original_url=url,
|
||||||
attachment_id=att_id,
|
attachment_id=att_id,
|
||||||
size_bytes=p.stat().st_size,
|
size_bytes=p.stat().st_size,
|
||||||
duration_sec=item.duration_sec,
|
duration_sec=int(real_duration) if real_duration else item.duration_sec,
|
||||||
width=item.width,
|
width=real_width or item.width,
|
||||||
height=item.height,
|
height=real_height or item.height,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user