From da87b6c0d8d8345a3a521374f290795aa36d26af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= <jeremy.laine@m4x.org>
Date: Sun, 14 Oct 2018 10:40:28 +0200
Subject: [PATCH] Add docstrings for Stream properties and methods.

For consistency with other APIs we make `start_time` return None if
unknown.
---
 av/container/streams.pyx   |  2 +-
 av/stream.pyx              | 77 +++++++++++++++++++++++++++++++++++---
 docs/api/stream.rst        |  6 +++
 tests/test_file_probing.py | 34 ++++++++++-------
 4 files changed, 98 insertions(+), 21 deletions(-)

diff --git a/av/container/streams.pyx b/av/container/streams.pyx
index dea0edc..8a7e225 100644
--- a/av/container/streams.pyx
+++ b/av/container/streams.pyx
@@ -12,7 +12,7 @@ Typed Collections
 ~~~~~~~~~~~~~~~~~
 
 These attributes are preferred for readability if you don't need the
-dynamic capabilities of :method:`.get`:
+dynamic capabilities of :meth:`.get`:
 
 .. attribute:: StreamContainer.video
 
diff --git a/av/stream.pyx b/av/stream.pyx
index 1b792a9..8e6102e 100644
--- a/av/stream.pyx
+++ b/av/stream.pyx
@@ -47,6 +47,9 @@ cdef Stream wrap_stream(Container container, lib.AVStream *c_stream):
 
 
 cdef class Stream(object):
+    """
+    A single stream of audio, video or subtitles within a :class:`Container`.
+    """
 
     def __cinit__(self, name):
         if name is _cinit_bypass_sentinel:
@@ -117,6 +120,10 @@ cdef class Stream(object):
             self._stream.time_base = self._codec_context.time_base
 
     def encode(self, frame=None):
+        """
+        Encode an :class:`.AudioFrame` or :class:`.VideoFrame` and return a list
+        of :class:`.Packet`.
+        """
         packets = self.codec_context.encode(frame)
         cdef Packet packet
         for packet in packets:
@@ -125,11 +132,14 @@ cdef class Stream(object):
         return packets
 
     def decode(self, packet=None):
+        """
+        Decode a :class:`.Packet` and return a list of :class:`.AudioFrame`
+        or :class:`.VideoFrame`.
+        """
         return self.codec_context.decode(packet)
 
     def seek(self, offset, whence='time', backward=True, any_frame=False):
-        """seek(offset, whence='time', backward=True, any_frame=False)
-
+        """
         .. seealso:: :meth:`.InputContainer.seek` for documentation on parameters.
             The only difference is that ``offset`` will be interpreted in
             :attr:`.Stream.time_base` when ``whence == 'time'``.
@@ -138,6 +148,11 @@ cdef class Stream(object):
         self._container.seek(self._stream.index, offset, whence, backward, any_frame)
 
     property id:
+        """
+        The format-specific ID of this stream.
+
+        :type: int
+        """
         def __get__(self):
             return self._stream.id
         def __set__(self, v):
@@ -147,6 +162,11 @@ cdef class Stream(object):
                 self._stream.id = v
 
     property profile:
+        """
+        The profile of this stream.
+
+        :type: str
+        """
         def __get__(self):
             if self._codec and lib.av_get_profile_name(self._codec, self._codec_context.profile):
                 return lib.av_get_profile_name(self._codec, self._codec_context.profile)
@@ -154,30 +174,75 @@ cdef class Stream(object):
                 return None
 
     property index:
+        """
+        The index of this stream in its :class:`.Container`.
+
+        :type: int
+        """
         def __get__(self): return self._stream.index
 
     property time_base:
+        """
+        The unit of time (in fractional seconds) in which timestamps are expressed.
+
+        :type: fractions.Fraction
+        """
         def __get__(self):
             return avrational_to_fraction(&self._stream.time_base)
         def __set__(self, value):
             to_avrational(value, &self._stream.time_base)
 
     property average_rate:
+        """
+        The average frame rate of this stream.
+
+        :type: fractions.Fraction
+        """
         def __get__(self):
             return avrational_to_fraction(&self._stream.avg_frame_rate)
 
     property start_time:
