diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 0.10. 0
+
+* Fix encoder bug that created a single black frame at the start of
+  every video (Jonathan Daugherty)
+
 # 0.9.0
 
 * Add support for camera input (Thomas M. DuBuisson)
diff --git a/ffmpeg-light.cabal b/ffmpeg-light.cabal
--- a/ffmpeg-light.cabal
+++ b/ffmpeg-light.cabal
@@ -1,5 +1,5 @@
 name:                ffmpeg-light
-version:             0.9.0
+version:             0.10.0
 synopsis:            Minimal bindings to the FFmpeg library.
 
 description:         Stream frames from an encoded video, or stream frames to
@@ -15,7 +15,7 @@
                      > go = do (getFrame, cleanup) <- imageReader "myVideo.mov"
                      >         (fmap ImageRGB8 <$> getFrame) <* cleanup
                      .
-                     Tested on OS X 10.9.4 with <http://www.ffmpeg.org FFmpeg> 2.3
+                     Tested on OS X 10.11.2 with <http://www.ffmpeg.org FFmpeg> 2.8.3
                      installed via <http://brew.sh homebrew>.
 
 license:             BSD3
diff --git a/src/Codec/FFmpeg/Encode.hs b/src/Codec/FFmpeg/Encode.hs
--- a/src/Codec/FFmpeg/Encode.hs
+++ b/src/Codec/FFmpeg/Encode.hs
@@ -327,6 +327,11 @@
   avio_open_check oc fname
   write_header_check oc
 
+  -- Frame number ioref. We use this to determine whether we should
+  -- increment the frame PTS; we only want to do this for frames after
+  -- the first one since we want the first frame PTS to be zero.
+  frameNum <- newIORef (0::Int)
+
   let framePeriod = AVRational 1 (fromIntegral $ epFps ep)
 
   -- The stream time_base can be changed by the call to
@@ -365,13 +370,29 @@
 
       scaleToDst sws' img = void $ swsScale sws' img dstFrame
       fillDst = maybe copyDstData scaleToDst
+
+      -- | Gets the PTS to be used for the current frame by reading the
+      -- PTS from dstFrame. If the current frame is the first frame
+      -- (zero), the existing timestamp is left unmodified. Otherwise it
+      -- is incremented by frameTime.
+      --
+      -- This also increments the current frame number stored in the
+      -- frameNum IORef so the caller needn't worry about it.
+      getCurrentFrameTimestamp = do
+           curFrame <- readIORef frameNum
+           ts <- case curFrame == 0 of
+               True -> getPts dstFrame
+               False -> (+ frameTime) <$> getPts dstFrame
+           modifyIORef frameNum (+1)
+           return ts
+
       addRaw Nothing = return ()
       addRaw (Just (_, _, pixels)) =
         do resetPacket
            getPacketFlags pkt >>= setPacketFlags pkt . (.|. avPktFlagKey)
            --setSize pkt (fromIntegral $ V.length pixels)
            setSize pkt (fromIntegral pictureSize)
-           timeStamp <- (+ frameTime) <$> getPts dstFrame
+           timeStamp <- getCurrentFrameTimestamp
            setPts dstFrame timeStamp
            setPts pkt timeStamp
            -- getPts dstFrame >>= setDts pkt
@@ -394,7 +415,8 @@
                      writeIORef sPtr s'
                      return s'
            fillDst sws' (srcFmt, V2 srcW srcH, pixels')
-           getPts dstFrame >>= setPts dstFrame . (+ frameTime)
+           timeStamp <- getCurrentFrameTimestamp
+           setPts dstFrame timeStamp
            encode_video_check ctx pkt (Just dstFrame) >>= flip when writePacket
            -- Make sure the GC hasn't clobbered our palettized pixel data
            let (fp,_,_) = V.unsafeToForeignPtr pixels'
