Skip to content

Commit c932e17

Browse files
committed
Updates for encoder
1 parent ab3159d commit c932e17

File tree

15 files changed

+814
-178
lines changed

15 files changed

+814
-178
lines changed

pkg/ffmpeg/encoder.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
package ffmpeg
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"syscall"
9+
10+
// Packages
11+
ff "github.com/mutablelogic/go-media/sys/ffmpeg61"
12+
13+
// Namespace imports
14+
. "github.com/djthorpe/go-errors"
15+
)
16+
17+
////////////////////////////////////////////////////////////////////////////////
18+
// TYPES
19+
20+
type Encoder struct {
21+
ctx *ff.AVCodecContext
22+
stream *ff.AVStream
23+
packet *ff.AVPacket
24+
//next_pts int64
25+
}
26+
27+
// EncoderFrameFn is a function which is called to receive a frame to encode. It should
28+
// return nil to continue encoding or io.EOF to stop encoding.
29+
type EncoderFrameFn func(int) (*ff.AVFrame, error)
30+
31+
// EncoderPacketFn is a function which is called for each packet encoded. It should
32+
// return nil to continue encoding or io.EOF to stop encoding immediately.
33+
type EncoderPacketFn func(*ff.AVPacket) error
34+
35+
////////////////////////////////////////////////////////////////////////////////
36+
// LIFECYCLE
37+
38+
// Create an encoder with the given parameters
39+
func NewEncoder(ctx *ff.AVFormatContext, stream int, par *Par) (*Encoder, error) {
40+
encoder := new(Encoder)
41+
42+
// Get codec
43+
codec_id := ff.AV_CODEC_ID_NONE
44+
switch par.CodecType() {
45+
case ff.AVMEDIA_TYPE_AUDIO:
46+
codec_id = ctx.Output().AudioCodec()
47+
case ff.AVMEDIA_TYPE_VIDEO:
48+
codec_id = ctx.Output().VideoCodec()
49+
case ff.AVMEDIA_TYPE_SUBTITLE:
50+
codec_id = ctx.Output().SubtitleCodec()
51+
}
52+
if codec_id == ff.AV_CODEC_ID_NONE {
53+
return nil, ErrBadParameter.Withf("no codec specified for stream %v", stream)
54+
}
55+
56+
// Allocate codec
57+
codec := ff.AVCodec_find_encoder(codec_id)
58+
if codec == nil {
59+
return nil, ErrBadParameter.Withf("codec %q cannot encode", codec_id)
60+
}
61+
if codecctx := ff.AVCodec_alloc_context(codec); codecctx == nil {
62+
return nil, ErrInternalAppError.With("could not allocate audio codec context")
63+
} else {
64+
encoder.ctx = codecctx
65+
}
66+
67+
// Check codec against parameters and set defaults as needed, then
68+
// copy back to codec
69+
if err := par.ValidateFromCodec(encoder.ctx); err != nil {
70+
ff.AVCodec_free_context(encoder.ctx)
71+
return nil, err
72+
} else if err := par.CopyToCodec(encoder.ctx); err != nil {
73+
ff.AVCodec_free_context(encoder.ctx)
74+
return nil, err
75+
}
76+
77+
// Create the stream
78+
if streamctx := ff.AVFormat_new_stream(ctx, nil); streamctx == nil {
79+
ff.AVCodec_free_context(encoder.ctx)
80+
return nil, ErrInternalAppError.With("could not allocate stream")
81+
} else {
82+
streamctx.SetId(stream)
83+
encoder.stream = streamctx
84+
}
85+
86+
// Copy parameters to stream
87+
if err := ff.AVCodec_parameters_from_context(encoder.stream.CodecPar(), encoder.ctx); err != nil {
88+
ff.AVCodec_free_context(encoder.ctx)
89+
return nil, err
90+
}
91+
92+
// Some formats want stream headers to be separate.
93+
if ctx.Output().Flags().Is(ff.AVFMT_GLOBALHEADER) {
94+
encoder.ctx.SetFlags(encoder.ctx.Flags() | ff.AV_CODEC_FLAG_GLOBAL_HEADER)
95+
}
96+
97+
// Open it
98+
if err := ff.AVCodec_open(encoder.ctx, codec, nil); err != nil {
99+
ff.AVCodec_free_context(encoder.ctx)
100+
return nil, ErrInternalAppError.Withf("codec_open: %v", err)
101+
}
102+
103+
// Create a packet
104+
packet := ff.AVCodec_packet_alloc()
105+
if packet == nil {
106+
ff.AVCodec_free_context(encoder.ctx)
107+
return nil, errors.New("failed to allocate packet")
108+
} else {
109+
encoder.packet = packet
110+
}
111+
112+
// Return it
113+
return encoder, nil
114+
}
115+
116+
func (encoder *Encoder) Close() error {
117+
// Free respurces
118+
if encoder.ctx != nil {
119+
ff.AVCodec_free_context(encoder.ctx)
120+
}
121+
if encoder.packet != nil {
122+
ff.AVCodec_packet_free(encoder.packet)
123+
}
124+
125+
// Release resources
126+
encoder.packet = nil
127+
encoder.stream = nil
128+
encoder.ctx = nil
129+
130+
// Return success
131+
return nil
132+
}
133+
134+
////////////////////////////////////////////////////////////////////////////////
135+
// STRINGIFY
136+
137+
func (e *Encoder) MarshalJSON() ([]byte, error) {
138+
type jsonEncoder struct {
139+
Codec *ff.AVCodecContext `json:"codec"`
140+
Stream *ff.AVStream `json:"stream"`
141+
}
142+
return json.Marshal(&jsonEncoder{
143+
Codec: e.ctx,
144+
Stream: e.stream,
145+
})
146+
}
147+
148+
func (e *Encoder) String() string {
149+
data, _ := json.MarshalIndent(e, "", " ")
150+
return string(data)
151+
}
152+
153+
//////////////////////////////////////////////////////////////////////////////
154+
// PUBLIC METHODS
155+
156+
// Encode a frame and pass packets to the EncoderPacketFn. If the frame is nil, then
157+
// the encoder will flush any remaining packets. If io.EOF is returned then
158+
// it indicates that the encoder has ended prematurely.
159+
func (e *Encoder) Encode(frame *ff.AVFrame, fn EncoderPacketFn) error {
160+
if fn == nil {
161+
return ErrBadParameter.With("nil fn")
162+
}
163+
// Encode a frame (or flush the encoder)
164+
return e.encode(frame, fn)
165+
}
166+
167+
// Return the codec parameters
168+
func (e *Encoder) Par() *Par {
169+
par := new(Par)
170+
if err := ff.AVCodec_parameters_from_context(&par.AVCodecParameters, e.ctx); err != nil {
171+
return nil
172+
} else {
173+
return par
174+
}
175+
}
176+
177+
//////////////////////////////////////////////////////////////////////////////
178+
// PRIVATE METHODS
179+
180+
func (e *Encoder) encode(frame *ff.AVFrame, fn EncoderPacketFn) error {
181+
// Send the frame to the encoder
182+
fmt.Println("Sending frame", frame)
183+
if err := ff.AVCodec_send_frame(e.ctx, frame); err != nil {
184+
if errors.Is(err, syscall.EAGAIN) || errors.Is(err, io.EOF) {
185+
return nil
186+
}
187+
return err
188+
}
189+
190+
// Write out the packets
191+
var result error
192+
for {
193+
// Receive the packet
194+
fmt.Println("Receiving packet")
195+
if err := ff.AVCodec_receive_packet(e.ctx, e.packet); errors.Is(err, syscall.EAGAIN) || errors.Is(err, io.EOF) {
196+
// Finished receiving packet or EOF
197+
break
198+
} else if err != nil {
199+
return err
200+
}
201+
202+
// Pass back to the caller
203+
if err := fn(e.packet); errors.Is(err, io.EOF) {
204+
// End early, return EOF
205+
result = io.EOF
206+
break
207+
} else if err != nil {
208+
return err
209+
}
210+
211+
// Re-allocate frames for next iteration
212+
ff.AVCodec_packet_unref(e.packet)
213+
}
214+
215+
// Flush
216+
if result == nil {
217+
result = fn(nil)
218+
}
219+
220+
// Return success or EOF
221+
return result
222+
}

