4

Situation
Currently I am working on an application for image-processing that uses ffmpeg-light to fetch all the frames of a given video-file so that the program afterwards can apply grayscaling, as well as edge detection alogrithms to each of the frames.

With the help of friendly stackoverflowers I was able to set up a method capable of converting several images into one video file using ffmpeg-lights' frameWriter function.

Problem
The application runs fine to the moment it hits the frameWriterfunction and I don't really know why as there are no errors or exception-messages thrown. (OS: Win 10 64bit)

What did I try?
I tried..
- different versions of ffmpeg (from 3.2 to 3.4).
- ffmpeg.exe using the command line to test if there are any codecs missing, but any conversion I tried worked.
- different EncodingParams-combinations: like.. EncodingParams width height fps (Nothing) (Nothing) "medium"

Question
Unfortunately, none of above worked and the web lacks on information to that specific case. Maybe I missed something essential (like ghc flags or something) or made a bigger mistake within my code. That is why I have to ask you: Do you have any suggestions/advice for me?

Haskell Packages
- ffmpeg-light-0.12.0
- JuicyPixels-3.2.8.3

Code

{--------------------------------------------------------------------------------------------
 Applies "juicyToFFmpeg'" and "getFPS" to a list of images and saves the output-video
 to a user defined location.
---------------------------------------------------------------------------------------------}     
saveVideo :: String -> [Image PixelYA8] -> Int -> IO ()
saveVideo path imgs fps = do
         -- program stops after hitting next line --
         frame <- frameWriter ep path
         ------------------------------------------------
         Prelude.mapM_ (frame . Just) ffmpegImgs 
         frame Nothing
         where ep = EncodingParams width height fps (Just avCodecIdMpeg4) (Just avPixFmtGray8a) "medium"
               width      = toCInt $ imageWidth  $ head imgs
               height     = toCInt $ imageHeight $ head imgs
               ffmpegImgs = juicyToFFmpeg' imgs
               toCInt x   = fromIntegral x :: CInt

{--------------------------------------------------------------------------------------------
 Converts a single image from JuicyPixel-format to ffmpeg-light-format. 
---------------------------------------------------------------------------------------------}      
juicyToFFmpeg :: Image PixelYA8 -> (AVPixelFormat, V2 CInt, Vector CUChar)
juicyToFFmpeg img = (avPixFmtGray8a, V2 (toCInt width) (toCInt height), ffmpegData)
                  where toCInt   x   = fromIntegral x :: CInt
                        toCUChar x   = fromIntegral x :: CUChar
                        width        = imageWidth img
                        height       = imageHeight img
                        ffmpegData   = VS.map toCUChar (imageData img)

{--------------------------------------------------------------------------------------------
 Converts a list of images from JuicyPixel-format to ffmpeg-light-format. 
---------------------------------------------------------------------------------------------}                        
juicyToFFmpeg' :: [Image PixelYA8] -> [(AVPixelFormat, V2 CInt, Vector CUChar)]
juicyToFFmpeg' imgs = Prelude.foldr (\i acc -> acc++[juicyToFFmpeg i]) [] imgs

{--------------------------------------------------------------------------------------------
 Simply calculates the FPS for image-to-video conversion.
 -> frame :: (Double, DynamicImage) where Double is a timestamp of when it got extracted 
---------------------------------------------------------------------------------------------}
getFPS :: [(Double, DynamicImage)] -> Int
getFPS frames = div (ceiling $ lastTimestamp - firstTimestamp) frameCount :: Int
              where firstTimestamp = fst $ head frames
                    lastTimestamp  = fst $ last frames
                    frameCount     = length frames
oRole
  • 1,316
  • 1
  • 8
  • 24
  • Is there a reason that the `imageWriter` function from your last question isn't used here? I'm certain that it uses `frameWriter` under the hood, so it probably doesn't affect the question, but using it would mean you don't need to implement conversion yourself. – HTNW Oct 26 '17 at 00:34
  • 1
    @HTNW As I'm pretty new to Haskell, I saw implementing it by myself as a way to improve my skills. Of course I tried your solution too and it helped me a lot to understand how things work in this case but I thought it would probably be easier for the community to understand what I'm trying to do if I split it up a bit. – oRole Oct 26 '17 at 06:36

1 Answers1

2

I suspect issue you are having has something to do with Windows environment and usage of ffmpeg from Haskell (i.e. ffmpeg-simple)

I was able to successfully compile and run your module on Ubuntu 16.04, although I did get a runtime error from ffmpeg:

$ ./main
[NULL @ 0x1ea6900] Unable to find a suitable output format for 'foo.avi'
main: Couldn't allocate output format context
CallStack (from HasCallStack):
  error, called at src/Codec/FFmpeg/Encode.hs:214:17 in ffmpeg-light-
0.12.0-DYHyy7pUAhZ7WHcd6Y2mLO:Codec.FFmpeg.Encode

It seems like the above error can be fixed with some ffmpeg arguments tweaking, but since that is not the issue you are experiencing I decided not to go any further with debugging it.

Just in case my main:

main :: IO ()
main = do
  Right (ImageYA8 img) <- readPng "foo_ya.png"
  saveVideo "foo.avi" (replicate 10 img) 10

I ran the same thing on Windows 7 64-bit and it seems I was unable to fully satisfy the dependencies.

Compilation and dependency installation done on Windows:

> stack exec -- pacman -Syu
> stack exec -- pacman -S mingw-w64-x86_64-gtk3
> stack exec -- pacman -S mingw-w64-x86_64-pkg-config
> stack exec -- pacman -S mingw-w64-x86_64-ffmpeg
> stack --install-ghc --resolver lts-9.10 exec --package vector --package JuicyPixels --package ffmpeg-light -- ghc main.hs -O2 -threaded
> stack exec -- main.exe

Results in a popup error when ran in cmd (ps simply exits):

The procedure entry point inflateValidate could not be located in the dynamic link library zlib1.dll

I am no expert on development on Windows, so I feel like I am missing something. Hope my attempt will be at least a little bit helpful.

lehins
  • 9,642
  • 2
  • 35
  • 49
  • I faced the same error on Windows 10 but downloading a version of zlib1.dll that contains the method inflateValidate and replacing the old one solved it. At least I know now that it runs on Ubuntu if everything else fails. Thanks for your efforts! – oRole Oct 26 '17 at 10:04