From 70249406929153769224a72876df844cecf9dd3b Mon Sep 17 00:00:00 2001
From: Sebastian <sebastian@baldr.asgard>
Date: Wed, 23 Oct 2019 21:55:27 +0300
Subject: [PATCH] more libde265 API, custom exceptions

---
 python/ftl/libde265.py | 113 +++++++++++++++++++++++++----------------
 1 file changed, 70 insertions(+), 43 deletions(-)

diff --git a/python/ftl/libde265.py b/python/ftl/libde265.py
index 3a4c7b6c4..d35a35842 100644
--- a/python/ftl/libde265.py
+++ b/python/ftl/libde265.py
@@ -115,6 +115,9 @@ libde265.de265_decode.restype = ctypes.c_int
 libde265.de265_get_next_picture.argtypes = [ctypes.c_void_p]
 libde265.de265_get_next_picture.restype = ctypes.c_void_p
 
+libde265.de265_peek_next_picture.argtypes = [ctypes.c_void_p]
+libde265.de265_peek_next_picture.restype = ctypes.c_void_p
+
 libde265.de265_release_next_picture.argtypes = [ctypes.c_void_p]
 libde265.de265_release_next_picture.restype = None
 
@@ -135,6 +138,14 @@ libde265.de265_get_number_of_input_bytes_pending.restype = ctypes.c_int
 
 import time
 
+class libde265Error(Exception):
+    def __init__(self, code):
+        super(libde265Error, self).__init__(
+            libde265.de265_get_error_text(code).decode("ascii"))
+
+class WaitingForInput(libde265Error):
+    pass
+
 class Decoder:
     def __init__(self, size, threads=_threads):
         self._size = size
@@ -143,14 +154,50 @@ class Decoder:
         self._ctx = libde265.de265_new_decoder()
         err = libde265.de265_start_worker_threads(self._ctx, threads)
         if err:
-            raise Exception(self.get_error_str(err))
+            raise libde265Error(err)
         
     def __del__(self):
         libde265.de265_free_decoder(self._ctx)
+
+    def _copy_image(self, de265_image):
+        res = np.zeros((self._size[0], self._size[1], 3), dtype=np.uint8)
+        
+        for c in range(0, 3):
+            size = (libde265.de265_get_image_height(de265_image, c),
+                    libde265.de265_get_image_width(de265_image, c))
+            
+            bpp = libde265.de265_get_bits_per_pixel(de265_image, c)
+
+            if bpp != 8:
+                raise NotImplementedError("unsupported bits per pixel %i" % bpp)
+            
+            img_ptr = libde265.de265_get_image_plane(de265_image, c, self._out_stride)
+            
+            ch = np.frombuffer(img_ptr[:size[0] * size[1]], dtype=np.uint8)
+            ch.shape = size
+            
+            res[:,:,c] = _resize(ch, self._size)
+        
+        return res
+
+    def decode(self):
+        err = libde265.de265_decode(self._ctx, self._more)
+        
+        # should use custom
+        if err:
+            if err == libde265error.DE265_ERROR_WAITING_FOR_INPUT_DATA:
+                raise WaitingForInput(err)
+
+            raise libde265Error(err)
+        
+        return self._more.value != 0
     
-    def get_error_str(self, code):
-        return libde265.de265_get_error_text(code).decode("ascii")
-    
+    def flush_data(self):
+        err = libde265.de265_flush_data(self._ctx)
+        
+        if err:
+            raise libde265Error(err)
+
     def push_data(self, data):
         if not isinstance(data, bytes):
             raise ValueError("expected bytes")
@@ -158,13 +205,13 @@ class Decoder:
         err = libde265.de265_push_data(self._ctx, data, len(data), None, None)
         
         if err:
-            raise Exception(self.get_error_str(err))
+            raise libde265Error(err)
             
     def push_end_of_frame(self):
         err = libde265.de265_push_end_of_frame(self._ctx)
         
         if err:
-            raise Exception(self.get_error_str(err))
+            raise libde265Error(err)
             
     def push_NAL(self, data):
         if not isinstance(data, bytes):
@@ -173,54 +220,34 @@ class Decoder:
         err = libde265.de265_push_NAL(self._ctx, data, len(data), None, None)
         
         if err:
-            raise Exception(self.get_error_str(err))
-            
-    def decode(self):
-        err = libde265.de265_decode(self._ctx, self._more)
-        
-        # if err and err != libde265error.DE265_ERROR_WAITING_FOR_INPUT_DATA:
-        # Does this happen? Should use custom exception.
+            raise libde265Error(err)
 
-        if err:
-            raise Exception(self.get_error_str(err))
-        
-        return self._more.value != 0
-    
-    def flush_data(self):
-        err = libde265.de265_flush_data(self._ctx)
-        
-        if err:
-            raise Exception(self.get_error_str(err))
-        
     def get_next_picture(self):
         '''
         Returns next decoded frame. Image in YCbCr format. If no frame available
         returns None.
         '''
 
-        img = libde265.de265_get_next_picture(self._ctx)
+        de265_image = libde265.de265_get_next_picture(self._ctx)
         
-        if not img:
+        if not de265_image:
             return None
         
-        res = np.zeros((self._size[0], self._size[1], 3), dtype=np.uint8)
-        
-        for c in range(0, 3):
-            size = (libde265.de265_get_image_height(img, c),
-                    libde265.de265_get_image_width(img, c))
-            
-            bpp = libde265.de265_get_bits_per_pixel(img, c)
+        res = self._copy_image(de265_image)
+        libde265.de265_release_next_picture(self._ctx)
 
-            if bpp != 8:
-                raise NotImplementedError("unsupported bits per pixel %i" % bpp)
-            
-            img_ptr = libde265.de265_get_image_plane(img, c, self._out_stride)
-            
-            ch = np.frombuffer(img_ptr[:size[0] * size[1]], dtype=np.uint8)
-            ch.shape = size
-            
-            res[:,:,c] = _resize(ch, self._size)
+        return res
+
+    def get_number_of_input_bytes_pending(self):
+        return libde265.de265_get_number_of_input_bytes_pending(self._ctx)
 
+    def peek_next_picture(self):
+        de265_image = libde265.de265_peek_next_picture(self._ctx)
+        
+        if not de265_image:
+            return None
+        
+        res = self._copy_image(de265_image)
         libde265.de265_release_next_picture(self._ctx)
 
-        return res
\ No newline at end of file
+        return res
-- 
GitLab