Skip to content

Commit 3f773c3

Browse files
committed
Added enumeration of channel layouts, sample formats and pixel formats
1 parent 42f791d commit 3f773c3

13 files changed

+319
-44
lines changed

cmd/cli/formats.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
"os"
5+
6+
// Packages
7+
"github.com/djthorpe/go-tablewriter"
8+
"github.com/mutablelogic/go-media"
9+
)
10+
11+
type SampleFormatsCmd struct{}
12+
13+
type ChannelLayoutsCmd struct{}
14+
15+
type PixelFormatsCmd struct{}
16+
17+
func (cmd *SampleFormatsCmd) Run(globals *Globals) error {
18+
manager := media.NewManager()
19+
writer := tablewriter.New(os.Stdout, tablewriter.OptHeader(), tablewriter.OptOutputText())
20+
return writer.Write(manager.SampleFormats())
21+
}
22+
23+
func (cmd *ChannelLayoutsCmd) Run(globals *Globals) error {
24+
manager := media.NewManager()
25+
writer := tablewriter.New(os.Stdout, tablewriter.OptHeader(), tablewriter.OptOutputText())
26+
return writer.Write(manager.ChannelLayouts())
27+
}
28+
29+
func (cmd *PixelFormatsCmd) Run(globals *Globals) error {
30+
manager := media.NewManager()
31+
writer := tablewriter.New(os.Stdout, tablewriter.OptHeader(), tablewriter.OptOutputText())
32+
return writer.Write(manager.PixelFormats())
33+
}

