Skip to content

Commit d31618f

Browse files
authored
Merge pull request #15 from mutablelogic/ffmpeg61
Updates to the media abstraction
2 parents 1436b17 + b382447 commit d31618f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2695
-575
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ BUILD_DIR := "build"
2222
CMD_DIR := $(filter-out cmd/ffmpeg/README.md, $(wildcard cmd/ffmpeg/*))
2323
BUILD_TAG := ${DOCKER_REGISTRY}/go-media-${OS}-${ARCH}:${VERSION}
2424

25-
all: clean cmds
25+
all: clean cli cmds
2626

2727
cmds: $(CMD_DIR)
2828

@@ -47,6 +47,11 @@ test: go-dep
4747
@${GO} test ./pkg/...
4848
@${GO} test .
4949

50+
51+
cli: go-dep mkdir
52+
@echo Build media tool
53+
@${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/media ./cmd/cli
54+
5055
$(CMD_DIR): go-dep mkdir
5156
@echo Build cmd $(notdir $@)
5257
@${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@) ./$@

README.md

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,79 @@ DOCKER_REGISTRY=ghcr.io/mutablelogic make docker
4646

4747
## Examples
4848

49-
## Media Transcoding
49+
There are a variety of types of object needed as part of media processing.
50+
All examples require a `Manager` to be created, which is used to enumerate all supported formats
51+
and open media files and byte streams.
52+
53+
* `Manager` is the main entry point for the package. It is used to open media files and byte streams,
54+
and enumerate supported formats, codecs, pixel formats, etc.
55+
* `Media` is a hardware device, file or byte stream. It contains metadata, artwork, and streams.
56+
* `Decoder` is used to demultiplex media streams. Create a decoder and enumerate the streams which
57+
you'd like to demultiplex. Provide the audio and video parameters if you want to resample or
58+
reformat the streams.
59+
* `Encoder` is used to multiplex media streams. Create an encoder and send the output of the
60+
decoder to reencode the streams.
61+
62+
### Demultiplexing
63+
64+
```go
65+
import (
66+
media "github.com/mutablelogic/go-media"
67+
)
68+
69+
func main() {
70+
manager := media.NewManager()
71+
72+
// Open a media file for reading. The format of the file is guessed.
73+
// Alteratively, you can pass a format as the second argument. Further optional
74+
// arguments can be used to set the format options.
75+
file, err := manager.Open(os.Args[1], nil)
76+
if err != nil {
77+
log.Fatal(err)
78+
}
79+
defer file.Close()
80+
81+
// Choose which streams to demultiplex - pass the stream parameters
82+
// to the decoder. If you don't want to resample or reformat the streams,
83+
// then you can pass nil as the function and all streams will be demultiplexed.
84+
decoder, err := file.Decoder(func (stream media.Stream) (media.Parameters, error) {
85+
return stream.Parameters(), nil
86+
}
87+
if err != nil {
88+
log.Fatal(err)
89+
}
90+
91+
// Demuliplex the stream and receive the packets. If you don't want to
92+
// process the packets yourself, then you can pass nil as the function
93+
if err := decoder.Demux(context.Background(), func(_ media.Packet) error {
94+
// Each packet is specific to a stream. It can be processed here
95+
// to receive audio or video frames, then resize or resample them,
96+
// for example. Alternatively, you can pass the packet to an encoder
97+
// to remultiplex the streams without processing them.
98+
return nil
99+
}); err != nil {
100+
log.Fatal(err)
101+
})
102+
}
103+
```
104+
105+
### Decoding
106+
107+
TODO
108+
109+
### Encoding
110+
111+
TODO
50112

51-
## Audio Fingerprinting
113+
### Multiplexing
114+
115+
TODO
116+
117+
### Retrieving Metadata and Artwork from a media file
118+
119+
TODO
120+
121+
### Audio Fingerprinting
52122

53123
You can programmatically fingerprint audio files, compare fingerprints and identify music using the following packages:
54124

@@ -73,5 +143,4 @@ repository for more information:
73143
74144
## References
75145

76-
* https://ffmpeg.org/doxygen/6.1/index.html
77-
146+
* https://ffmpeg.org/doxygen/6.1/index.html

cmd/cli/decode.go

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,61 @@
11
package main
22

33
import (
4-
"encoding/json"
4+
"context"
55
"fmt"
6+
"os"
67

78
// Packages
9+
"github.com/djthorpe/go-tablewriter"
810
"github.com/mutablelogic/go-media"
911
)
1012

1113
type DecodeCmd struct {
12-
Path string `arg:"" required:"" help:"Media file" type:"path"`
14+
Path string `arg:"" required:"" help:"Media file" type:"path"`
15+
Format string `name:"format" short:"f" help:"Format of input file (name, .extension or mimetype)" type:"string"`
16+
Audio *bool `name:"audio" short:"a" help:"Output raw audio stream" type:"bool"`
17+
Video *bool `name:"video" short:"v" help:"Output raw video stream" type:"bool"`
1318
}
1419

1520
func (cmd *DecodeCmd) Run(globals *Globals) error {
16-
reader, err := media.Open(cmd.Path, "")
21+
var format media.Format
22+
23+
manager := media.NewManager()
24+
if cmd.Format != "" {
25+
if formats := manager.InputFormats(media.NONE, cmd.Format); len(formats) == 0 {
26+
return fmt.Errorf("unknown format %q", cmd.Format)
27+
} else if len(formats) > 1 {
28+
return fmt.Errorf("ambiguous format %q", cmd.Format)
29+
} else {
30+
format = formats[0]
31+
}
32+
}
33+
34+
// Open media file
35+
reader, err := manager.Open(cmd.Path, format)
1736
if err != nil {
1837
return err
1938
}
2039
defer reader.Close()
2140

22-
data, _ := json.MarshalIndent(reader, "", " ")
23-
fmt.Println(string(data))
41+
// Create a decoder - copy streams
42+
decoder, err := reader.Decoder(nil)
43+
if err != nil {
44+
return err
45+
}
2446

25-
return nil
47+
// Demultiplex the stream
48+
header := []tablewriter.TableOpt{tablewriter.OptHeader()}
49+
tablewriter := tablewriter.New(os.Stdout, tablewriter.OptOutputText())
50+
return decoder.Demux(context.Background(), func(packet media.Packet) error {
51+
if packet == nil {
52+
return nil
53+
}
54+
if err := tablewriter.Write(packet, header...); err != nil {
55+
return err
56+
}
57+
// Reset the header
58+
header = header[:0]
59+
return nil
60+
})
2661
}

cmd/cli/demuxers.go

Lines changed: 0 additions & 26 deletions
This file was deleted.

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 & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +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-
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"`
2226
}
2327

2428
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, "")
17+
manager := media.NewManager()
18+
reader, err := manager.Open(cmd.Path, nil)
1819
if err != nil {
1920
return err
2021
}

cmd/cli/muxers.go

Lines changed: 0 additions & 26 deletions
This file was deleted.

cmd/cli/muxers_demuxers.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
// Packages
8+
"github.com/djthorpe/go-tablewriter"
9+
"github.com/mutablelogic/go-media"
10+
)
11+
12+
type DemuxersCmd struct {
13+
Filter string `arg:"" optional:"" help:"Filter by mimetype, name or .ext" type:"string"`
14+
}
15+
16+
type MuxersCmd struct {
17+
Filter string `arg:"" optional:"" help:"Filter by mimetype, name or .ext" type:"string"`
18+
}
19+
20+
func (cmd *MuxersCmd) Run(globals *Globals) error {
21+
manager := media.NewManager()
22+
var formats []media.Format
23+
if cmd.Filter == "" {
24+
formats = manager.OutputFormats(media.ANY)
25+
} else {
26+
formats = manager.OutputFormats(media.ANY, cmd.Filter)
27+
}
28+
return Run(cmd.Filter, formats)
29+
}
30+
31+
func (cmd *DemuxersCmd) Run(globals *Globals) error {
32+
manager := media.NewManager()
33+
var formats []media.Format
34+
if cmd.Filter == "" {
35+
formats = manager.InputFormats(media.ANY)
36+
} else {
37+
formats = manager.InputFormats(media.ANY, cmd.Filter)
38+
}
39+
return Run(cmd.Filter, formats)
40+
}
41+
42+
func Run(filter string, formats []media.Format) error {
43+
writer := tablewriter.New(os.Stdout, tablewriter.OptHeader(), tablewriter.OptOutputText())
44+
if len(formats) == 0 {
45+
fmt.Printf("No (de)muxers found for %q\n", filter)
46+
return nil
47+
} else {
48+
return writer.Write(formats)
49+
}
50+
}

cmd/cli/probe.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"regexp"
7+
8+
// Packages
9+
"github.com/mutablelogic/go-media"
10+
)
11+
12+
type ProbeCmd struct {
13+
Path string `arg:"" required:"" help:"Media file or device name" type:"string"`
14+
Opts string `name:"opts" short:"o" help:"Options for opening the media file or device, (ie, \"framerate=30 video_size=176x144\")"`
15+
}
16+
17+
var (
18+
reDevice = regexp.MustCompile(`^([a-zA-Z0-9]+):(.*)$`)
19+
)
20+
21+
func (cmd *ProbeCmd) Run(globals *Globals) error {
22+
var format media.Format
23+
24+
manager := media.NewManager()
25+
filter := media.NONE
26+
27+
// Try device first
28+
if m := reDevice.FindStringSubmatch(cmd.Path); m != nil {
29+
cmd.Path = m[2]
30+
fmts := manager.InputFormats(filter|media.DEVICE, m[1])
31+
if len(fmts) > 0 {
32+
format = fmts[0]
33+
}
34+
}
35+
36+
// Open the media file or device
37+
reader, err := manager.Open(cmd.Path, format, cmd.Opts)
38+
if err != nil {
39+
return err
40+
}
41+
defer reader.Close()
42+
43+
// Print out probe data
44+
data, _ := json.MarshalIndent(reader, "", " ")
45+
fmt.Println(string(data))
46+
47+
// Return success
48+
return nil
49+
}

0 commit comments

Comments
 (0)