pkg/ffmpeg/opts.go

Lines changed: 81 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,23 @@ type Opt func(*opts) error
1515
type opts struct {
1616
// Resample/resize options
1717
force bool
18+
par *Par
1819

1920
// Format options
2021
oformat *ffmpeg.AVOutputFormat
2122

22-
// Audio options
23-
sample_fmt ffmpeg.AVSampleFormat
24-
ch ffmpeg.AVChannelLayout
25-
samplerate int
23+
// Stream options
24+
streams map[int]*Par
25+
}
26+
27+
////////////////////////////////////////////////////////////////////////////////
28+
// LIFECYCLE
2629

27-
// Video options
28-
pix_fmt ffmpeg.AVPixelFormat
29-
width, height int
30+
func newOpts() *opts {
31+
return &opts{
32+
par: new(Par),
33+
streams: make(map[int]*Par),
34+
}
3035
}
3136

3237
////////////////////////////////////////////////////////////////////////////////
@@ -45,6 +50,50 @@ func OptOutputFormat(name string) Opt {
4550
}
4651
}
4752

53+
// New audio stream with parameters
54+
func OptAudioStream(stream int, par *Par) Opt {
55+
return func(o *opts) error {
56+
if par == nil || par.CodecType() != ffmpeg.AVMEDIA_TYPE_AUDIO {
57+
return ErrBadParameter.With("invalid audio parameters")
58+
}
59+
if stream == 0 {
60+
stream = len(o.streams) + 1
61+
}
62+
if _, exists := o.streams[stream]; exists {
63+
return ErrDuplicateEntry.Withf("stream %v", stream)
64+
}
65+
if stream < 0 {
66+
return ErrBadParameter.Withf("invalid stream %v", stream)
67+
}
68+
o.streams[stream] = par
69+
70+
// Return success
71+
return nil
72+
}
73+
}
74+
75+
// New video stream with parameters
76+
func OptVideoStream(stream int, par *Par) Opt {
77+
return func(o *opts) error {
78+
if par == nil || par.CodecType() != ffmpeg.AVMEDIA_TYPE_VIDEO {
79+
return ErrBadParameter.With("invalid video parameters")
80+
}
81+
if stream == 0 {
82+
stream = len(o.streams) + 1
83+
}
84+
if _, exists := o.streams[stream]; exists {
85+
return ErrDuplicateEntry.Withf("stream %v", stream)
86+
}
87+
if stream < 0 {
88+
return ErrBadParameter.Withf("invalid stream %v", stream)
89+
}
90+
o.streams[stream] = par
91+
92+
// Return success
93+
return nil
94+
}
95+
}
96+
4897
// Force resampling and resizing on decode, even if the input and output
4998
// parameters are the same
5099
func OptForce() Opt {
@@ -61,7 +110,8 @@ func OptPixFormat(format string) Opt {
61110
if fmt == ffmpeg.AV_PIX_FMT_NONE {
62111
return ErrBadParameter.Withf("invalid pixel format %q", format)
63112
}
64-
o.pix_fmt = fmt
113+
o.par.SetCodecType(ffmpeg.AVMEDIA_TYPE_VIDEO)
114+
o.par.SetPixelFormat(fmt)
65115
return nil
66116
}
67117
}
@@ -72,8 +122,9 @@ func OptWidthHeight(w, h int) Opt {
72122
if w <= 0 || h <= 0 {
73123
return ErrBadParameter.Withf("invalid width %v or height %v", w, h)
74124
}
75-
o.width = w
76-
o.height = h
125+
o.par.SetCodecType(ffmpeg.AVMEDIA_TYPE_VIDEO)
126+
o.par.SetWidth(w)
127+
o.par.SetHeight(h)
77128
return nil
78129
}
79130
}
@@ -85,27 +136,35 @@ func OptFrameSize(size string) Opt {
85136
if err != nil {
86137
return ErrBadParameter.Withf("invalid frame size %q", size)
87138
}
88-
o.width = w
89-
o.height = h
139+
o.par.SetCodecType(ffmpeg.AVMEDIA_TYPE_VIDEO)
140+
o.par.SetWidth(w)
141+
o.par.SetHeight(h)
90142
return nil
91143
}
92144
}
93145