cmd/cli/main.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ type Globals struct {
1414

1515
type CLI struct {
1616
Globals
17-
Version VersionCmd `cmd:"version" help:"Print version information"`
18-
Demuxers DemuxersCmd `cmd:"demuxers" help:"List media demultiplex (input) formats"`
19-
Muxers MuxersCmd `cmd:"muxers" help:"List media multiplex (output) formats"`
20-
Metadata MetadataCmd `cmd:"metadata" help:"Display media metadata information"`
21-
Probe ProbeCmd `cmd:"probe" help:"Probe media file or device"`
22-
Decode DecodeCmd `cmd:"decode" help:"Decode media"`
17+
Version VersionCmd `cmd:"version" help:"Print version information"`
18+
Demuxers DemuxersCmd `cmd:"demuxers" help:"List media demultiplex (input) formats"`
19+
Muxers MuxersCmd `cmd:"muxers" help:"List media multiplex (output) formats"`
20+
SampleFormats SampleFormatsCmd `cmd:"samplefmts" help:"List audio sample formats"`
21+
ChannelLayouts ChannelLayoutsCmd `cmd:"channellayouts" help:"List audio channel layouts"`
22+
PixelFormats PixelFormatsCmd `cmd:"pixelfmts" help:"List video pixel formats"`
23+
Metadata MetadataCmd `cmd:"metadata" help:"Display media metadata information"`
24+
Probe ProbeCmd `cmd:"probe" help:"Probe media file or device"`
25+
Decode DecodeCmd `cmd:"decode" help:"Decode media"`
2326
}
2427

2528
func main() {

cmd/cli/metadata.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ type MetadataCmd struct {
1414
}
1515

1616
func (cmd *MetadataCmd) Run(globals *Globals) error {
17-
reader, err := media.Open(cmd.Path, nil)
17+
manager := media.NewManager()
18+
reader, err := manager.Open(cmd.Path, nil)
1819
if err != nil {
1920
return err
2021
}

decoder.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,40 @@ func newDemuxer(input *ff.AVFormatContext, mapfn DecoderMapFunc) (*demuxer, erro
4040
// Get all the streams
4141
streams := input.Streams()
4242

43+
// Use standard map function if none provided
44+
if mapfn == nil {
45+
mapfn = func(stream Stream) (Parameters, error) {
46+
return stream.Parameters(), nil
47+
}
48+
}
49+
4350
// Create a decoder for each stream
4451
// The decoder map function should be returning the parameters for the
4552
// destination frame. If it's nil then it's mostly a copy.
53+
var result error
4654
for _, stream := range streams {
47-
if mapfn == nil || mapfn(stream) {
48-
if decoder, err := demuxer.newDecoder(stream); err != nil {
49-
return nil, errors.Join(err, demuxer.close())
50-
} else {
51-
streamNum := stream.Index()
52-
demuxer.decoders[streamNum] = decoder
53-
}
55+
// Get decoder parameters
56+
parameters, err := mapfn(newStream(stream))
57+
if err != nil {
58+
result = errors.Join(result, err)
59+
} else if parameters == nil {
60+
continue
61+
}
62+
63+
// Create the decoder with the parameters
64+
if decoder, err := demuxer.newDecoder(stream, parameters); err != nil {
65+
result = errors.Join(result, err)
66+
} else {
67+
streamNum := stream.Index()
68+
demuxer.decoders[streamNum] = decoder
5469
}
5570
}
5671

72+
// Return any errors
73+
if result != nil {
74+
return nil, errors.Join(result, demuxer.close())
75+
}
76+
5777
// Create a frame for encoding - after resampling and resizing
5878
if frame := ff.AVUtil_frame_alloc(); frame == nil {
5979
return nil, errors.Join(demuxer.close(), errors.New("failed to allocate frame"))
@@ -65,10 +85,12 @@ func newDemuxer(input *ff.AVFormatContext, mapfn DecoderMapFunc) (*demuxer, erro
6585
return demuxer, nil
6686
}
6787

68-
func (d *demuxer) newDecoder(stream *ff.AVStream) (*decoder, error) {
88+
func (d *demuxer) newDecoder(stream *ff.AVStream, parameters Parameters) (*decoder, error) {
6989
decoder := new(decoder)
7090
decoder.stream = stream.Id()
7191

92+
// TODO: Use parameters to create the decoder
93+
7294
// Create a codec context for the decoder
7395
codec := ff.AVCodec_find_decoder(stream.CodecPar().CodecID())
7496
if codec == nil {
@@ -192,6 +214,14 @@ FOR_LOOP:
192214
return ctx.Err()
193215
}
194216

217+
func (d *demuxer) Decode(context.Context, FrameFunc) error {
218+
// TODO
219+
return errors.New("not implemented")
220+
}
221+
222+
////////////////////////////////////////////////////////////////////////////
223+
// PRIVATE METHODS
224+
195225
func (d *decoder) decode(fn DecoderFunc, packet *ff.AVPacket) error {
196226
// Send the packet to the user defined packet function or
197227
// to the default version

decoder_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ func Test_decoder_001(t *testing.T) {
2222
}
2323
defer media.Close()
2424

25-
decoder, err := media.Decoder(func (stream Stream) Parameters {
25+
decoder, err := media.Decoder(func(stream Stream) (Parameters, error) {
2626
// Copy parameters from the stream
27-
return stream.Parameters()
28-
}
27+
return stream.Parameters(), nil
28+
})
2929
if !assert.NoError(err) {
3030
t.SkipNow()
3131
}

interfaces.go

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,6 @@ import (
1313
// Manager represents a manager for media formats and devices.
1414
// Create a new manager object using the NewManager function.
1515
type Manager interface {
16-
// Return supported input formats which match any filter, which can be
17-
// a name, extension (with preceeding period) or mimetype. The MediaType
18-
// can be NONE (for any) or combinations of DEVICE and STREAM.
19-
InputFormats(MediaType, ...string) []Format
20-
21-
// Return supported output formats which match any filter, which can be
22-
// a name, extension (with preceeding period) or mimetype. The MediaType
23-
// can be NONE (for any) or combinations of DEVICE and STREAM.
24-
OutputFormats(MediaType, ...string) []Format
25-
26-
// Return supported input devices for a given format name
27-
// Not all devices may be supported on all platforms or listed
28-
// if the device does not support enumeration.
29-
InputDevices(string) []Device
30-
31-
// Return supported output devices for a given format name
32-
// Not all devices may be supported on all platforms or listed
33-
// if the device does not support enumeration.
34-
OutputDevices(string) []Device
35-
3616
// Open a media file or device for reading, from a path or url.
3717
// If a format is specified, then the format will be used to open
3818
// the file. Close the media object when done.
@@ -57,9 +37,25 @@ type Manager interface {
5737
// TODO
5838
Write(io.Writer, Format) (Media, error)
5939

60-
// Return version information for the media manager as a set of
61-
// metadata
62-
Version() []Metadata
40+
// Return supported input formats which match any filter, which can be
41+
// a name, extension (with preceeding period) or mimetype. The MediaType
42+
// can be NONE (for any) or combinations of DEVICE and STREAM.
43+
InputFormats(MediaType, ...string) []Format
44+
45+
// Return supported output formats which match any filter, which can be
46+
// a name, extension (with preceeding period) or mimetype. The MediaType
47+
// can be NONE (for any) or combinations of DEVICE and STREAM.
48+
OutputFormats(MediaType, ...string) []Format
49+
50+
// Return supported input devices for a given format name
51+
// Not all devices may be supported on all platforms or listed
52+
// if the device does not support enumeration.
53+
InputDevices(string) []Device
54+
55+
// Return supported output devices for a given format name
56+
// Not all devices may be supported on all platforms or listed
57+
// if the device does not support enumeration.
58+
OutputDevices(string) []Device
6359

6460
// Return all supported channel layouts
6561
ChannelLayouts() []Metadata
@@ -71,12 +67,16 @@ type Manager interface {
7167
PixelFormats() []Metadata
7268

7369
// Return audio parameters for encoding
74-
// ChannelLayout, SampleFormat, SampleRate
70+
// ChannelLayout, SampleFormat, Samplerate
7571
AudioParameters(string, string, int) (AudioParameters, error)
7672

7773
// Return video parameters for encoding
78-
// Width, Height, PixelFormat, FrameRate
79-
VideoParameters(int, int, string, int) (VideoParameters, error)
74+
// Width, Height, PixelFormat, Framerate
75+
VideoParameters(int, int, string, float32) (VideoParameters, error)
76+
77+
// Return version information for the media manager as a set of
78+
// metadata
79+
Version() []Metadata
8080
}
8181

8282
// Device represents a device for input or output of media streams.

manager.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,68 @@ func (manager *manager) OutputDevices(format string) []Device {
163163
panic("TODO")
164164
}
165165

166+
// Return all supported channel layouts
167+
func (manager *manager) ChannelLayouts() []Metadata {
168+
var result []Metadata
169+
var iter uintptr
170+
for {
171+
ch := ff.AVUtil_channel_layout_standard(&iter)
172+
if ch == nil {
173+
break
174+
}
175+
if name, err := ff.AVUtil_channel_layout_describe(ch); err != nil {
176+
continue
177+
} else {
178+
result = append(result, newMetadata(name, ch))
179+
}
180+
}
181+
return result
182+
}
183+
184+
// Return all supported sample formats
185+
func (manager *manager) SampleFormats() []Metadata {
186+
var result []Metadata
187+
var opaque uintptr
188+
for {
189+
samplefmt := ff.AVUtil_next_sample_fmt(&opaque)
190+
if samplefmt == ff.AV_SAMPLE_FMT_NONE {
191+
break
192+
}
193+
if name := ff.AVUtil_get_sample_fmt_name(samplefmt); name != "" {
194+
result = append(result, newMetadata(name, samplefmt))
195+
}
196+
}
197+
return result
198+
}
199+
200+
// Return all supported pixel formats
201+
func (manager *manager) PixelFormats() []Metadata {
202+
var result []Metadata
203+
var opaque uintptr
204+
for {
205+
pixfmt := ff.AVUtil_next_pixel_fmt(&opaque)
206+
if pixfmt == ff.AV_PIX_FMT_NONE {
207+
break
208+
}
209+
if name := ff.AVUtil_get_pix_fmt_name(pixfmt); name != "" {
210+
result = append(result, newMetadata(name, pixfmt))
211+
}
212+
}
213+
return result
214+
}
215+
216+
// Return audio parameters for encoding
217+
// ChannelLayout, SampleFormat, Samplerate
218+
func (manager *manager) AudioParameters(string, string, int) (AudioParameters, error) {
219+
return nil, ErrNotImplemented
220+
}
221+
222+
// Return video parameters for encoding
223+
// Width, Height, PixelFormat, Framerate
224+
func (manager *manager) VideoParameters(int, int, string, float32) (VideoParameters, error) {
225+
return nil, ErrNotImplemented
226+
}
227+
166228
// Open a media file or device for reading, from a path or url.
167229
func (manager *manager) Open(url string, format Format, opts ...string) (Media, error) {
168230
return newMedia(url, format, opts...)

manager_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,39 @@ func Test_manager_003(t *testing.T) {
4646

4747
tablewriter.New(os.Stderr, tablewriter.OptHeader(), tablewriter.OptOutputText()).Write(version)
4848
}
49+
50+
func Test_manager_004(t *testing.T) {
51+
assert := assert.New(t)
52+
53+
manager := NewManager()
54+
assert.NotNil(manager)
55+
56+
channel_layouts := manager.ChannelLayouts()
57+
assert.NotNil(channel_layouts)
58+
59+
tablewriter.New(os.Stderr, tablewriter.OptHeader(), tablewriter.OptOutputText()).Write(channel_layouts)
60+
}
61+
62+
func Test_manager_005(t *testing.T) {
63+
assert := assert.New(t)
64+
65+
manager := NewManager()
66+
assert.NotNil(manager)
67+
68+
sample_formats := manager.SampleFormats()
69+
assert.NotNil(sample_formats)
70+
71+
tablewriter.New(os.Stderr, tablewriter.OptHeader(), tablewriter.OptOutputText()).Write(sample_formats)
72+
}
73+
74+
func Test_manager_006(t *testing.T) {
75+
assert := assert.New(t)
76+
77+
manager := NewManager()
78+
assert.NotNil(manager)
79+
80+
pixel_formats := manager.PixelFormats()
81+
assert.NotNil(pixel_formats)
82+
83+
tablewriter.New(os.Stderr, tablewriter.OptHeader(), tablewriter.OptOutputText()).Write(pixel_formats)
84+
}

stream.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package media
2+
3+
import (
4+
// Packages
5+
ff "github.com/mutablelogic/go-media/sys/ffmpeg61"
6+
)
7+
8+
////////////////////////////////////////////////////////////////////////////////
9+
// TYPES
10+
11+
type stream struct {
12+
*ff.AVStream
13+
}
14+
15+
var _ Stream = (*stream)(nil)
16+
17+
////////////////////////////////////////////////////////////////////////////////
18+
// LIFECYCLE
19+
20+
// Open media from a url, file path or device
21+
func newStream(ctx *ff.AVStream) *stream {
22+
return &stream{ctx}
23+
}
24+
25+
////////////////////////////////////////////////////////////////////////////////
26+
// PUBLIC METHODS
27+
28+
func (stream *stream) Type() MediaType {
29+
switch stream.CodecPar().CodecType() {
30+
case ff.AVMEDIA_TYPE_AUDIO:
31+
return AUDIO
32+
case ff.AVMEDIA_TYPE_VIDEO:
33+
return VIDEO
34+
case ff.AVMEDIA_TYPE_SUBTITLE:
35+
return SUBTITLE
36+
}
37+
return DATA
38+
}
39+
40+
func (stream *stream) Parameters() Parameters {
41+
// TODO
42+
return nil
43+
}

0 commit comments

Comments
 (0)