Skip to content

Commit

Permalink
Merge pull request #30 from mutablelogic/ffmpeg61
Browse files Browse the repository at this point in the history
Added SDL video player spike
  • Loading branch information
djthorpe authored Jul 5, 2024
2 parents 25a536a + d0d1853 commit 0957e23
Show file tree
Hide file tree
Showing 47 changed files with 1,189 additions and 126 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/on_pull_request_merge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ jobs:
go-version: ${{ matrix.go-version }}
- name: Run tests
run: |
sudo apt install -y libavcodec-dev libavdevice-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev libchromaprint-dev
sudo apt install -y libavcodec-dev libavdevice-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev
sudo apt install -y libchromaprint-dev
sudo apt install -y libsdl2-dev
make container-test
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/llgcode/draw2d v0.0.0-20240627062922-0ed1ff131195
github.com/mutablelogic/go-client v1.0.8
github.com/stretchr/testify v1.9.0
github.com/veandco/go-sdl2 v0.4.40
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
)

Expand Down
51 changes: 28 additions & 23 deletions manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ package media
// }
//
// Various options are available to control the manager, for
// logging and affecting decoding.
// logging and affecting decoding, that can be applied when
// creating the manager by passing them as arguments.
//
// Only one manager can be created. If NewManager is called
// a second time, the previously created manager is returned,
Expand Down Expand Up @@ -46,33 +47,11 @@ type Manager interface {
// of the caller to also close the writer when done.
//Write(io.Writer, Format, []Metadata, ...Parameters) (Media, error)

// Return supported input formats which match any filter, which can be
// a name, extension (with preceeding period) or mimetype. The MediaType
// can be NONE (for any) or combinations of DEVICE and STREAM.
//InputFormats(Type, ...string) []Format

// Return supported output formats which match any filter, which can be
// a name, extension (with preceeding period) or mimetype. The MediaType
// can be NONE (for any) or combinations of DEVICE and STREAM.
//OutputFormats(Type, ...string) []Format

// Return supported devices for a given format.
// Not all devices may be supported on all platforms or listed
// if the device does not support enumeration.
//Devices(Format) []Device

// Return all supported channel layouts
//ChannelLayouts() []Metadata

// Return all supported sample formats
//SampleFormats() []Metadata

// Return all supported pixel formats
//PixelFormats() []Metadata

// Return all supported codecs
//Codecs() []Metadata

// Return audio parameters for encoding
// ChannelLayout, SampleFormat, Samplerate
//AudioParameters(string, string, int) (Parameters, error)
Expand All @@ -89,6 +68,32 @@ type Manager interface {
// Codec name, Profile name, Framerate (fps) and VideoParameters
//VideoCodecParameters(string, string, float64, VideoParameters) (Parameters, error)

// Return supported input formats which match any filter, which can be
// a name, extension (with preceeding period) or mimetype. The MediaType
// can be NONE (for any) or combinations of DEVICE and STREAM.
//InputFormats(Type, ...string) []Format

// Return supported output formats which match any filter, which can be
// a name, extension (with preceeding period) or mimetype. The MediaType
// can be NONE (for any) or combinations of DEVICE and STREAM.
//OutputFormats(Type, ...string) []Format

// Return all supported sample formats
SampleFormats() []Metadata

// Return all supported pixel formats
PixelFormats() []Metadata

// Return standard channel layouts which can be used for audio,
// with the number of channels provided. If no channels are provided,
// then all standard channel layouts are returned.
ChannelLayouts() []Metadata

// Return all supported codecs, of a specific type or all
// if ANY is used. If any names is provided, then only the codecs
// with those names are returned.
Codecs(Type, ...string) []Metadata

// Return version information for the media manager as a set of
// metadata
Version() []Metadata
Expand Down
3 changes: 3 additions & 0 deletions metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ type Metadata interface {

// Returns the value as an image
Image() image.Image

// Returns the value as an interface
Any() any
}
135 changes: 135 additions & 0 deletions pkg/ffmpeg/channellayout.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package ffmpeg

import (
"encoding/json"

// Packages
ff "github.com/mutablelogic/go-media/sys/ffmpeg61"
)

///////////////////////////////////////////////////////////////////////////////
// TYPES

type (
ChannelLayout ff.AVChannelLayout
Channel ff.AVChannel
)

type jsonChannelLayout struct {
Name string `json:"name"`
NumChannels int `json:"num_channels"`
Order string `json:"order"`
Channels []*Channel `json:"channels"`
}

type jsonChannel struct {
Index int `json:"index"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
}

///////////////////////////////////////////////////////////////////////////////
// LIFECYCLE

func newChannelLayout(channellayout *ff.AVChannelLayout) *ChannelLayout {
if !ff.AVUtil_channel_layout_check(channellayout) {
return nil
}
return (*ChannelLayout)(channellayout)
}

func newChannel(channel ff.AVChannel) *Channel {
if channel == ff.AV_CHAN_NONE {
return nil
}
return (*Channel)(&channel)
}

///////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (ch *ChannelLayout) MarshalJSON() ([]byte, error) {
return json.Marshal(&jsonChannelLayout{
Name: ch.Name(),
NumChannels: ch.NumChannels(),
Order: ch.Order(),
Channels: ch.Channels(),
})
}

func (ch *Channel) MarshalJSON() ([]byte, error) {
return json.Marshal(&jsonChannel{
Name: ch.Name(),
Description: ch.Description(),
})
}

func (ch *ChannelLayout) String() string {
data, _ := json.MarshalIndent(ch, "", " ")
return string(data)
}

func (ch *Channel) String() string {
data, _ := json.MarshalIndent(ch, "", " ")
return string(data)
}

///////////////////////////////////////////////////////////////////////////////
// PROPERTIES - CHANNEL LAYOUT

func (ch *ChannelLayout) Name() string {
if desc, err := ff.AVUtil_channel_layout_describe((*ff.AVChannelLayout)(ch)); err != nil {
return ""
} else {
return desc
}
}

func (ch *ChannelLayout) NumChannels() int {
return ff.AVUtil_get_channel_layout_nb_channels((*ff.AVChannelLayout)(ch))
}

func (ch *ChannelLayout) Channels() []*Channel {
var result []*Channel
for i := 0; i < ch.NumChannels(); i++ {
channel := ff.AVUtil_channel_layout_channel_from_index((*ff.AVChannelLayout)(ch), i)
if channel != ff.AV_CHAN_NONE {
result = append(result, newChannel(channel))
}
}
return result
}

func (ch *ChannelLayout) Order() string {
order := (*ff.AVChannelLayout)(ch).Order()
switch order {
case ff.AV_CHANNEL_ORDER_UNSPEC:
return "unspecified"
case ff.AV_CHANNEL_ORDER_NATIVE:
return "native"
case ff.AV_CHANNEL_ORDER_CUSTOM:
return "custom"
case ff.AV_CHANNEL_ORDER_AMBISONIC:
return "ambisonic"
}
return order.String()
}

///////////////////////////////////////////////////////////////////////////////
// PROPERTIES - CHANNEL

func (ch *Channel) Name() string {
if desc, err := ff.AVUtil_channel_name((ff.AVChannel)(*ch)); err != nil {
return "unknown"
} else {
return desc
}
}

func (ch *Channel) Description() string {
if desc, err := ff.AVUtil_channel_description((ff.AVChannel)(*ch)); err != nil {
return ""
} else {
return desc
}
}
22 changes: 22 additions & 0 deletions pkg/ffmpeg/channellayout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ffmpeg_test

import (
"testing"

// Packages
ffmpeg "github.com/mutablelogic/go-media/pkg/ffmpeg"
assert "github.com/stretchr/testify/assert"
)

func Test_channellayout_001(t *testing.T) {
assert := assert.New(t)

manager, err := ffmpeg.NewManager()
if !assert.NoError(err) {
t.FailNow()
}

for _, format := range manager.ChannelLayouts() {
t.Logf("%v", format)
}
}
114 changes: 114 additions & 0 deletions pkg/ffmpeg/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package ffmpeg

import (
"encoding/json"
"sort"

// Packages
media "github.com/mutablelogic/go-media"
ff "github.com/mutablelogic/go-media/sys/ffmpeg61"
)

///////////////////////////////////////////////////////////////////////////////
// TYPES

type Codec ff.AVCodec

///////////////////////////////////////////////////////////////////////////////
// LIFECYCLE

func newCodec(codec *ff.AVCodec) *Codec {
return (*Codec)(codec)
}

///////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (codec *Codec) MarshalJSON() ([]byte, error) {
return (*ff.AVCodec)(codec).MarshalJSON()
}

func (codec *Codec) String() string {
data, _ := json.MarshalIndent((*ff.AVCodec)(codec), "", " ")
return string(data)
}

///////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS

// Return the type of codec
func (codec *Codec) Type() media.Type {
switch (*ff.AVCodec)(codec).Type() {
case ff.AVMEDIA_TYPE_AUDIO:
return media.AUDIO
case ff.AVMEDIA_TYPE_VIDEO:
return media.VIDEO
case ff.AVMEDIA_TYPE_SUBTITLE:
return media.SUBTITLE
}
return media.NONE
}

// The name the codec is referred to by
func (codec *Codec) Name() string {
return (*ff.AVCodec)(codec).Name()
}

// The description of the codec
func (codec *Codec) Description() string {
return (*ff.AVCodec)(codec).LongName()
}

// Pixel formats supported by the codec. This is only valid for video codecs.
// The first pixel format is the default.
func (codec *Codec) PixelFormats() []string {
pixfmts := (*ff.AVCodec)(codec).PixelFormats()
result := make([]string, len(pixfmts))
for i, pixfmt := range pixfmts {
result[i] = ff.AVUtil_get_pix_fmt_name(pixfmt)
}
return result
}

// Sample formats supported by the codec. This is only valid for audio codecs.
// The first sample format is the default.
func (codec *Codec) SampleFormats() []string {
samplefmts := (*ff.AVCodec)(codec).SampleFormats()
result := make([]string, len(samplefmts))
for i, samplefmt := range samplefmts {
result[i] = ff.AVUtil_get_sample_fmt_name(samplefmt)
}
return result
}

// Sample rates supported by the codec. This is only valid for audio codecs.
// The first sample rate is the highest, sort the list in reverse order.
func (codec *Codec) SampleRates() []int {
samplerates := (*ff.AVCodec)(codec).SupportedSamplerates()
sort.Sort(sort.Reverse(sort.IntSlice(samplerates)))
return samplerates
}

// Channel layouts supported by the codec. This is only valid for audio codecs.
func (codec *Codec) ChannelLayouts() []string {
chlayouts := (*ff.AVCodec)(codec).ChannelLayouts()
result := make([]string, 0, len(chlayouts))
for _, chlayout := range chlayouts {
name, err := ff.AVUtil_channel_layout_describe(&chlayout)
if err != nil {
continue
}
result = append(result, name)
}
return result
}

// Profiles supported by the codec. This is only valid for video codecs.
func (codec *Codec) Profiles() []string {
profiles := (*ff.AVCodec)(codec).Profiles()
result := make([]string, len(profiles))
for i, profile := range profiles {
result[i] = profile.Name()
}
return result
}
Loading

0 comments on commit 0957e23

Please sign in to comment.