Skip to content

Commit 5db5f2b

Browse files
committed
Start of mux method
1 parent dc97548 commit 5db5f2b

File tree

6 files changed

+92
-12
lines changed

6 files changed

+92
-12
lines changed

frame.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ func (frame *frame) Type() MediaType {
7171
return NONE
7272
}
7373

74+
// Id is unused
75+
func (frame *frame) Id() int {
76+
return 0
77+
}
78+
7479
// Return the timestamp as a duration, or minus one if not set
7580
func (frame *frame) Time() time.Duration {
7681
pts := frame.ctx.Pts()

interfaces.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,14 @@ type Media interface {
140140
// Return a decoding context for the media stream, and
141141
// map the streams to decoders. If no function is provided
142142
// (ie, the argument is nil) then all streams are demultiplexed.
143+
// Will return an error if called on a writer.
143144
Decoder(DecoderMapFunc) (Decoder, error)
144145

146+
// Multiplex media into packets. Pass a packet to a muxer function.
147+
// Stop when the context is cancelled or the end of the media stream is
148+
// signalled. Will return an error if called on a reader.
149+
Mux(context.Context, MuxFunc) error
150+
145151
// Return INPUT for a demuxer or source, OUTPUT for a muxer or
146152
// sink, DEVICE for a device, FILE for a file or stream.
147153
Type() MediaType
@@ -191,6 +197,9 @@ type Parameters interface {
191197
// Return the media type (AUDIO, VIDEO, SUBTITLE, DATA)
192198
Type() MediaType
193199

200+
// Return the stream id for encoding, or zero if not set
201+
Id() int
202+
194203
// Return number of planes for a specific PixelFormat
195204
// or SampleFormat and ChannelLayout combination
196205
NumPlanes() int
@@ -224,6 +233,10 @@ type VideoParameters interface {
224233
// io.EOF if you want to stop processing the packets early.
225234
type DecoderFunc func(Packet) error
226235

236+
// MuxFunc is a function that multiplexes a packet. Return
237+
// io.EOF to stop multiplexing normally.
238+
type MuxFunc func(Packet) error
239+
227240
// FrameFunc is a function that processes a frame of audio
228241
// or video data. Return io.EOF if you want to stop
229242
// processing the frames early.

parameters.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ type par struct {
2323
type codecpar struct {
2424
Codec ff.AVCodecID `json:"codec"`
2525

26+
// Stream Id
27+
StreamId int `json:"stream_id"`
28+
2629
// For video (in fps)
2730
Framerate float64 `json:"framerate"`
2831
}
@@ -168,6 +171,11 @@ func (par *par) Type() MediaType {
168171
return par.t
169172
}
170173

174+
// Return stream id
175+
func (par *par) Id() int {
176+
return par.codecpar.StreamId
177+
}
178+
171179
// Return number of planes for a specific PixelFormat
172180
// or SampleFormat and ChannelLayout combination
173181
func (par *par) NumPlanes() int {

reader.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package media
22

33
import (
4+
"context"
45
"encoding/json"
56
"errors"
67
"io"
@@ -9,6 +10,9 @@ import (
910

1011
// Packages
1112
ff "github.com/mutablelogic/go-media/sys/ffmpeg61"
13+
14+
// Namespace imports
15+
. "github.com/djthorpe/go-errors"
1216
)
1317

1418
////////////////////////////////////////////////////////////////////////////////
@@ -229,6 +233,10 @@ func (r *reader) Metadata(keys ...string) []Metadata {
229233
return result
230234
}
231235

236+
func (r *reader) Mux(context.Context, MuxFunc) error {
237+
return ErrOutOfOrder.With("not an output stream")
238+
}
239+
232240
////////////////////////////////////////////////////////////////////////////////
233241
// PRIVATE METHODS
234242

writer.go

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package media
22

33
import (
4+
"context"
45
"encoding/json"
56
"errors"
67
"fmt"
@@ -67,11 +68,18 @@ func createMedia(url string, format Format, metadata []Metadata, params ...Param
6768
// Add encoders and streams
6869
var result error
6970
for i, param := range params {
70-
encoder, err := newEncoder(ctx, i, param)
71+
// Stream Id from codec parameters, or use the index
72+
stream_id := param.Id()
73+
if stream_id <= 0 {
74+
stream_id = i + 1
75+
}
76+
encoder, err := newEncoder(ctx, stream_id, param)
7177
if err != nil {
7278
result = errors.Join(result, err)
79+
} else if _, exists := writer.encoder[stream_id]; exists {
80+
7381
} else {
74-
writer.encoder[i] = encoder
82+
writer.encoder[stream_id] = encoder
7583
}
7684
}
7785

@@ -143,16 +151,15 @@ func (w *writer) Close() error {
143151
result = errors.Join(result, encoder.Close())
144152
}
145153

146-
// Free resources
147-
if w.metadata != nil {
148-
ff.AVUtil_dict_free(w.metadata)
149-
}
154+
// Free output resources
150155
if w.output != nil {
156+
// This calls avio_close(w.avio)
151157
result = errors.Join(result, ff.AVFormat_close_writer(w.output))
152158
}
153-
if w.avio != nil {
154-
fmt.Println("TODO AVIO")
155-
// result = errors.Join(result, ff.AVFormat_avio_close(w.avio))
159+
160+
// Free resources
161+
if w.metadata != nil {
162+
ff.AVUtil_dict_free(w.metadata)
156163
}
157164

158165
// Release resources
@@ -183,7 +190,46 @@ func (w *writer) String() string {
183190
// PUBLIC METHODS
184191

185192
func (w *writer) Decoder(DecoderMapFunc) (Decoder, error) {
186-
return nil, ErrNotImplemented
193+
return nil, ErrOutOfOrder.With("not an input stream")
194+
}
195+
196+
func (w *writer) Mux(context.Context, MuxFunc) error {
197+
return ErrNotImplemented
198+
199+
/*
200+
while (1) {
201+
AVStream *in_stream, *out_stream;
202+
203+
ret = av_read_frame(ifmt_ctx, pkt);
204+
if (ret < 0)
205+
break;
206+
207+
in_stream = ifmt_ctx->streams[pkt->stream_index];
208+
if (pkt->stream_index >= stream_mapping_size ||
209+
stream_mapping[pkt->stream_index] < 0) {
210+
av_packet_unref(pkt);
211+
continue;
212+
}
213+
214+
pkt->stream_index = stream_mapping[pkt->stream_index];
215+
out_stream = ofmt_ctx->streams[pkt->stream_index];
216+
log_packet(ifmt_ctx, pkt, "in");
217+
218+
// copy packet
219+
av_packet_rescale_ts(pkt, in_stream->time_base, out_stream->time_base);
220+
pkt->pos = -1;
221+
log_packet(ofmt_ctx, pkt, "out");
222+
223+
ret = av_interleaved_write_frame(ofmt_ctx, pkt);
224+
// pkt is now blank (av_interleaved_write_frame() takes ownership of
225+
// its contents and resets pkt), so that no unreferencing is necessary.
226+
// This would be different if one used av_write_frame().
227+
if (ret < 0) {
228+
fprintf(stderr, "Error muxing packet\n");
229+
break;
230+
}
231+
}
232+
*/
187233
}
188234

189235
// Return OUTPUT and combination of DEVICE and STREAM

writer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func Test_writer_001(t *testing.T) {
2222
}
2323

2424
// Write audio file
25-
filename := filepath.Join(t.TempDir(), t.Name()+".sw")
25+
filename := filepath.Join(t.TempDir(), t.Name()+".mp3")
2626
stream, err := manager.AudioParameters("mono", "s16", 22050)
2727
if !assert.NoError(err) {
2828
t.SkipNow()
@@ -33,5 +33,5 @@ func Test_writer_001(t *testing.T) {
3333
t.SkipNow()
3434
}
3535
defer writer.Close()
36-
t.Log(writer)
36+
t.Log(writer, "=>", filename)
3737
}

0 commit comments

Comments
 (0)