Skip to content

Commit 2d6df88

Browse files
committed
Updated
1 parent c7dca06 commit 2d6df88

File tree

7 files changed

+142
-44
lines changed

7 files changed

+142
-44
lines changed

Makefile

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ DOCKER=$(shell which docker)
66
FFMPEG_VERSION=ffmpeg-7.1.1
77
CHROMAPRINT_VERSION=chromaprint-1.5.1
88

9+
# CGO configuration - set CGO vars for C++ libraries
10+
CGO_ENV=PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" CGO_LDFLAGS="-lstdc++"
11+
912
# Build flags
1013
BUILD_MODULE := $(shell cat go.mod | head -1 | cut -d ' ' -f 2)
1114
BUILD_LD_FLAGS += -X $(BUILD_MODULE)/pkg/version.GitSource=${BUILD_MODULE}
@@ -39,11 +42,11 @@ cmds: $(CMD_DIR)
3942
.PHONY: cli
4043
cli: go-dep go-tidy mkdir
4144
@echo Build media tool
42-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/media ./cmd/media
45+
@${CGO_ENV} ${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/media ./cmd/media
4346

4447
$(CMD_DIR): go-dep go-tidy mkdir
4548
@echo Build cmd $(notdir $@)
46-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@) ./$@
49+
@${CGO_ENV} ${GO} build ${BUILD_FLAGS} -o ${BUILD_DIR}/$(notdir $@) ./$@
4750

4851
###############################################################################
4952
# FFMPEG
@@ -100,6 +103,7 @@ ${BUILD_DIR}/${CHROMAPRINT_VERSION}:
100103
chromaprint-configure: mkdir ${BUILD_DIR}/${CHROMAPRINT_VERSION}
101104
@echo "Configuring ${CHROMAPRINT_VERSION} => ${PREFIX}"
102105
cmake \
106+
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
103107
-DCMAKE_BUILD_TYPE=Release \
104108
-DBUILD_SHARED_LIBS=0 \
105109
-DBUILD_TESTS=0 \
@@ -115,10 +119,13 @@ chromaprint-build: chromaprint-configure
115119
@cd $(BUILD_DIR) && make -j2
116120

117121
# Install chromaprint
122+
# Create a modified pkg-config file that ensures correct linking order for C++
118123
.PHONY: chromaprint
119124
chromaprint: chromaprint-build
120125
@echo "Installing ${CHROMAPRINT_VERSION} => ${PREFIX}"
121126
@cd $(BUILD_DIR) && make install
127+
@sed -i.bak 's/Libs: -L\${libdir} -lchromaprint/Libs: -L\${libdir} -lchromaprint -lstdc++/g' "${PREFIX}/lib/pkgconfig/libchromaprint.pc"
128+
@rm -f "${PREFIX}/lib/pkgconfig/libchromaprint.pc.bak"
122129

123130
###############################################################################
124131
# DOCKER
@@ -147,11 +154,11 @@ test: test-ffmpeg
147154
test-ffmpeg: go-dep go-tidy ffmpeg chromaprint
148155
@echo Test
149156
@echo ... test sys/ffmpeg71
150-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} test ./sys/ffmpeg71
157+
@${CGO_ENV} ${GO} test ./sys/ffmpeg71
151158
@echo ... test pkg/segmenter
152-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} test ./pkg/segmenter
159+
@${CGO_ENV} ${GO} test ./pkg/segmenter
153160
@echo ... test pkg/chromaprint
154-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} test ./pkg/chromaprint
161+
@${CGO_ENV} ${GO} test ./pkg/chromaprint
155162

156163

