Skip to content

Commit 05e7580

Browse files
committed
Updated codec
1 parent f31b51e commit 05e7580

File tree

4 files changed

+121
-45
lines changed

4 files changed

+121
-45
lines changed

pkg/avcodec/codec.go

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package avcodec
22

33
import (
44
"encoding/json"
5+
"fmt"
56

67
// Packages
7-
88
media "github.com/mutablelogic/go-media"
99
metadata "github.com/mutablelogic/go-media/pkg/metadata"
1010
ff "github.com/mutablelogic/go-media/sys/ffmpeg71"
@@ -57,64 +57,60 @@ func Codecs(t media.Type) []media.Metadata {
5757
return result
5858
}
5959

60-
// Return an encoder by name, with additional options. Call Close() to
61-
// release the codec context. Codec options are listed at
60+
// NewEncodingCodec returns an encoder by name. Options for encoding can be passed.
61+
// Call Close() to release the codec. Codec options are listed at
6262
// <https://ffmpeg.org/ffmpeg-codecs.html>
63-
func NewEncoder(name string, opts ...Opt) (*Codec, error) {
64-
ctx := new(Codec)
65-
66-
// Options
67-
o, err := applyOptions(opts)
68-
if err != nil {
69-
return nil, err
70-
}
71-
72-
// Codec context
73-
if codec := ff.AVCodec_find_encoder_by_name(name); codec == nil {
74-
return nil, media.ErrBadParameter.Withf("unknown codec %q", name)
75-
} else if context := ff.AVCodec_alloc_context(codec); context == nil {
76-
return nil, media.ErrInternalError.Withf("failed to allocate codec context for %q", name)
77-
} else if err := set_par(context, codec, o); err != nil {
78-
ff.AVCodec_free_context(context)
79-
return nil, err
80-
} else if ff.AVCodec_open(context, codec, nil); err != nil {
81-
ff.AVCodec_free_context(context)
82-
return nil, err
83-
} else {
84-
ctx.context = context
85-
}
86-
87-
// Return success
88-
return ctx, nil
63+
func NewEncodingCodec(name string, opts ...Opt) (*Codec, error) {
64+
return newCodec(ff.AVCodec_find_encoder_by_name(name), opts...)
8965
}
9066

91-
// Return a decoder by name, with additional options. Call Close() to
92-
// release the codec context. Codec options are listed at
67+
// NewDecodingCodec returns a decoder by name. Options for decoding can be passed.
68+
// Call Close() to release the codec context. Codec options are listed at
9369
// <https://ffmpeg.org/ffmpeg-codecs.html>
94-
func NewDecoder(name string, opts ...Opt) (*Codec, error) {
70+
func NewDecodingCodec(name string, opts ...Opt) (*Codec, error) {
71+
return newCodec(ff.AVCodec_find_decoder_by_name(name), opts...)
72+
}
73+
74+
func newCodec(codec *ff.AVCodec, opts ...Opt) (*Codec, error) {
9575
ctx := new(Codec)
9676

97-
// Options
77+
// Check parameters
78+
if codec == nil {
79+
return nil, media.ErrBadParameter.Withf("unknown codec %q", codec.Name())
80+
}
81+
82+
// Apply options
9883
o, err := applyOptions(opts)
9984
if err != nil {
10085
return nil, err
10186
}
10287

88+
// Create a dictionary of codec options
89+
dict := ff.AVUtil_dict_alloc()
90+
defer ff.AVUtil_dict_free(dict)
91+
for _, opt := range o.meta {
92+
if err := ff.AVUtil_dict_set(dict, opt.Key(), opt.Value(), ff.AV_DICT_APPEND); err != nil {
93+
ff.AVUtil_dict_free(dict)
94+
return nil, err
95+
}
96+
}
97+
10398
// Codec context
104-
if codec := ff.AVCodec_find_decoder_by_name(name); codec == nil {
105-
return nil, media.ErrBadParameter.Withf("unknown codec %q", name)
106-
} else if context := ff.AVCodec_alloc_context(codec); context == nil {
107-
return nil, media.ErrInternalError.Withf("failed to allocate codec context for %q", name)
99+
if context := ff.AVCodec_alloc_context(codec); context == nil {
100+
return nil, media.ErrInternalError.Withf("failed to allocate codec context for %q", codec.Name())
108101
} else if err := set_par(context, codec, o); err != nil {
109102
ff.AVCodec_free_context(context)
110103
return nil, err
111-
} else if ff.AVCodec_open(context, codec, nil); err != nil {
104+
} else if err := ff.AVCodec_open(context, codec, dict); err != nil {
112105
ff.AVCodec_free_context(context)
113106
return nil, err
114107
} else {
115108
ctx.context = context
116109
}
117110

111+
// TODO: Get the options which were not consumed
112+
fmt.Println("TODO: Codec options not consumed:", dict)
113+
118114
// Return success
119115
return ctx, nil
120116
}

pkg/avcodec/codec_test.go

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@ func Test_codec_001(t *testing.T) {
1616
assert.NotNil(codecs)
1717
for _, meta := range codecs {
1818
assert.NotNil(meta)
19-
codec, err := avcodec.NewEncoder(meta.Key())
20-
defer codec.Close()
21-
19+
codec, err := avcodec.NewEncoder(meta.Key(), avcodec.WithSampleRate(22050))
2220
if assert.NoError(err) {
2321
assert.NotNil(codec)
24-
assert.Equal(media.AUDIO, codec.Type())
22+
assert.Equal(media.OUTPUT|media.AUDIO, codec.Type())
2523
t.Log(codec)
24+
assert.NoError(codec.Close())
2625
}
2726
}
2827
}
@@ -34,13 +33,46 @@ func Test_codec_002(t *testing.T) {
3433
assert.NotNil(codecs)
3534
for _, meta := range codecs {
3635
assert.NotNil(meta)
37-
codec, err := avcodec.NewEncoder(meta.Key())
38-
defer codec.Close()
36+
codec, err := avcodec.NewEncoder(meta.Key(), avcodec.WithFrameRate(1, 25), avcodec.WithFrameSize("hd720"))
37+
if assert.NoError(err) {
38+
assert.NotNil(codec)
39+
assert.Equal(media.OUTPUT|media.VIDEO, codec.Type())
40+
t.Log(codec)
41+
assert.NoError(codec.Close())
42+
}
43+
}
44+
}
45+
46+
func Test_codec_003(t *testing.T) {
47+
assert := assert.New(t)
48+
49+
codecs := avcodec.Codecs(media.INPUT | media.AUDIO)
50+
assert.NotNil(codecs)
51+
for _, meta := range codecs {
52+
assert.NotNil(meta)
53+
codec, err := avcodec.NewDecoder(meta.Key())
54+
if assert.NoError(err) {
55+
assert.NotNil(codec)
56+
assert.Equal(media.INPUT|media.AUDIO, codec.Type())
57+
t.Log(codec)
58+
assert.NoError(codec.Close())
59+
}
60+
}
61+
}
62+
63+
func Test_codec_004(t *testing.T) {
64+
assert := assert.New(t)
3965

66+
codecs := avcodec.Codecs(media.INPUT | media.VIDEO)
67+
assert.NotNil(codecs)
68+
for _, meta := range codecs {
69+
assert.NotNil(meta)
70+
codec, err := avcodec.NewDecoder(meta.Key(), avcodec.WithFrameSize("hd720"))
4071
if assert.NoError(err) {
4172
assert.NotNil(codec)
42-
assert.Equal(media.VIDEO, codec.Type())
73+
assert.Equal(media.INPUT|media.VIDEO, codec.Type())
4374
t.Log(codec)
75+
assert.NoError(codec.Close())
4476
}
4577
}
4678
}

pkg/avcodec/encode.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package avcodec
2+
3+
import (
4+
5+
// Packages
6+
ff "github.com/mutablelogic/go-media/sys/ffmpeg71"
7+
)
8+
9+
///////////////////////////////////////////////////////////////////////////////
10+
// TYPES
11+
12+
type EncodingContext struct {
13+
codec *Codec
14+
stream *ff.AVStream
15+
packet *ff.AVPacket
16+
}
17+
18+
////////////////////////////////////////////////////////////////////////////////
19+
// LIFECYCLE
20+
21+
// Create an encoding context (a stream and a packet) with the given codec
22+
func NewEncodingContext(codec *Codec) (*Encoder, error) {
23+
24+
}

pkg/avcodec/opt.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package avcodec
33
import (
44
// Packages
55
media "github.com/mutablelogic/go-media"
6+
metadata "github.com/mutablelogic/go-media/pkg/metadata"
67
ff "github.com/mutablelogic/go-media/sys/ffmpeg71"
78
)
89

@@ -20,6 +21,9 @@ type opt struct {
2021
pixel_format ff.AVPixelFormat
2122
frame_rate ff.AVRational
2223
pixel_ratio ff.AVRational
24+
25+
// Codec options
26+
meta []*metadata.Metadata
2327
}
2428

2529
type Opt func(*opt) error
@@ -42,6 +46,13 @@ func applyOptions(opts []Opt) (*opt, error) {
4246
///////////////////////////////////////////////////////////////////////////////
4347
// PUBLIC METHODS
4448

49+
func WithOpt(key string, value any) Opt {
50+
return func(o *opt) error {
51+
o.meta = append(o.meta, metadata.New(key, value))
52+
return nil
53+
}
54+
}
55+
4556
func WithChannelLayout(v string) Opt {
4657
return func(o *opt) error {
4758
if err := ff.AVUtil_channel_layout_from_string(&o.channel_layout, v); err != nil {
@@ -96,3 +107,16 @@ func WithFrameSize(v string) Opt {
96107
return nil
97108
}
98109
}
110+
111+
func WithFrameRate(num, den int) Opt {
112+
return func(o *opt) error {
113+
o.frame_rate = ff.AVUtil_rational(num, den)
114+
if o.frame_rate.IsZero() {
115+
return media.ErrBadParameter.Withf("zero frame rate %v/%v", num, den)
116+
}
117+
if o.frame_rate.Num() <= 0 || o.frame_rate.Den() <= 0 {
118+
return media.ErrBadParameter.Withf("negative frame rate %v/%v", num, den)
119+
}
120+
return nil
121+
}
122+
}

0 commit comments

Comments
 (0)