Repository: just-work/fffw

Issue:
Wrong MKV duration
In MKV container duration field contains float milliseconds value and in MP4 container it's int.
TS implementation parses int as milliseconds and float as seconds (because of ffmpeg duration meanings).

media info normalizer should perform duration rounding.

Discussion:

Patch:
diff --git a/fffw/graph/meta.py b/fffw/graph/meta.py
index 5ed0f15..328d1f0 100644
--- a/fffw/graph/meta.py
+++ b/fffw/graph/meta.py
@@ -381,9 +381,28 @@ class AudioMeta(Meta):
         assert abs(self.samples - interval * self.sampling_rate) <= 1
 
 
+def maybe_parse_duration(value: Union[str, float, int, None]) -> TS:
+    """
+    Parses duration from mediainfo output, if necessary.
+
+    Some containers store float value and some int, but in both cases (which is different from ffmpeg) value is
+    counted in milliseconds.
+    """
+    if value is None:
+        return TS(0)
+    if isinstance(value, str):
+        try:
+            value = int(value)
+        except ValueError:
+            # prepare seconds for TS constructor
+            value = float(value) / 1000
+    return TS(value)
+
+
 def audio_meta_data(**kwargs: Any) -> AudioMeta:
+    duration = maybe_parse_duration(kwargs.get('duration'))
+
     stream = kwargs.get('stream')
-    duration = TS(kwargs.get('duration', 0))
     start = TS(kwargs.get('start', 0))
     scene = Scene(
         stream=stream,
@@ -405,7 +424,7 @@ def audio_meta_data(**kwargs: Any) -> AudioMeta:
 
 
 def video_meta_data(**kwargs: Any) -> VideoMeta:
-    duration = TS(kwargs.get('duration', 0))
+    duration = maybe_parse_duration(kwargs.get('duration'))
     width = int(kwargs.get('width', 0))
     height = int(kwargs.get('height', 0))
     par = float(kwargs.get('pixel_aspect_ratio', 1.0))


Test patch:
diff --git a/tests/test_meta_data.py b/tests/test_meta_data.py
index d8fed7f..6ad882b 100644
--- a/tests/test_meta_data.py
+++ b/tests/test_meta_data.py
@@ -2,9 +2,10 @@ import json
 from copy import deepcopy
 from dataclasses import dataclass, fields
 from datetime import timedelta
+from itertools import product
 from typing import Iterable, Tuple, Any
 from unittest import TestCase
-from itertools import product
+
 from pymediainfo import MediaInfo  # type: ignore
 
 from fffw.graph import meta
@@ -268,6 +269,17 @@ class MetaDataTestCase(TestCase):
 
         self.assertTrue(fields(ExtendedVideoMeta))
 
+    def test_mkv_stream_duration(self):
+        """ MKV duration is stored as float and this is a problem for TS constuctor."""
+        original = meta.from_media_info(self.media_info)
+        s = SAMPLE
+        s = s.replace('<Duration>6742</Duration>', '<Duration>6742.000000</Duration>')
+        s = s.replace('<Duration>6740</Duration>', '<Duration>6740.000000</Duration>')
+        streams = meta.from_media_info(MediaInfo(s))
+        self.assertEqual(len(original), len(streams))
+        for s, o in zip(streams, original):
+            self.assertEqual(s.duration, o.duration)
+
 
 class TimeStampTestCase(TestCase):
     td: timedelta