157164
# @echo ... test pkg/ffmpeg
@@ -172,11 +179,11 @@ test-ffmpeg: go-dep go-tidy ffmpeg chromaprint
172179
container-test: go-dep go-tidy ffmpeg chromaprint
173180
@echo Test
174181
@echo ... test sys/ffmpeg71
175-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} test ./sys/ffmpeg71
182+
@${CGO_ENV} ${GO} test ./sys/ffmpeg71
176183
@echo ... test pkg/segmenter
177-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} test ./pkg/segmenter
184+
@${CGO_ENV} ${GO} test ./pkg/segmenter
178185
@echo ... test pkg/chromaprint
179-
@PKG_CONFIG_PATH="$(shell realpath ${PREFIX})/lib/pkgconfig" CGO_LDFLAGS_ALLOW="-(W|D).*" ${GO} test ./pkg/chromaprint
186+
@${CGO_ENV} ${GO} test ./pkg/chromaprint
180187

181188
###############################################################################
182189
# DEPENDENCIES, ETC

error.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ type Err uint
1414
// GLOBALS
1515

1616
const (
17-
ErrBadParameter Err = http.StatusBadRequest
18-
ErrInternalError Err = http.StatusInternalServerError
17+
ErrBadParameter Err = http.StatusBadRequest
18+
ErrInternalError Err = http.StatusInternalServerError
19+
ErrNotImplemented Err = http.StatusNotImplemented
1920
)
2021

2122
///////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,8 @@ func (code Err) Error() string {
3132
return "bad parameter"
3233
case ErrInternalError:
3334
return "internal error"
35+
case ErrNotImplemented:
36+
return "not implemented"
3437
default:
3538
return fmt.Sprintf("error code %d", code.Code())
3639
}

manager.go

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ functions to determine capabilities and manage media files and devices.
55
*/
66
package media
77

8-
import "io"
8+
import (
9+
"context"
10+
"io"
11+
)
912

1013
// Manager represents a manager for media formats and devices.
1114
// Create a new manager object using the NewManager function.
@@ -34,36 +37,36 @@ type Manager interface {
3437
// specified, then the format will be used to open the file. Close the
3538
// media object when done. It is the responsibility of the caller to
3639
// also close the reader when done.
37-
//Read(io.Reader, Format, ...string) (Media, error)
40+
Read(io.Reader, Format, ...string) (Media, error)
3841

3942
// Create a media file or device for writing, from a path. If a format is
4043
// specified, then the format will be used to create the file or else
4144
// the format is guessed from the path. If no parameters are provided,
4245
// then the default parameters for the format are used.
43-
//Create(string, Format, []Metadata, ...Parameters) (Media, error)
46+
Create(string, Format, []Metadata, ...Par) (Media, error)
4447

4548
// Create a media stream for writing. The format will be used to
4649
// determine the format and one or more CodecParameters used to
4750
// create the streams. If no parameters are provided, then the
4851
// default parameters for the format are used. It is the responsibility
4952
// of the caller to also close the writer when done.
50-
//Write(io.Writer, Format, []Metadata, ...Parameters) (Media, error)
53+
//Write(io.Writer, Format, []Metadata, ...Par) (Media, error)
5154

5255
// Return audio parameters for encoding
5356
// ChannelLayout, SampleFormat, Samplerate
54-
//AudioParameters(string, string, int) (Parameters, error)
57+
//AudioPar(string, string, int) (Par, error)
5558

5659
// Return video parameters for encoding
5760
// Width, Height, PixelFormat
58-
//VideoParameters(int, int, string) (Parameters, error)
61+
//VideoPar(int, int, string) (Par, error)
5962

6063
// Return codec parameters for audio encoding
6164
// Codec name and AudioParameters
62-
//AudioCodecParameters(string, AudioParameters) (Parameters, error)
65+
//AudioCodecParameters(string, AudioPar) (Par, error)
6366

6467
// Return codec parameters for video encoding
6568
// Codec name, Profile name, Framerate (fps) and VideoParameters
66-
//VideoCodecParameters(string, string, float64, VideoParameters) (Parameters, error)
69+
//VideoCodecParameters(string, string, float64, VideoPar) (Par, error)
6770

6871
// Return supported input and output container formats which match any filter,
6972
// which can be a name, extension (with preceeding period) or mimetype. The Type
@@ -95,9 +98,30 @@ type Manager interface {
9598

9699
// Log info messages with arguments
97100
Infof(string, ...any)
101+
102+
// Decode an input stream, determining the streams to be decoded
103+
// and the function to accept the decoded frames. If MapFunc is nil,
104+
// all streams are passed through (demultiplexing).
105+
Decode(context.Context, Media, MapFunc, FrameFunc) error
98106
}
99107

100-
// A container format for a media file or stream
108+
// MapFunc return parameters if a stream should be decoded,
109+
// resampled (for audio streams) or resized (for video streams).
110+
// Return nil if you want to ignore the stream, or pass back the
111+
// stream parameters if you want to copy the stream without any changes.
112+
type MapFunc func(int, Par) (Par, error)
113+
114+
// FrameFunc is a function which is called to send a frame after decoding. It should
115+
// return nil to continue decoding or io.EOF to stop.
116+
type FrameFunc func(int, Frame) error
117+
118+
// Parameters for a stream or frame
119+
type Par interface{}
120+
121+
// A frame of decoded data
122+
type Frame interface{}
123+
124+
// A container format for a media file
101125
type Format interface {
102126
// The type of the format, which can be combinations of
103127
// INPUT, OUTPUT, DEVICE, AUDIO, VIDEO and SUBTITLE

pkg/ffmpeg/manager.go

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ffmpeg
22

33
import (
4+
"context"
5+
"io"
46
"slices"
57
"strings"
68

@@ -69,35 +71,25 @@ func NewManager(opt ...Opt) (*Manager, error) {
6971
return manager, nil
7072
}
7173

72-
// Open a media file or device for reading, from a path or url.
73-
// If a format is specified, then the format will be used to open
74-
// the file. You can add additional options to the open call as
75-
// key=value pairs
76-
/*
74+
///////////////////////////////////////////////////////////////////////////////
75+
// PUBLIC METHODS - READER
76+
7777
func (manager *Manager) Open(url string, format media.Format, opts ...string) (media.Media, error) {
78-
opt := append([]Opt{OptInputOpt(opts...)}, manager.opts...)
78+
o := append([]Opt{}, manager.opts[:]...)
7979
if format != nil {
80-
opt = append(opt, OptInputFormat(format.Name()))
80+
if format_, ok := format.(*Format); ok && format_.Input != nil {
81+
o = append(o, optInputFormat(format_))
82+
} else {
83+
return nil, media.ErrBadParameter.With("invalid input format")
84+
}
8185
}
82-
return Open(url, opt...)
83-
}
84-
85-
// Open an io.Reader for reading. If a format is specified, then the
86-
// format will be used to open the file. You can add additional options
87-
// to the open call as key=value pairs
88-
func (manager *Manager) NewReader(r io.Reader, format media.Format, opts ...string) (media.Media, error) {
89-
opt := append([]Opt{OptInputOpt(opts...)}, manager.opts...)
90-
if format != nil {
91-
opt = append(opt, OptInputFormat(format.Name()))
86+
if len(opts) > 0 {
87+
o = append(o, OptInputOpt(opts...))
9288
}
93-
return NewReader(r, opt...)
89+
return Open(url, o...)
9490
}
95-
*/
96-
97-
///////////////////////////////////////////////////////////////////////////////
98-
// PUBLIC METHODS - READER
9991

100-
func (manager *Manager) Open(url string, format media.Format, opts ...string) (media.Media, error) {
92+
func (manager *Manager) Read(r io.Reader, format media.Format, opts ...string) (media.Media, error) {
10193
o := append([]Opt{}, manager.opts[:]...)
10294
if format != nil {
10395
if format_, ok := format.(*Format); ok && format_.Input != nil {
@@ -109,7 +101,40 @@ func (manager *Manager) Open(url string, format media.Format, opts ...string) (m
109101
if len(opts) > 0 {
110102
o = append(o, OptInputOpt(opts...))
111103
}
112-
return Open(url, o...)
104+
return NewReader(r, o...)
105+
}
106+
107+
///////////////////////////////////////////////////////////////////////////////
108+
// PUBLIC METHODS - WRITER
109+
110+
func (manager *Manager) Create(url string, format media.Format, meta []media.Metadata, streams ...media.Par) (media.Media, error) {
111+
o := slices.Clone(manager.opts[:])
112+
113+
// Append format
114+
if format != nil {
115+
if format_, ok := format.(*Format); ok && format_.Output != nil {
116+
o = append(o, optOutputFormat(format_))
117+
} else {
118+
return nil, media.ErrBadParameter.With("invalid output format")
119+
}
120+
}
121+
122+
// Append metadata
123+
for _, m := range meta {
124+
o = append(o, OptMetadata(NewMetadata(m.Key(), m.Any())))
125+
}
126+
127+
// Append streams
128+
for i, stream := range streams {
129+
par, ok := stream.(*Par)
130+
if !ok || par == nil {
131+
return nil, media.ErrBadParameter.With("invalid stream parameters")
132+
}
133+
o = append(o, OptStream(i, par))
134+
}
135+
136+
// Create the writer
137+
return Create(url, o...)
113138
}
114139

115140
///////////////////////////////////////////////////////////////////////////////
@@ -407,3 +432,25 @@ func (manager *Manager) Warningf(v string, args ...any) {
407432
func (manager *Manager) Infof(v string, args ...any) {
408433
ff.AVUtil_log(nil, ff.AV_LOG_INFO, v, args...)
409434
}
435+
436+
///////////////////////////////////////////////////////////////////////////////
437+
// PUBLIC METHODS - DECODING
438+
439+
func (manager *Manager) Decode(ctx context.Context, m media.Media, mapFunc media.MapFunc, frameFunc media.FrameFunc) error {
440+
return media.ErrNotImplemented.With("decoding not implemented")
441+
}
442+
443+
/*
444+
// Check if the media is valid
445+
if m == nil || !m.Type().Is(media.INPUT) {
446+
return media.ErrBadParameter.With("invalid media, cannot decode")
447+
}
448+
// Get the concrete reader object
449+
reader, ok := m.(*Reader)
450+
if !ok || reader == nil {
451+
return media.ErrBadParameter.With("invalid media, cannot decode")
452+
}
453+
// Perform the decode
454+
return reader.Decode(ctx, mapFunc, frameFunc)
455+
}
456+
*/

pkg/ffmpeg/opts.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ func optInputFormat(format *Format) Opt {
101101
}
102102
}
103103

104+
// Output format from ff.AVInputFormat
105+
func optOutputFormat(format *Format) Opt {
106+
return func(o *opts) error {
107+
if format != nil && format.Output != nil {
108+
o.oformat = format.Output
109+
} else {
110+
return ErrBadParameter.With("invalid output format")
111+
}
112+
return nil
113+
}
114+
}
115+
104116
// Input format options
105117
func OptInputOpt(opt ...string) Opt {
106118
return func(o *opts) error {

pkg/ffmpeg/writer.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"sort"
1111

1212
// Packages
13+
"github.com/mutablelogic/go-media"
1314
ff "github.com/mutablelogic/go-media/sys/ffmpeg71"
1415
maps "golang.org/x/exp/maps"
1516

@@ -227,6 +228,11 @@ func (w *Writer) String() string {
227228
//////////////////////////////////////////////////////////////////////////////
228229
// PUBLIC METHODS
229230

231+
// Return a "stream" for encoding
232+
func (w *Writer) Type() media.Type {
233+
return media.OUTPUT
234+
}
235+
230236
// Return a "stream" for encoding
231237
func (w *Writer) Stream(stream int) *Encoder {
232238
for _, encoder := range w.encoders {

sys/chromaprint/chromaprint.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
/*
1313
#cgo pkg-config: libchromaprint
14-
#cgo LDFLAGS: -lstdc++
1514
#cgo darwin LDFLAGS: -framework Accelerate
1615
#include <chromaprint.h>
1716
*/

0 commit comments

Comments
 (0)