Skip to content

Commit

Permalink
Merge pull request #3 from Arimeka/reorganize-code
Browse files Browse the repository at this point in the history
Reorganize code
  • Loading branch information
Arimeka committed Apr 12, 2019
2 parents eeb1eec + a9070df commit 37d175e
Show file tree
Hide file tree
Showing 24 changed files with 235 additions and 122 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.idea
*.iml
.DS_Store
example/example

# coverage
coverage/*.out
Expand Down
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[![Build Status](https://travis-ci.org/Arimeka/mediaprobe.svg?branch=master)](https://travis-ci.org/Arimeka/mediaprobe)
[![Coverage Status](https://coveralls.io/repos/github/Arimeka/mediaprobe/badge.svg?branch=master)](https://coveralls.io/github/Arimeka/mediaprobe?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/Arimeka/mediaprobe)](https://goreportcard.com/report/github.com/Arimeka/mediaprobe)
[![GoDoc](https://godoc.org/github.com/Arimeka/mediaprobe?status.svg)](https://godoc.org/github.com/Arimeka/mediaprobe)

# mediaprobe

Expand All @@ -11,16 +12,34 @@ Parsing media files using bindings for getting information about codec, bitrate,
It uses [github.com/rakyll/magicmime](https://github.com/rakyll/magicmime) for detect mimetypes and
[github.com/3d0c/gmf](https://github.com/3d0c/gmf) for parsing audio and video. See these packages for installation info.

## TL;DR Installing on Ubuntu

1. You need go version 1.10 or higher
1. You need `libmagic-dev`
```bash
sudo apt-get install libmagic-dev
```
1. You need ffmpeg libraries version 4.x. You may [compile it from sources](https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu),
or use PPA. For example, [ppa:jonathonf/ffmpeg-4](https://launchpad.net/~jonathonf/+archive/ubuntu/ffmpeg-4)
```bash
sudo add-apt-repository ppa:jonathonf/ffmpeg-4
sudo apt-get update
sudo apt-get install libavcodec-dev libavdevice-dev libavfilter-dev \
libavformat-dev libavresample-dev libavutil-dev \
libpostproc-dev libswresample-dev libswscale-dev
```
## Usage

See example folder.
See [godoc](https://godoc.org/github.com/Arimeka/mediaprobe) examples.

## Benchmark

```
BenchmarkParse-4 200 7433064 ns/op 2069 B/op 59 allocs/op
BenchmarkInfo_FFProbe-4 300 4776643 ns/op 2392 B/op 48 allocs/op
BenchmarkInfo_ParseImage-4 3000 465799 ns/op 1488 B/op 23 allocs/op
BenchmarkParse-4 200 7877832 ns/op 2071 B/op 59 allocs/op
BenchmarkNew-4 10000 123999 ns/op 488 B/op 5 allocs/op
BenchmarkInfo_CalculateMime-4 500 2303269 ns/op 75 B/op 4 allocs/op
BenchmarkInfo_FFProbe-4 300 5137768 ns/op 2392 B/op 48 allocs/op
BenchmarkInfo_ParseImage-4 2000 578220 ns/op 1320 B/op 23 allocs/op
```

## Caveats
Expand Down
17 changes: 15 additions & 2 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (
)

const (
benchmarkValidVideo = "./example/samples/video.mp4"
benchmarkValidImage = "./example/samples/image.jpeg"
benchmarkValidVideo = "./fixtures/video.mp4"
benchmarkValidImage = "./fixtures/image.jpeg"
)

func BenchmarkParse(b *testing.B) {
Expand All @@ -17,6 +17,19 @@ func BenchmarkParse(b *testing.B) {
}
}

func BenchmarkNew(b *testing.B) {
for i := 0; i < b.N; i++ {
mediaprobe.New(benchmarkValidVideo)
}
}

func BenchmarkInfo_CalculateMime(b *testing.B) {
info, _ := mediaprobe.New(benchmarkValidVideo)
for i := 0; i < b.N; i++ {
info.CalculateMime()
}
}

func BenchmarkInfo_FFProbe(b *testing.B) {
info, _ := mediaprobe.New(benchmarkValidVideo)
b.ResetTimer()
Expand Down
22 changes: 0 additions & 22 deletions example/main.go

This file was deleted.

30 changes: 29 additions & 1 deletion example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,38 @@ import (
)

func ExampleParse() {
info, err := mediaprobe.Parse("./samples/video.mp4")
info, err := mediaprobe.Parse("./fixtures/video.mp4")
if err != nil {
log.Fatalf("Error: %s\n", err)
}

log.Printf("Result: %+v\n", info)
}

func ExampleInfo_FFProbe() {
info, err := mediaprobe.New("./fixtures/video.mp4")
if err != nil {
log.Fatalf("Error: %s\n", err)
}

// Skipping calculate mime-type by magic numbers and immediately parse video
if err = info.FFProbe(); err != nil {
log.Fatalf("Error: %s\n", err)
}

log.Printf("Result: %+v\n", info)
}

func ExampleInfo_ParseImage() {
info, err := mediaprobe.New("./fixtures/image.jpeg")
if err != nil {
log.Fatalf("Error: %s\n", err)
}

// Skipping calculate mime-type by magic numbers and immediately parse image
if err = info.ParseImage(); err != nil {
log.Fatalf("Error: %s\n", err)
}

log.Printf("Result: %+v\n", info)
}
15 changes: 8 additions & 7 deletions ffprobe.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import (
"github.com/3d0c/gmf"
)

// FFProbe parsing audio/video file
func (probe *Info) FFProbe() error {
inputCtx, err := gmf.NewInputCtx(probe.filename)
// FFProbe parses video or audio using ffmpeg bindings
// It uses github.com/3d0c/gmf package
func (info *Info) FFProbe() error {
inputCtx, err := gmf.NewInputCtx(info.filename)
if err != nil {
return err
}
defer inputCtx.Free()

probe.Duration = time.Duration(inputCtx.Duration() * float64(time.Second))
probe.StartTime = time.Duration(inputCtx.StartTime() * int(time.Second))
probe.BitRate = inputCtx.BitRate()
info.Duration = time.Duration(inputCtx.Duration() * float64(time.Second))
info.StartTime = time.Duration(inputCtx.StartTime() * int(time.Second))
info.BitRate = inputCtx.BitRate()

for idx := 0; idx < inputCtx.StreamsCnt(); idx++ {
stream, err := inputCtx.GetStream(idx)
Expand Down Expand Up @@ -54,7 +55,7 @@ func (probe *Info) FFProbe() error {
codecCtx.Free()
stream.Free()

probe.Streams = append(probe.Streams, streamInfo)
info.Streams = append(info.Streams, streamInfo)
}

return nil
Expand Down
12 changes: 6 additions & 6 deletions ffprobe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import (
)

var bitrateFiles = map[string]int64{
"./example/samples/video.mp4": 551193,
"./example/samples/audio.mp3": 128000,
"./example/samples/not-a-video.mp4": 0,
"./example/samples/corrupted.mp4": 0,
"./fixtures/video.mp4": 551193,
"./fixtures/audio.mp3": 128000,
"./fixtures/not-a-video.mp4": 0,
"./fixtures/corrupted.mp4": 0,
}

func TestInfo_FFProbe(t *testing.T) {
Expand All @@ -19,11 +19,11 @@ func TestInfo_FFProbe(t *testing.T) {

err := info.FFProbe()
if err != nil {
if filename != "./example/samples/corrupted.mp4" {
if filename != "./fixtures/corrupted.mp4" {
t.Errorf("Filename: %s. Unexpected error %v", filename, err)
}
} else {
if filename == "./example/samples/corrupted.mp4" {
if filename == "./fixtures/corrupted.mp4" {
t.Errorf("Filename: %s. Expected to return error but return nil", filename)
}
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes
File renamed without changes.
20 changes: 9 additions & 11 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import (
)

// ParseImage used for retrieve image data
func (probe *Info) ParseImage() error {
file, err := os.Open(probe.filename)
func (info *Info) ParseImage() error {
file, err := os.Open(info.filename)
if err != nil {
return err
}
Expand All @@ -38,21 +38,19 @@ func (probe *Info) ParseImage() error {
return err
}

info.Width = img.Width
info.Height = img.Height

if orientationTag, err := x.Get(exif.Orientation); err == nil {
switch orientationTag.String() {
case "5", "6", "7", "8":
probe.Width = img.Height
probe.Height = img.Width
info.Width = img.Height
info.Height = img.Width
default:
probe.Width = img.Width
probe.Height = img.Height
info.Width = img.Width
info.Height = img.Height
}

return nil
}

probe.Width = img.Width
probe.Height = img.Height

return nil
}
14 changes: 11 additions & 3 deletions image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,19 @@ import (
)

const (
testImageValidImage = "./example/samples/image.jpeg"
testImageWithExifOrientation = "./example/samples/left.jpg"
testImageInvalidImage = "./example/samples/not-an-image.jpeg"
testImageValidImage = "./fixtures/image.jpeg"
testImageWithExifOrientation = "./fixtures/left.jpg"
testImageInvalidImage = "./fixtures/not-an-image.jpeg"
)

func TestInfo_ParseImageNotFound(t *testing.T) {
info := &mediaprobe.Info{}
err := info.ParseImage()
if err == nil {
t.Errorf("Expected to return error found but return nil")
}
}

func TestInfo_ParseImage(t *testing.T) {
info, _ := mediaprobe.New(testImageInvalidImage)
err := info.ParseImage()
Expand Down
29 changes: 27 additions & 2 deletions info.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
package mediaprobe

import "time"
import (
"os"
"time"
)

// New initialized Info using magic bytes for calculating media type
func New(filename string) (*Info, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()

fileinfo, err := file.Stat()
if err != nil {
return nil, err
}

info := &Info{
filename: filename,
Name: fileinfo.Name(),
Size: fileinfo.Size(),
}

return info, nil
}

// Info contains parsed information
type Info struct {
Expand All @@ -18,7 +43,7 @@ type Info struct {
Streams []Stream
}

// Stream used for contain audio/video stream information
// Stream contains audio/video stream information
type Stream struct {
ID int
Index int
Expand Down
14 changes: 14 additions & 0 deletions info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package mediaprobe_test

import (
"testing"

"github.com/Arimeka/mediaprobe"
)

func TestNewNotFound(t *testing.T) {
_, err := mediaprobe.New("")
if err == nil {
t.Errorf("Expected to return error found but return nil")
}
}
32 changes: 32 additions & 0 deletions mime.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package mediaprobe

import (
"strings"

"github.com/rakyll/magicmime"
)

// CalculateMime calculates mime type by magic numbers
// Function uses libmagic bindings using github.com/rakyll/magicmime package.
func (info *Info) CalculateMime() error {
if err := magicmime.Open(magicmime.MAGIC_MIME_TYPE | magicmime.MAGIC_SYMLINK | magicmime.MAGIC_ERROR); err != nil {
return err
}
defer magicmime.Close()

media, err := magicmime.TypeByFile(info.filename)
if err != nil {
return err
}

info.MediaType = "application"
info.MediaSubtype = "octet-stream"

kind := strings.Split(media, "/")
if len(kind) == 2 {
info.MediaType = kind[0]
info.MediaSubtype = kind[1]
}

return nil
}
15 changes: 15 additions & 0 deletions mime_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mediaprobe_test

import (
"testing"

"github.com/Arimeka/mediaprobe"
)

func TestInfo_CalculateMimeNotFound(t *testing.T) {
info := &mediaprobe.Info{}
err := info.CalculateMime()
if err == nil {
t.Errorf("Expected to return error found but return nil")
}
}

0 comments on commit 37d175e

Please sign in to comment.