Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added SDL video player spike #30

Merged
merged 10 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading