Skip to content

Commit 337e585

Browse files
committed
Updated to add resampler
1 parent b595dea commit 337e585

File tree

12 files changed

+271
-61
lines changed

12 files changed

+271
-61
lines changed

cmd/transcode/main.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,6 @@ func main() {
119119
os.Exit(-2)
120120
}
121121

122-
// Print the map
123-
media_map.PrintMap(os.Stdout)
124-
125122
// For the output, we can output to a device, a URL, or a file
126123
var out Media
127124
if reDeviceName.MatchString(flags.Out()) {
@@ -138,6 +135,25 @@ func main() {
138135
os.Exit(-2)
139136
}
140137

138+
// For each input stream, add an output
139+
for _, stream := range media_map.Streams(flags.MediaFlags()) {
140+
// TODO
141+
// If audio, then resample data
142+
switch {
143+
case stream.Flags().Is(MEDIA_FLAG_AUDIO):
144+
if err := media_map.Resample(AudioFormat{
145+
Rate: 11025,
146+
Format: SAMPLE_FORMAT_U8,
147+
}, stream); err != nil {
148+
fmt.Fprintln(os.Stderr, "Cannot resample audio stream:", err)
149+
os.Exit(-2)
150+
}
151+
}
152+
}
153+
154+
// Print the map
155+
media_map.PrintMap(os.Stdout)
156+
141157
// Create a cancellable context
142158
ctx := contextForSignal(os.Interrupt)
143159

media.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,24 @@ type Map interface {
105105
// Return input media
106106
Input() Media
107107

108-
// Return streams which are mapped for decoding
109-
Streams() []Stream
108+
// Return a single stream which is mapped for decoding, filtering by
109+
// stream type. Returns nil if there is no selection of that type
110+
Streams(MediaFlag) []Stream
110111

111112
// Print a summary of the mapping
112113
PrintMap(w io.Writer)
114+
115+
// Resample an audio stream
116+
Resample(AudioFormat, Stream) error
117+
118+
// Encode to output media using default codec from a specific stream
119+
//Encode(Media, Stream) error
113120
}
114121

115122
// Media is a source or destination of media
116123
type Media interface {
117124
io.Closer
118125

119-
// Return best streams for specific types (video, audio, subtitle, data or attachment)
120-
// or returns empty slice if no streams of that type are in the media file. Only returns
121-
// one stream of each type.
122-
//StreamsByType(MediaFlag) []Stream
123-
124126
// URL for the media
125127
URL() string
126128

@@ -130,6 +132,9 @@ type Media interface {
130132
// Return media flags for the media
131133
Flags() MediaFlag
132134

135+
// Return the format of the media
136+
Format() MediaFormat
137+
133138
// Return metadata for the media
134139
Metadata() Metadata
135140

pkg/media/decoder.go

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ package media
22

33
import (
44
// Packages
5+
"fmt"
6+
57
multierror "github.com/hashicorp/go-multierror"
68
ffmpeg "github.com/mutablelogic/go-media/sys/ffmpeg51"
9+
710
// Namespace imports
11+
. "github.com/mutablelogic/go-media"
812
//. "github.com/djthorpe/go-errors"
9-
//. "github.com/mutablelogic/go-media"
1013
)
1114

1215
////////////////////////////////////////////////////////////////////////////////
@@ -16,7 +19,6 @@ type decoder struct {
1619
ctx *ffmpeg.AVCodecContext
1720
stream *stream
1821
frame *frame
19-
codec *ffmpeg.AVCodec
2022
}
2123

2224
////////////////////////////////////////////////////////////////////////////////
@@ -37,8 +39,6 @@ func NewDecoder(stream *stream) *decoder {
3739
decoder := ffmpeg.AVCodec_find_decoder(stream.ctx.CodecPar().CodecID())
3840
if decoder == nil {
3941
return nil
40-
} else {
41-
this.codec = decoder
4242
}
4343

4444
// Allocate a codec context for the decoder
@@ -92,8 +92,35 @@ func (decoder *decoder) Close() error {
9292
decoder.ctx = nil
9393
decoder.frame = nil
9494
decoder.stream = nil
95-
decoder.codec = nil
9695

9796
// Return any errors
9897
return result
9998
}
99+
100+
////////////////////////////////////////////////////////////////////////////////
101+
// STRINGIFY
102+
103+
func (decoder *decoder) String() string {
104+
str := "<media.decoder"
105+
if decoder.ctx != nil {
106+
str += fmt.Sprint(" context=", decoder.ctx)
107+
}
108+
if decoder.frame != nil {
109+
str += fmt.Sprint(" frame=", decoder.frame)
110+
}
111+
return str + ">"
112+
}
113+
114+
////////////////////////////////////////////////////////////////////////////////
115+
// PUBLIC METHODS
116+
117+
func (decoder *decoder) AudioFormat() AudioFormat {
118+
if decoder.ctx == nil {
119+
return AudioFormat{}
120+
}
121+
return AudioFormat{
122+
Rate: uint(decoder.ctx.BitRate()),
123+
Format: fromSampleFormat(decoder.ctx.SampleFormat()),
124+
Layout: fromChannelLayout(decoder.ctx.ChannelLayout()),
125+
}
126+
}

pkg/media/encoder.go

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

33
import (
4+
"fmt"
5+
46
ffmpeg "github.com/mutablelogic/go-media/sys/ffmpeg51"
57
// Namespace imports
68
//. "github.com/djthorpe/go-errors"
@@ -11,31 +13,16 @@ import (
1113
// TYPES
1214

1315
type encoder struct {
14-
codec *ffmpeg.AVCodec
15-
ctx *ffmpeg.AVCodecContext
16+
ctx *ffmpeg.AVCodecContext
1617
}
1718

1819
////////////////////////////////////////////////////////////////////////////////
1920
// LIFECYCLE
2021

2122
// Create a encoder for a stream
22-
func NewEncoderByName(name string) *encoder {
23+
func NewEncoderWithCodec(codec *ffmpeg.AVCodec) *encoder {
2324
this := new(encoder)
2425

25-
// Find encoder
26-
if codec := ffmpeg.AVCodec_find_encoder_by_name(name); codec == nil {
27-
return nil
28-
} else {
29-
this.codec = codec
30-
}
31-
32-
// Allocate context
33-
if ctx := ffmpeg.AVCodec_alloc_context3(this.codec); ctx == nil {
34-
return nil
35-
} else {
36-
this.ctx = ctx
37-
}
38-
3926
// Return success
4027
return this
4128
}
@@ -50,7 +37,6 @@ func (encoder *encoder) Close() error {
5037

5138
// Blank out other fields
5239
encoder.ctx = nil
53-
encoder.codec = nil
5440

5541
// Return success
5642
return result
@@ -61,8 +47,8 @@ func (encoder *encoder) Close() error {
6147

6248
func (encoder *encoder) String() string {
6349
str := "<media.encoder"
64-
if encoder.codec != nil {
65-
str += " name=" + encoder.codec.Name()
50+
if encoder.ctx != nil {
51+
str += fmt.Sprint(" context=", encoder.ctx)
6652
}
6753
return str + ">"
6854
}

pkg/media/manager.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package media
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"io"
78
"log"
89
"strings"
@@ -236,6 +237,16 @@ func (manager *manager) Decode(ctx context.Context, media_map Map, p Packet, fn
236237
// Receive frames from the packet
237238
err := ffmpeg.AVCodec_receive_frame(decoder.ctx, decoder.frame.ctx)
238239
if err == nil {
240+
// Resample
241+
if mapentry.Resampler != nil {
242+
if err := mapentry.Resampler.Resample(decoder.frame); err == nil {
243+
fmt.Println("Resample", mapentry.Resampler.Frame())
244+
} else {
245+
fmt.Println("Error", err)
246+
}
247+
// Release the frame
248+
mapentry.Resampler.Release()
249+
}
239250
err = fn(ctx, decoder.frame)
240251
}
241252

pkg/media/manager_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
)
1717

1818
const (
19-
SAMPLE_MP4 = "../../etc/sample.mp4"
19+
SAMPLE_MP4 = "../../etc/test/sample.mp4"
2020
SAMPLE_HLS = "http://a.files.bbci.co.uk/media/live/manifesto/audio/simulcast/hls/nonuk/sbr_vlow/ak/bbc_radio_fourfm.m3u8"
2121
)
2222

pkg/media/map.go

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ type decodemap struct {
2626
type mapentry struct {
2727
Decoder *decoder // Decoder context for the stream
2828
Resampler *resampler // Resampler context for the audio frames
29-
Scaler *scaler // Scaler context for the video frames
29+
Rescaler *rescaler // Rescaler context for the video frames
30+
Encoder *encoder // Encoder context for the stream
3031
}
3132

3233
// Ensure decodemap complies with Map interface
@@ -115,16 +116,22 @@ func (mapentry *mapentry) Close() error {
115116
result = multierror.Append(result, err)
116117
}
117118
}
118-
if mapentry.Scaler != nil {
119-
if err := mapentry.Scaler.Close(); err != nil {
119+
if mapentry.Rescaler != nil {
120+
if err := mapentry.Rescaler.Close(); err != nil {
121+
result = multierror.Append(result, err)
122+
}
123+
}
124+
if mapentry.Encoder != nil {
125+
if err := mapentry.Encoder.Close(); err != nil {
120126
result = multierror.Append(result, err)
121127
}
122128
}
123129

124130
// Release resources
125131
mapentry.Decoder = nil
126132
mapentry.Resampler = nil
127-
mapentry.Scaler = nil
133+
mapentry.Rescaler = nil
134+
mapentry.Encoder = nil
128135

129136
// Return any errors
130137
return result
@@ -147,16 +154,43 @@ func (m *decodemap) Input() Media {
147154
}
148155

149156
// Return the input media streams which should be decoded
150-
func (m *decodemap) Streams() []Stream {
157+
func (m *decodemap) Streams(flag MediaFlag) []Stream {
151158
var result []Stream
152159
for _, mapentry := range m.context {
153160
if mapentry.Decoder != nil {
154-
result = append(result, mapentry.Decoder.stream)
161+
if flag == MEDIA_FLAG_NONE || mapentry.Decoder.stream.Flags().Is(flag) {
162+
result = append(result, mapentry.Decoder.stream)
163+
}
155164
}
156165
}
157166
return result
158167
}
159168

169+
// Create a resampler for an audio stream
170+
func (m *decodemap) Resample(out AudioFormat, in Stream) error {
171+
decoder := toDecoder(m.context, in)
172+
if decoder == nil {
173+
return ErrBadParameter.With("resample: no decoder: ", in)
174+
}
175+
176+
// Check decoder is audio
177+
if !in.Flags().Is(MEDIA_FLAG_AUDIO) {
178+
return ErrBadParameter.With("not an audio stream: ", in)
179+
}
180+
181+
// Create a resampler
182+
resampler := NewResampler(out, decoder.AudioFormat())
183+
if resampler == nil {
184+
return ErrInternalAppError.With("resample: returned nil: ", decoder)
185+
}
186+
187+
// Set resampler
188+
m.context[in.Index()].Resampler = resampler
189+
190+
// Return success
191+
return nil
192+
}
193+
160194
// Decode a packet, by calling a decoding function with a packet.
161195
// If the stream associated with the packet is not in the map, then
162196
// ignore it
@@ -176,15 +210,35 @@ func (m *decodemap) Demux(ctx context.Context, p Packet, fn DemuxFn) error {
176210

177211
// PrintMap will print out a summary of the mapping
178212
func (m *decodemap) PrintMap(w io.Writer) {
179-
for id := range m.context {
213+
for id, entry := range m.context {
180214
stream := m.input.streams[id]
181215
fmt.Fprintf(w, "Stream %2d (%s): %s\n", id, toMediaType(stream.Flags()), stream)
216+
if entry.Rescaler != nil {
217+
fmt.Fprintf(w, " Rescale: %s\n", entry.Rescaler)
218+
}
219+
if entry.Resampler != nil {
220+
fmt.Fprintf(w, " Resample: %s\n", entry.Resampler)
221+
}
222+
if entry.Encoder != nil {
223+
fmt.Fprintf(w, " Encode: %s\n", entry.Encoder)
224+
}
182225
}
183226
}
184227

185228
////////////////////////////////////////////////////////////////////////////////
186229
// PRIVATE METHODS
187230

231+
// Return a decoder from the context
232+
func toDecoder(context map[int]*mapentry, stream Stream) *decoder {
233+
if context == nil || stream == nil {
234+
return nil
235+
} else if entry, exists := context[stream.Index()]; !exists || entry == nil {
236+
return nil
237+
} else {
238+
return entry.Decoder
239+
}
240+
}
241+
188242
// Return media type (audio, video, subtitle, etc)
189243
func toMediaType(flag MediaFlag) string {
190244
switch {

0 commit comments

Comments
 (0)