94146
// Channel layout
95147
func OptChannelLayout(layout string) Opt {
96148
return func(o *opts) error {
97-
return ffmpeg.AVUtil_channel_layout_from_string(&o.ch, layout)
149+
var ch ffmpeg.AVChannelLayout
150+
if err := ffmpeg.AVUtil_channel_layout_from_string(&ch, layout); err != nil {
151+
return ErrBadParameter.Withf("invalid channel layout %q", layout)
152+
}
153+
o.par.SetCodecType(ffmpeg.AVMEDIA_TYPE_AUDIO)
154+
return o.par.SetChannelLayout(ch)
98155
}
99156
}
100157

101158
// Nuumber of channels
102-
func OptChannels(ch int) Opt {
159+
func OptChannels(num int) Opt {
103160
return func(o *opts) error {
104-
if ch <= 0 || ch > 64 {
105-
return ErrBadParameter.Withf("invalid number of channels %v", ch)
161+
var ch ffmpeg.AVChannelLayout
162+
if num <= 0 || num > 64 {
163+
return ErrBadParameter.Withf("invalid number of channels %v", num)
106164
}
107-
ffmpeg.AVUtil_channel_layout_default(&o.ch, ch)
108-
return nil
165+
ffmpeg.AVUtil_channel_layout_default(&ch, num)
166+
o.par.SetCodecType(ffmpeg.AVMEDIA_TYPE_AUDIO)
167+
return o.par.SetChannelLayout(ch)
109168
}
110169
}
111170

@@ -115,7 +174,8 @@ func OptSampleRate(rate int) Opt {
115174
if rate <= 0 {
116175
return ErrBadParameter.Withf("invalid sample rate %v", rate)
117176
}
118-
o.samplerate = rate
177+
o.par.SetCodecType(ffmpeg.AVMEDIA_TYPE_AUDIO)
178+
o.par.SetSamplerate(rate)
119179
return nil
120180
}
121181
}
@@ -127,7 +187,8 @@ func OptSampleFormat(format string) Opt {
127187
if fmt == ffmpeg.AV_SAMPLE_FMT_NONE {
128188
return ErrBadParameter.Withf("invalid sample format %q", format)
129189
}
130-
o.sample_fmt = fmt
190+
o.par.SetCodecType(ffmpeg.AVMEDIA_TYPE_AUDIO)
191+
o.par.SetSampleFormat(fmt)
131192
return nil
132193
}
133194
}

0 commit comments

Comments
 (0)