├── .gitignore ├── LICENSE ├── README.md ├── go_mediainfo.h ├── mediainfo.go ├── mediainfo_test.go └── testdata ├── test.mp3 └── test.ogg /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Gleb Sinyavsky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mediainfo 2 | Golang binding for [libmediainfo](https://mediaarea.net/en/MediaInfo) 3 | 4 | Duration, Bitrate, Codec, Streams and a lot of other meta-information about media files can be extracted through it. 5 | 6 | For now supports only media files with one stream. Bindings for MediaInfoList is not provided. It can be easy fixed if anybody ask me. 7 | 8 | Works through MediaInfoDLL/MediaInfoDLL.h(dynamic load and so on), so your mediainfo installation should has it. 9 | 10 | Supports direct reading files by name and reading data from []byte buffers(without copying it for C calls). 11 | 12 | Documentation for libmediainfo is poor and ascetic, can be found [here](https://mediaarea.net/en/MediaInfo/Support/SDK). 13 | 14 | Your advices and suggestions are welcome! 15 | 16 | ## Example 17 | ```go 18 | package main 19 | 20 | import ( 21 | "fmt" 22 | "github.com/zhulik/go_mediainfo" 23 | "io/ioutil" 24 | "os" 25 | ) 26 | 27 | func main() { 28 | f, err := os.Open(os.Args[1]) 29 | if err != nil { 30 | panic(err) 31 | } 32 | bytes, err := ioutil.ReadAll(f) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | mi := mediainfo.NewMediaInfo() 38 | err = mi.OpenMemory(bytes) 39 | if err != nil { 40 | panic(err) 41 | } 42 | fmt.Println(mi.AvailableParameters()) // Print all supported params for Get 43 | fmt.Println(mi.Get("BitRate")) // Print bitrate 44 | } 45 | 46 | ``` 47 | 48 | Read the [documentation](https://godoc.org/github.com/zhulik/go_mediainfo) for other functions 49 | 50 | ## Contributing 51 | You know=) -------------------------------------------------------------------------------- /go_mediainfo.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const wchar_t *toWchar(const char *c) 10 | { 11 | const size_t cSize = strlen(c)+1; 12 | wchar_t* wc = malloc(cSize * sizeof(wchar_t)); 13 | mbstowcs (wc, c, cSize); 14 | return wc; 15 | } 16 | 17 | const char *toChar(const wchar_t *c) 18 | { 19 | const size_t cSize = wcslen(c)+1; 20 | char* wc = malloc(cSize * sizeof(char)); 21 | wcstombs(wc, c, cSize); 22 | return wc; 23 | } 24 | 25 | void *GoMediaInfo_New() { 26 | return MediaInfo_New(); 27 | } 28 | 29 | void GoMediaInfo_Delete(void *handle) { 30 | MediaInfo_Delete(handle); 31 | } 32 | 33 | size_t GoMediaInfo_OpenFile(void *handle, char *name) { 34 | return MediaInfo_Open(handle, toWchar(name)); 35 | } 36 | 37 | size_t GoMediaInfo_OpenMemory(void *handle, char *bytes, size_t length) { 38 | MediaInfo_Open_Buffer_Init(handle, length, 0); 39 | MediaInfo_Open_Buffer_Continue(handle, bytes, length); 40 | 41 | return MediaInfo_Open_Buffer_Finalize(handle); 42 | } 43 | 44 | void GoMediaInfo_Close(void *handle) { 45 | MediaInfo_Close(handle); 46 | } 47 | 48 | const char *GoMediaInfoGet(void *handle, char *name) { 49 | return toChar(MediaInfo_Get(handle, MediaInfo_Stream_General, 0, toWchar(name), MediaInfo_Info_Text, MediaInfo_Info_Name)); 50 | } 51 | 52 | const char *GoMediaInfoOption(void *handle, char *name, char *value) { 53 | return toChar(MediaInfo_Option(handle, toWchar(name), toWchar(value))); 54 | } 55 | 56 | const char *GoMediaInfoInform(void *handle) { 57 | return toChar(MediaInfo_Inform(handle, 0)); 58 | } -------------------------------------------------------------------------------- /mediainfo.go: -------------------------------------------------------------------------------- 1 | package mediainfo 2 | 3 | // #cgo CFLAGS: -DUNICODE 4 | // #cgo LDFLAGS: -lz -lzen -lpthread -lstdc++ -lmediainfo -ldl 5 | // #include "go_mediainfo.h" 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | "runtime" 11 | "strconv" 12 | "unsafe" 13 | ) 14 | 15 | // MediaInfo - represents MediaInfo class, all interaction with libmediainfo through it 16 | type MediaInfo struct { 17 | handle unsafe.Pointer 18 | } 19 | 20 | func init() { 21 | C.setlocale(C.LC_CTYPE, C.CString("")) 22 | C.MediaInfoDLL_Load() 23 | 24 | if C.MediaInfoDLL_IsLoaded() == 0 { 25 | panic("Cannot load mediainfo") 26 | } 27 | } 28 | 29 | // NewMediaInfo - constructs new MediaInfo 30 | func NewMediaInfo() *MediaInfo { 31 | result := &MediaInfo{handle: C.GoMediaInfo_New()} 32 | runtime.SetFinalizer(result, func(f *MediaInfo) { 33 | f.Close() 34 | C.GoMediaInfo_Delete(f.handle) 35 | }) 36 | return result 37 | } 38 | 39 | // OpenFile - opens file 40 | func (mi *MediaInfo) OpenFile(path string) error { 41 | p := C.CString(path) 42 | s := C.GoMediaInfo_OpenFile(mi.handle, p) 43 | if s == 0 { 44 | return fmt.Errorf("MediaInfo can't open file: %s", path) 45 | } 46 | C.free(unsafe.Pointer(p)) 47 | return nil 48 | } 49 | 50 | // OpenMemory - opens memory buffer 51 | func (mi *MediaInfo) OpenMemory(bytes []byte) error { 52 | if len(bytes) == 0 { 53 | return fmt.Errorf("Buffer is empty") 54 | } 55 | s := C.GoMediaInfo_OpenMemory(mi.handle, (*C.char)(unsafe.Pointer(&bytes[0])), C.size_t(len(bytes))) 56 | if s == 0 { 57 | return fmt.Errorf("MediaInfo can't open memory buffer") 58 | } 59 | return nil 60 | } 61 | 62 | // Close - closes file 63 | func (mi *MediaInfo) Close() { 64 | C.GoMediaInfo_Close(mi.handle) 65 | } 66 | 67 | // Get - allow to read info from file 68 | func (mi *MediaInfo) Get(param string) (result string) { 69 | p := C.CString(param) 70 | r := C.GoMediaInfoGet(mi.handle, p) 71 | result = C.GoString(r) 72 | C.free(unsafe.Pointer(p)) 73 | C.free(unsafe.Pointer(r)) 74 | return 75 | } 76 | 77 | // Inform returns string with summary file information, like mediainfo util 78 | func (mi *MediaInfo) Inform() (result string) { 79 | r := C.GoMediaInfoInform(mi.handle) 80 | result = C.GoString(r) 81 | C.free(unsafe.Pointer(r)) 82 | return 83 | } 84 | 85 | // Option configure or get information about MediaInfoLib 86 | func (mi *MediaInfo) Option(option string, value string) (result string) { 87 | o := C.CString(option) 88 | v := C.CString(value) 89 | r := C.GoMediaInfoOption(mi.handle, o, v) 90 | C.free(unsafe.Pointer(o)) 91 | C.free(unsafe.Pointer(v)) 92 | result = C.GoString(r) 93 | C.free(unsafe.Pointer(r)) 94 | return 95 | } 96 | 97 | // AvailableParameters returns string with all available Get params and it's descriptions 98 | func (mi *MediaInfo) AvailableParameters() string { 99 | return mi.Option("Info_Parameters", "") 100 | } 101 | 102 | // Duration returns file duration 103 | func (mi *MediaInfo) Duration() int { 104 | duration, _ := strconv.Atoi(mi.Get("Duration")) 105 | return duration 106 | } 107 | 108 | // Codec returns file codec 109 | func (mi *MediaInfo) Codec() string { 110 | return mi.Get("Codec") 111 | } 112 | 113 | // Format returns file codec 114 | func (mi *MediaInfo) Format() string { 115 | return mi.Get("Format") 116 | } 117 | -------------------------------------------------------------------------------- /mediainfo_test.go: -------------------------------------------------------------------------------- 1 | package mediainfo_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/zhulik/go_mediainfo" 6 | "io/ioutil" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | const ( 12 | ogg = "testdata/test.ogg" 13 | mp3 = "testdata/test.mp3" 14 | nonExists = "testdata/non_exists.ogg" 15 | ) 16 | 17 | func TestOpenWithOgg(t *testing.T) { 18 | mi := mediainfo.NewMediaInfo() 19 | error := mi.OpenFile(ogg) 20 | if error != nil { 21 | t.Fail() 22 | } 23 | } 24 | 25 | func TestOpenWithMp3(t *testing.T) { 26 | mi := mediainfo.NewMediaInfo() 27 | error := mi.OpenFile(mp3) 28 | if error != nil { 29 | t.Fail() 30 | } 31 | } 32 | 33 | func TestOpenWithUnexistsFile(t *testing.T) { 34 | mi := mediainfo.NewMediaInfo() 35 | error := mi.OpenFile(nonExists) 36 | if error == nil { 37 | t.Fail() 38 | } 39 | } 40 | 41 | func TestOpenMemoryWithOgg(t *testing.T) { 42 | mi := mediainfo.NewMediaInfo() 43 | f, _ := os.Open(ogg) 44 | bytes, _ := ioutil.ReadAll(f) 45 | 46 | error := mi.OpenMemory(bytes) 47 | if error != nil { 48 | t.Fail() 49 | } 50 | } 51 | 52 | func TestOpenMemoryWithMp3(t *testing.T) { 53 | mi := mediainfo.NewMediaInfo() 54 | f, _ := os.Open(mp3) 55 | bytes, _ := ioutil.ReadAll(f) 56 | 57 | error := mi.OpenMemory(bytes) 58 | if error != nil { 59 | t.Fail() 60 | } 61 | } 62 | 63 | func TestOpenMemoryWithEmptyArray(t *testing.T) { 64 | mi := mediainfo.NewMediaInfo() 65 | error := mi.OpenMemory([]byte{}) 66 | if error == nil { 67 | t.Fail() 68 | } 69 | } 70 | 71 | func TestInformWithOgg(t *testing.T) { 72 | mi := mediainfo.NewMediaInfo() 73 | mi.OpenFile(ogg) 74 | 75 | if len(mi.Inform()) < 10 { 76 | t.Fail() 77 | } 78 | } 79 | 80 | func TestInformWithMp3(t *testing.T) { 81 | mi := mediainfo.NewMediaInfo() 82 | mi.OpenFile(mp3) 83 | 84 | if len(mi.Inform()) < 10 { 85 | t.Fail() 86 | } 87 | } 88 | 89 | func TestAvailableParametersWithOgg(t *testing.T) { 90 | mi := mediainfo.NewMediaInfo() 91 | mi.OpenFile(ogg) 92 | 93 | if len(mi.AvailableParameters()) < 10 { 94 | t.Fail() 95 | } 96 | } 97 | 98 | func TestAvailableParametersWithMp3(t *testing.T) { 99 | mi := mediainfo.NewMediaInfo() 100 | mi.OpenFile(mp3) 101 | 102 | if len(mi.AvailableParameters()) < 10 { 103 | t.Fail() 104 | } 105 | } 106 | 107 | func TestDurationWithOgg(t *testing.T) { 108 | mi := mediainfo.NewMediaInfo() 109 | mi.OpenFile(ogg) 110 | 111 | if mi.Duration() != 3494 { 112 | t.Fail() 113 | } 114 | } 115 | 116 | func TestDurationWithMp3(t *testing.T) { 117 | mi := mediainfo.NewMediaInfo() 118 | mi.OpenFile(mp3) 119 | 120 | if mi.Duration() != 87771 { 121 | t.Fail() 122 | } 123 | } 124 | 125 | func TestCodecWithOgg(t *testing.T) { 126 | mi := mediainfo.NewMediaInfo() 127 | mi.OpenFile(ogg) 128 | 129 | if mi.Codec() != "OGG" { 130 | t.Fail() 131 | } 132 | } 133 | 134 | func TestCodecWithMp3(t *testing.T) { 135 | mi := mediainfo.NewMediaInfo() 136 | mi.OpenFile(mp3) 137 | 138 | if mi.Codec() != "MPEG Audio" { 139 | t.Fail() 140 | } 141 | } 142 | 143 | func TestFormatWithOgg(t *testing.T) { 144 | mi := mediainfo.NewMediaInfo() 145 | mi.OpenFile(ogg) 146 | 147 | if mi.Format() != "OGG" { 148 | t.Fail() 149 | } 150 | } 151 | 152 | func TestFormatWithMp3(t *testing.T) { 153 | mi := mediainfo.NewMediaInfo() 154 | mi.OpenFile(mp3) 155 | 156 | if mi.Format() != "MPEG Audio" { 157 | t.Fail() 158 | } 159 | } 160 | 161 | //---------------------------------------------------------------------------------------------------------------------- 162 | func BenchmarkOpenAndDurationWithOgg(b *testing.B) { 163 | for n := 0; n < b.N; n++ { 164 | mi := mediainfo.NewMediaInfo() 165 | mi.OpenFile(ogg) 166 | 167 | mi.Duration() 168 | } 169 | } 170 | 171 | func BenchmarkOpenAndDurationWithMp3(b *testing.B) { 172 | for n := 0; n < b.N; n++ { 173 | mi := mediainfo.NewMediaInfo() 174 | mi.OpenFile(mp3) 175 | 176 | mi.Duration() 177 | } 178 | } 179 | 180 | func BenchmarkOpenMemoryAndDurationWithOgg(b *testing.B) { 181 | for n := 0; n < b.N; n++ { 182 | mi := mediainfo.NewMediaInfo() 183 | f, _ := os.Open(ogg) 184 | bytes, _ := ioutil.ReadAll(f) 185 | 186 | mi.OpenMemory(bytes) 187 | mi.Duration() 188 | } 189 | } 190 | 191 | func BenchmarkOpenMemoryAndDurationWithMp3(b *testing.B) { 192 | for n := 0; n < b.N; n++ { 193 | mi := mediainfo.NewMediaInfo() 194 | f, _ := os.Open(mp3) 195 | bytes, _ := ioutil.ReadAll(f) 196 | 197 | mi.OpenMemory(bytes) 198 | mi.Duration() 199 | } 200 | } 201 | 202 | //---------------------------------------------------------------------------------------------------------------------- 203 | 204 | func ExampleUsage() { 205 | f, err := os.Open(os.Args[1]) 206 | if err != nil { 207 | panic(err) 208 | } 209 | bytes, err := ioutil.ReadAll(f) 210 | if err != nil { 211 | panic(err) 212 | } 213 | 214 | mi := mediainfo.NewMediaInfo() 215 | err = mi.OpenMemory(bytes) 216 | if err != nil { 217 | panic(err) 218 | } 219 | fmt.Println(mi.AvailableParameters()) // Print all supported params for Get 220 | fmt.Println(mi.Get("BitRate")) // Print bitrate 221 | } 222 | -------------------------------------------------------------------------------- /testdata/test.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhulik/go_mediainfo/29d57b2a6ea0dc7786777ebd76a4035c0403987c/testdata/test.mp3 -------------------------------------------------------------------------------- /testdata/test.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhulik/go_mediainfo/29d57b2a6ea0dc7786777ebd76a4035c0403987c/testdata/test.ogg --------------------------------------------------------------------------------