diff --git a/.clang-format b/.clang-format
deleted file mode 100644
index cd04efc..0000000
--- a/.clang-format
+++ /dev/null
@@ -1,2 +0,0 @@
-BasedOnStyle: Webkit
-ColumnLimit: 80
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 6620857..0000000
--- a/.dockerignore
+++ /dev/null
@@ -1,7 +0,0 @@
-.git
-.vscode
-testdata/*thumb.*
-.directory
-debug.test
-thumbnailer.test
-test.exe
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index fc762c9..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-services: docker
-script:
-  - docker build -t thumbnailer_test .
-  - docker run --rm thumbnailer_test go test --race
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 52f8059..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,27 +0,0 @@
-FROM ubuntu:focal
-
-WORKDIR /app
-
-ENV DEBIAN_FRONTEND=noninteractive
-RUN apt-get update
-RUN apt-get dist-upgrade -y
-RUN apt-get install -y \
-	build-essential \
-	pkg-config \
-	curl \
-	libavcodec-dev \
-	libavutil-dev \
-	libavformat-dev \
-	libswscale-dev
-
-# Install Go
-RUN GO_VERSION=$(curl -sSL "https://go.dev/VERSION?m=text" | awk 'NR==1{print $1}') && \
-    curl -sSL "https://dl.google.com/go/${GO_VERSION}.linux-amd64.tar.gz" | tar xpz -C /usr/local
-ENV PATH=$PATH:/usr/local/go/bin
-
-# Try to cache deps
-COPY go.mod .
-COPY go.sum .
-RUN go mod download
-
-COPY . .
diff --git a/Makefile b/Makefile
index 23b6e09..2d870f9 100644
--- a/Makefile
+++ b/Makefile
@@ -30,12 +30,5 @@ cross_tests_windows:
 test:
 	go test --race
 
-test_docker:
-	docker build -t thumbnailer_test .
-	docker run \
-		--mount type=bind,source="$(PWD)"/testdata,target=/app/testdata \
-		--rm thumbnailer_test \
-		make clean test testdata_restore_permissions
-
 testdata_restore_permissions:
 	chown $(shell stat -c "%u:%g" testdata/alpha.webm) testdata/*
diff --git a/README.md b/README.md
index bdb4a40..aaab76e 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,25 @@
+[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/license/mit)
 [![GoDoc](https://godoc.org/github.com/bakape/thumbnailer?status.svg)](https://godoc.org/github.com/bakape/thumbnailer)
-[![Build Status](https://travis-ci.com/bakape/thumbnailer.svg?branch=master)](https://travis-ci.com/bakape/thumbnailer)
+
 # thumbnailer
 Package thumbnailer provides a more efficient media thumbnailer than available
 with native Go processing libraries through ffmpeg bindings.
 
-Use 
 ```
 go get -u github.com/bakape/thumbnailer/v2
 ```
-to install the library in your project. 
 
-For a comprehensive list of file formats supported by default see
-main.go:Process().
+For a comprehensive list of file formats supported by default see main.go:Process().
 
 ## Dependencies
-* Go >= 1.10
+* Go >=1.13.0
 * C11 compiler
 * make
 * pkg-config
 * pthread
-* ffmpeg >= 4.1 libraries (libavcodec, libavutil, libavformat, libswscale)
+* ffmpeg >=6.1 libraries (libavcodec, libavutil, libavformat, libswscale)
 
 NB:
 * ffmpeg should be compiled with all the dependency libraries for formats you
 want to process. On most Linux distributions you should be fine with
 the packages in the stock repositories.
-* Ubuntu patches to ffmpeg on some Ubuntu versions <19.10 break this library.
-In this case, please compile from unmodified ffmpeg sources using:
-
-```
-sudo apt build-dep ffmpeg
-git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg
-cd ffmpeg
-git checkout n4.1
-./configure
-make -j`nproc`
-sudo make install
-```
diff --git a/cover_art.go b/cover_art.go
index 80368fb..e1345d8 100644
--- a/cover_art.go
+++ b/cover_art.go
@@ -5,7 +5,6 @@ import "C"
 import (
 	"bytes"
 	"image"
-	"unsafe"
 )
 
 // HasCoverArt return whether file has cover art in it
@@ -15,15 +14,19 @@ func (c *FFContext) HasCoverArt() bool {
 
 // CoverArt extracts any attached image
 func (c *FFContext) CoverArt() []byte {
+	// retrieve_cover_art returns an AVPacket struct
 	img := C.retrieve_cover_art(c.avFormatCtx)
 	if img.size <= 0 || img.data == nil {
 		return nil
 	}
-	defer C.free(unsafe.Pointer(img.data))
-	return copyCBuffer(C.struct_Buffer{
+
+	buf := copyCBuffer(C.struct_Buffer{
 		data: img.data,
 		size: C.size_t(img.size),
 	})
+	// img.data is freed later when the context is closed in ffmpeg.go
+
+	return buf
 }
 
 func processCoverArt(buf []byte, opts Options) (thumb image.Image, err error) {
diff --git a/ffmpeg.go b/ffmpeg.go
index f001536..d4891e9 100644
--- a/ffmpeg.go
+++ b/ffmpeg.go
@@ -106,8 +106,7 @@ func NewFFContext(rs io.ReadSeeker) (*FFContext, error) {
 
 // Like NewFFContext, but optionally specifies the passed input format explicitly.
 // inputFormat can be NULL.
-func newFFContextWithFormat(rs io.ReadSeeker, inputFormat *C.char,
-) (*FFContext, error) {
+func newFFContextWithFormat(rs io.ReadSeeker, inputFormat *C.char) (*FFContext, error) {
 	ctx := C.avformat_alloc_context()
 	this := &FFContext{
 		avFormatCtx: ctx,
@@ -130,8 +129,7 @@ func newFFContextWithFormat(rs io.ReadSeeker, inputFormat *C.char,
 	return this, nil
 }
 
-// Close closes and frees memory allocated for c. c should not be used after
-// this point.
+// Close closes and frees memory allocated for c. c should not be used after this point
 func (c *FFContext) Close() {
 	for _, ci := range c.codecs {
 		C.avcodec_free_context(&ci.ctx)
@@ -140,7 +138,8 @@ func (c *FFContext) Close() {
 		C.av_free(unsafe.Pointer(c.avFormatCtx.pb.buffer))
 		c.avFormatCtx.pb.buffer = nil
 		C.av_free(unsafe.Pointer(c.avFormatCtx.pb))
-		C.av_free(unsafe.Pointer(c.avFormatCtx))
+		// calls avformat_free_context internally
+		C.avformat_close_input(&c.avFormatCtx)
 	}
 	handlersMap.Delete(c.handlerKey)
 }
@@ -231,11 +230,7 @@ func readCallBack(opaque unsafe.Pointer, buf *C.uint8_t, bufSize C.int) C.int {
 }
 
 //export seekCallBack
-func seekCallBack(
-	opaque unsafe.Pointer,
-	offset C.int64_t,
-	whence C.int,
-) C.int64_t {
+func seekCallBack(opaque unsafe.Pointer, offset C.int64_t, whence C.int) C.int64_t {
 	n, err := handlersMap.Get(opaque).Seek(int64(offset), int(whence))
 	if err != nil {
 		return C.int64_t(castIOError(err))
diff --git a/main_test.go b/main_test.go
index 9930a89..44c3df4 100644
--- a/main_test.go
+++ b/main_test.go
@@ -51,6 +51,7 @@ var samples = [...]string{
 	"too small.png",
 	"exact_thumb_size.jpg",
 	"meta_segfault.mp4",
+	"gary.jpg", // 140x140 px image that manages to crash ffmpeg
 
 	// Exif rotation compensation
 	"jannu_baseline.jpg",
diff --git a/meta.go b/meta.go
index 6f82b51..4e798b3 100644
--- a/meta.go
+++ b/meta.go
@@ -2,6 +2,19 @@ package thumbnailer
 
 // #include "meta.h"
 import "C"
+import (
+	"strings"
+	"unicode/utf8"
+)
+
+// Convert string to valid UTF-8.
+// Strings passed from C are not guaranteed to be valid.
+func sanitize(s *string) {
+	if !utf8.ValidString(*s) {
+		// Need to replace invalid UTF-8 with a valid UTF-8 marker
+		*s = strings.ToValidUTF8(*s, "?")
+	}
+}
 
 // Meta retrieves title and artist for source, if present
 func (c *FFContext) Meta() (m Meta) {
diff --git a/mimes.go b/mimes.go
index c0c6096..6b62f98 100644
--- a/mimes.go
+++ b/mimes.go
@@ -173,6 +173,8 @@ func matchMP4(data []byte) (string, string) {
 			continue
 		}
 		if bytes.Equal(data[st:st+3], []byte("mp4")) ||
+			bytes.Equal(data[st:st+3], []byte("iso")) ||
+			bytes.Equal(data[st:st+4], []byte("qt  ")) ||
 			bytes.Equal(data[st:st+4], []byte("dash")) {
 			return "video/mp4", "mp4"
 		}
diff --git a/sanitize_go_1_13.go b/sanitize_go_1_13.go
deleted file mode 100644
index b190e17..0000000
--- a/sanitize_go_1_13.go
+++ /dev/null
@@ -1,17 +0,0 @@
-// +build go1.13
-
-package thumbnailer
-
-import (
-	"strings"
-	"unicode/utf8"
-)
-
-// Convert string to valid UTF-8.
-// Strings passed from C are not guaranteed to be valid.
-func sanitize(s *string) {
-	if !utf8.ValidString(*s) {
-		// Need to replace invalid UTF-8 with a valid UTF-8 marker
-		*s = strings.ToValidUTF8(*s, "?")
-	}
-}
diff --git a/sanitize_legacy.go b/sanitize_legacy.go
deleted file mode 100644
index ca23c6e..0000000
--- a/sanitize_legacy.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// +build !go1.13
-
-package thumbnailer
-
-import (
-	"strings"
-	"unicode/utf8"
-)
-
-// Convert string to valid UTF-8.
-// Strings passed from C are not guaranteed to be valid.
-func sanitize(s *string) {
-	if !utf8.ValidString(*s) {
-		*s = strings.Map(
-			func(r rune) rune {
-				if r == utf8.RuneError {
-					// Need to replace invalid UTF-8 with a valid UTF-8 marker
-					return '?'
-				}
-				return r
-			},
-			*s,
-		)
-	}
-}
diff --git a/testdata/gary.jpg b/testdata/gary.jpg
new file mode 100644
index 0000000..0866320
Binary files /dev/null and b/testdata/gary.jpg differ
diff --git a/thumbnailer.c b/thumbnailer.c
index 1f47902..e3c2333 100644
--- a/thumbnailer.c
+++ b/thumbnailer.c
@@ -90,7 +90,7 @@ static AVFrame* select_best_frame(AVFrame* frames[], int size)
 static void alloc_buffer(struct Buffer* dst)
 {
     dst->size
-        = av_image_get_buffer_size(AV_PIX_FMT_RGBA, dst->width, dst->height, 1);
+        = av_image_get_buffer_size(AV_PIX_FMT_RGBA, dst->width, dst->height, 32);
     dst->data = malloc(dst->size);
 }
 
@@ -161,6 +161,7 @@ static void downscale(struct Buffer* dst, const struct Buffer const* src)
             i += 4;
         }
     }
+    free(img);
 }
 
 // Decrease intensity of pixels with alpha
@@ -448,7 +449,6 @@ int get_rotation(AVStream* video_stream) {
     if (!video_stream)
         return 0;
 
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(60,31,0) /* ffmpeg >= 6.1 */
     const AVCodecParameters *par = video_stream->codecpar;
     if(!par)
         return 0;
