From 504f4e9408d6db6c4a20e58c2877e902d836968c Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Sun, 23 Feb 2020 15:41:03 +0100
Subject: [PATCH] Improve safety yet again

---
 src/ffmpeg_api/api.rs   | 101 ++++++++++++++++++++++++++++++++++++++++
 src/ffmpeg_api/enums.rs |  40 +++++++++++-----
 src/main.rs             |  73 ++++++++++-------------------
 3 files changed, 155 insertions(+), 59 deletions(-)

diff --git a/src/ffmpeg_api/api.rs b/src/ffmpeg_api/api.rs
index 2598ba0..52ccc88 100644
--- a/src/ffmpeg_api/api.rs
+++ b/src/ffmpeg_api/api.rs
@@ -103,6 +103,49 @@ impl AVBuffer {
     }
 }
 
+pub struct AVPacket {
+    base: *mut ffi::AVPacket,
+}
+
+impl AVPacket {
+    pub fn new() -> Result<Self, failure::Error> {
+        let base = unsafe { ffi::av_packet_alloc() };
+        if base.is_null() {
+            bail!("av_packet_alloc() failed");
+        }
+        Ok(AVPacket { base })
+    }
+
+    // TODO: Just for testing
+    pub unsafe fn as_mut(&mut self) -> &mut ffi::AVPacket {
+        self.base.as_mut().expect("not null")
+    }
+
+    pub fn pts(&self) -> i64 {
+        let base = unsafe { self.base.as_ref() }.expect("not null");
+
+        base.pts
+    }
+
+    pub fn dts(&self) -> i64 {
+        let base = unsafe { self.base.as_ref() }.expect("not null");
+
+        base.dts
+    }
+
+    pub fn stream_index(&self) -> i32 {
+        let base = unsafe { self.base.as_ref() }.expect("not null");
+
+        base.stream_index
+    }
+}
+
+impl Drop for AVPacket {
+    fn drop(&mut self) {
+        unsafe { ffi::av_packet_free(&mut self.base) }
+    }
+}
+
 pub struct AVFrame {
     base: *mut ffi::AVFrame,
     buffer: AVBuffer,
@@ -414,4 +457,62 @@ impl Drop for AVCodecContext {
     fn drop(&mut self) {
         unsafe { ffi::avcodec_free_context(&mut self.base) }
     }
+}
+
+pub struct SwsContext {
+    base: *mut ffi::SwsContext,
+}
+
+impl SwsContext {
+    pub fn new() -> Self {
+        SwsContext { base: std::ptr::null_mut() }
+    }
+
+    pub fn reinit(&mut self, source: &AVFrame, target: &AVFrame, scaler: SwsScaler) -> Result<(), failure::Error> {
+        let base = unsafe {
+            ffi::sws_getCachedContext(
+                self.base,
+                source.width(),
+                source.height(),
+                source.format() as ffi::AVPixelFormat,
+                target.width(),
+                target.height(),
+                target.format() as ffi::AVPixelFormat,
+                scaler as i32,
+                std::ptr::null_mut(),
+                std::ptr::null_mut(),
+                std::ptr::null(),
+            )
+        };
+        if base.is_null() {
+            bail!("sws_getCachedContext() failed");
+        }
+        self.base = base;
+
+        Ok(())
+    }
+
+    pub fn scale(&self, source: &AVFrame, target: &mut AVFrame) -> i32 {
+        self.scale_slice(source, target, 0, source.height())
+    }
+
+    pub fn scale_slice(&self, source: &AVFrame, target: &mut AVFrame, slice_from: i32, slice_to: i32) -> i32 {
+        unsafe {
+            ffi::sws_scale(
+                self.base,
+                source.data_ptr(),
+                source.linesize().as_ptr(),
+                slice_from,
+                slice_to,
+                target.data_mut_ptr(),
+                target.linesize().as_ptr(),
+            )
+        }
+    }
+}
+
+impl Drop for SwsContext {
+    fn drop(&mut self) {
+        unsafe { ffi::sws_freeContext(self.base) }
+    }
 }
\ No newline at end of file
diff --git a/src/ffmpeg_api/enums.rs b/src/ffmpeg_api/enums.rs
index 85de4af..ad915a0 100644
--- a/src/ffmpeg_api/enums.rs
+++ b/src/ffmpeg_api/enums.rs
@@ -913,22 +913,40 @@ enum_from_primitive! {
 }
 
 enum_from_primitive! {
-    # [derive(Debug, Copy, Clone, PartialEq)]
-    # [repr(i32)]
+    #[derive(Debug, Copy, Clone, PartialEq)]
+    #[repr(i32)]
     pub enum AVDiscard {
-        # [doc = "< discard nothing"]
+        #[doc = "< discard nothing"]
         None = ffi::AVDiscard_AVDISCARD_NONE,
-        # [doc = "< discard useless packets like 0 size packets in avi"]
-        Default =ffi::AVDiscard_AVDISCARD_DEFAULT,
-        # [doc = "< discard all non reference"]
+        #[doc = "< discard useless packets like 0 size packets in avi"]
+        Default = ffi::AVDiscard_AVDISCARD_DEFAULT,
+        #[doc = "< discard all non reference"]
         NonReference = ffi::AVDiscard_AVDISCARD_NONREF,
-        # [doc = "< discard all bidirectional frames"]
+        #[doc = "< discard all bidirectional frames"]
         BiDirectional = ffi::AVDiscard_AVDISCARD_BIDIR,
-        # [doc = "< discard all non intra frames"]
+        #[doc = "< discard all non intra frames"]
         NonIntra = ffi::AVDiscard_AVDISCARD_NONINTRA,
-        # [doc = "< discard all frames except keyframes"]
+        #[doc = "< discard all frames except keyframes"]
         NonKey = ffi::AVDiscard_AVDISCARD_NONKEY,
-        # [doc = "< discard all"]
-        All =ffi::AVDiscard_AVDISCARD_ALL
+        #[doc = "< discard all"]
+        All = ffi::AVDiscard_AVDISCARD_ALL,
+    }
+}
+
+enum_from_primitive! {
+    #[derive(Debug, Copy, Clone, PartialEq)]
+    #[repr(u32)]
+    pub enum SwsScaler {
+        FastBilinear = ffi::SWS_FAST_BILINEAR,
+        Bilinear = ffi::SWS_BILINEAR,
+        Bicubic = ffi::SWS_BICUBIC,
+        X = ffi::SWS_X,
+        Point = ffi::SWS_POINT,
+        Area = ffi::SWS_AREA,
+        Bicublin = ffi::SWS_BICUBLIN,
+        Gauss = ffi::SWS_GAUSS,
+        Sinc = ffi::SWS_SINC,
+        Lanczos = ffi::SWS_LANCZOS,
+        Spline = ffi::SWS_SPLINE,
     }
 }
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 707ae42..3dcc9b5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -48,11 +48,9 @@ fn main() -> Result<(), std::io::Error> {
             codec_context.set_skip_idct(AVDiscard::NonKey);
             codec_context.set_skip_frame(AVDiscard::NonKey);
 
-            // TODO: HERE BE DRAGONS
-            let packet: &mut ffi::AVPacket = unsafe {
-                ffi::av_packet_alloc().as_mut()
-            }.expect("not null");
-            // TODO: END DRAGONS
+            let mut packet = AVPacket::new().unwrap_or_else(|error| {
+                panic!("Could not init temporary packet: {:?}", error)
+            });
 
             let mut frame = AVFrame::new().unwrap_or_else(|error| {
                 panic!("Could not create input frame: {:?}", error)
@@ -63,10 +61,16 @@ fn main() -> Result<(), std::io::Error> {
             println!("Time: {:#?}", before.elapsed().unwrap());
             before = std::time::SystemTime::now();
 
+            let mut scale_context = SwsContext::new();
+
             //TODO: HERE BE DRAGONS
-            while unsafe { ffi::av_read_frame(avformat_context.raw(), packet) } >= 0 && i < 10 {
-                if packet.stream_index == stream.index() {
-                    unsafe { ffi::avcodec_send_packet(codec_context.raw(), packet) };
+            while unsafe { ffi::av_read_frame(avformat_context.raw(), packet.as_mut()) } >= 0 && i < 16 {
+                // TODO: END DRAGONS
+
+                if packet.stream_index() == stream.index() {
+
+                    //TODO: HERE BE DRAGONS
+                    unsafe { ffi::avcodec_send_packet(codec_context.raw(), packet.as_mut()) };
                     while unsafe { ffi::avcodec_receive_frame(codec_context.raw(), frame.as_mut()) } >= 0 {
                         // TODO: END DRAGONS
 
@@ -79,53 +83,26 @@ fn main() -> Result<(), std::io::Error> {
                         println!("Reading Time: {:#?}", before.elapsed().unwrap());
                         before = std::time::SystemTime::now();
 
-                        // TODO: HERE BE DRAGONS
-                        let sws_context = unsafe {
-                            ffi::sws_getContext(
-                                frame.width(),
-                                frame.height(),
-                                frame.format() as ffi::AVPixelFormat,
-                                output_frame.width(),
-                                output_frame.height(),
-                                output_frame.format() as ffi::AVPixelFormat,
-                                ffi::SWS_FAST_BILINEAR as i32,
-                                std::ptr::null_mut(),
-                                std::ptr::null_mut(),
-                                std::ptr::null(),
-                            ).as_mut()
-                        }.expect("not null");
-
-                        let success = unsafe {
-                            ffi::sws_scale(
-                                sws_context,
-                                frame.data_ptr(),
-                                frame.linesize().as_ptr(),
-                                0,
-                                frame.height(),
-                                output_frame.data_mut_ptr(),
-                                output_frame.linesize().as_ptr(),
-                            )
-                        };
-                        // TODO: END DRAGONS
+                        scale_context.reinit(&frame, &output_frame, SwsScaler::FastBilinear).unwrap_or_else(|error| {
+                            panic!("Could not reinit scale context: {:?}", error)
+                        });
+                        scale_context.scale(&frame, &mut output_frame);
 
-                        println!("success: {}", success);
                         println!("Processing Time: {:#?}", before.elapsed().unwrap());
                         before = std::time::SystemTime::now();
 
-                        if success > 0 {
-                            image::save_buffer(
-                                format!("/home/janne/Workspace/justflix/data/test/image_{}.png", i),
-                                output_frame.data(0),
-                                output_frame.width() as u32,
-                                output_frame.height() as u32,
-                                image::ColorType::Rgb8,
-                            ).unwrap();
-
-                            i += 1;
-                        }
+                        image::save_buffer(
+                            format!("/home/janne/Workspace/justflix/data/test/image_{}.png", i),
+                            output_frame.data(0),
+                            output_frame.width() as u32,
+                            output_frame.height() as u32,
+                            image::ColorType::Rgb8,
+                        ).unwrap();
 
                         println!("Writing Time: {:#?}", before.elapsed().unwrap());
                         before = std::time::SystemTime::now();
+
+                        i += 1;
                     }
                 }
             }
-- 
GitLab