-        def __get__(self): return self._stream.start_time
+        """
+        The presentation timestamp in :attr:`time_base` units of the first
+        frame in this stream.
+
+        Returns `None` if it is not known.
+
+        :type: int
+        """
+        def __get__(self):
+            if self._stream.start_time != lib.AV_NOPTS_VALUE:
+                return self._stream.start_time
 
     property duration:
+        """
+        The duration of this stream in :attr:`time_base` units.
+
+        Returns `None` if it is not known.
+
+        :type: int
+        """
         def __get__(self):
-            if self._stream.duration == lib.AV_NOPTS_VALUE:
-                return None
-            return self._stream.duration
+            if self._stream.duration != lib.AV_NOPTS_VALUE:
+                return self._stream.duration
 
     property frames:
+        """
+        The number of frames this stream contains.
+
+        Returns `0` if it is not known.
+
+        :type: int
+        """
         def __get__(self): return self._stream.nb_frames
 
     property language:
+        """
+        The language of the stream.
+
+        Returns `None` if it is not known.
+
+        :type: str
+        """
         def __get__(self):
             return self.metadata.get('language')
diff --git a/docs/api/stream.rst b/docs/api/stream.rst
index 2155753..5833ee0 100644
--- a/docs/api/stream.rst
+++ b/docs/api/stream.rst
@@ -2,8 +2,14 @@
 Streams
 =======
 
+Stream collections
+------------------
+
 .. automodule:: av.container.streams
 
+Streams
+-------
+
 .. automodule:: av.stream
 
     .. autoclass:: Stream
diff --git a/tests/test_file_probing.py b/tests/test_file_probing.py
index d441143..589cac6 100644
--- a/tests/test_file_probing.py
+++ b/tests/test_file_probing.py
@@ -66,28 +66,34 @@ class TestVideoProbe(TestCase):
 
     def test_stream_probing(self):
         stream = self.file.streams[0]
+
+        # actual stream properties
+        self.assertEqual(stream.average_rate, Fraction(25, 1))
+        self.assertEqual(stream.duration, 145800)
+        self.assertEqual(stream.frames, 0)
+        self.assertEqual(stream.id, 4131)
         self.assertEqual(stream.index, 0)
-        self.assertEqual(stream.type, 'video')
-        self.assertEqual(stream.name, 'mpeg2video')
-        self.assertEqual(stream.long_name, 'MPEG-2 video')
+        self.assertEqual(stream.language, None)
+        self.assertEqual(stream.metadata, {})
         self.assertEqual(stream.profile, 'Simple')
+        self.assertEqual(stream.start_time, 2065806749)
+        self.assertEqual(stream.time_base, Fraction(1, 90000))
 
-        # Libav is able to return a bit-rate for this file, but ffmpeg doesn't,
-        # so have to rely on rc_max_rate.
-        try:
-            self.assertEqual(stream.bit_rate, None)
-            self.assertEqual(stream.max_bit_rate, 3364800)
-        except AssertionError:
-            self.assertEqual(stream.bit_rate, 3364800)
+        # codec properties
+        self.assertEqual(stream.long_name, 'MPEG-2 video')
+        self.assertEqual(stream.name, 'mpeg2video')
+        self.assertEqual(stream.type, 'video')
 
-        self.assertEqual(stream.sample_aspect_ratio, Fraction(16, 15))
+        # codec context properties
+        self.assertEqual(stream.bit_rate, 3364800)
         self.assertEqual(stream.display_aspect_ratio, Fraction(4, 3))
-        self.assertEqual(stream.gop_size, 12)
         self.assertEqual(stream.format.name, 'yuv420p')
         self.assertFalse(stream.has_b_frames)
-        self.assertEqual(stream.average_rate, Fraction(25, 1))
-        self.assertEqual(stream.width, 720)
+        self.assertEqual(stream.gop_size, 12)
         self.assertEqual(stream.height, 576)
+        self.assertEqual(stream.max_bit_rate, None)
+        self.assertEqual(stream.sample_aspect_ratio, Fraction(16, 15))
+        self.assertEqual(stream.width, 720)
 
         # For some reason, these behave differently on OS X (@mikeboers) and
         # Ubuntu (Travis). We think it is FFmpeg, but haven't been able to
-- 
GitLab