@@ -458,14 +458,6 @@ int get_rotation(AVStream* video_stream) {
             sd->size >= 9 * sizeof(int32_t))
             return extract_rotation_from_matrix((const int32_t*)sd->data);
     }
-#else /* ffmpeg < 6.1 */
-    for (int i = 0; i < video_stream->nb_side_data; ++i) {
-        const AVPacketSideData *sd = &video_stream->side_data[i];
-        if (sd->type == AV_PKT_DATA_DISPLAYMATRIX &&
-            sd->size >= 9 * sizeof(int32_t))
-            return extract_rotation_from_matrix((const int32_t*)sd->data);
-    }
-#endif
     return 0; // No rotation detected or side data not found
 }
 
@@ -478,7 +470,7 @@ int generate_thumbnail(struct Buffer* img, AVFormatContext* avfc,
     AVFrame* frames[MAX_FRAMES] = { NULL };
     AVFrame* next = NULL;
 
-    // Read up to 10 frames
+    // Read up to MAX_FRAMES
     while (1) {
         next = av_frame_alloc();
         err = read_frame(avfc, avcc, next, stream);
@@ -518,8 +510,7 @@ end:
         }
 
         // Ignore all read errors, if at least one frame read
-        err = encode_frame(
-            img, select_best_frame(frames, size), thumb_dims, orientation);
+        err = encode_frame(img, select_best_frame(frames, size), thumb_dims, orientation);
     }
 
     for (int i = 0; i < size; i++) {
