@@ -61,6 +61,8 @@ and open media files and byte streams.
61
61
### Demultiplexing
62
62
63
63
``` go
64
+ package main
65
+
64
66
import (
65
67
media " github.com/mutablelogic/go-media"
66
68
)
@@ -106,51 +108,82 @@ func main() {
106
108
107
109
### Decoding - Video
108
110
109
- This example shows you how to decode video frames from a media file into images.
111
+ This example shows you how to decode video frames from a media file into images, and
112
+ encode those images to JPEG format.
110
113
111
114
``` go
115
+ package main
116
+
112
117
import (
113
- media " github.com/mutablelogic/go-media"
118
+ " context"
119
+ " fmt"
120
+ " image/jpeg"
121
+ " io"
122
+ " log"
123
+ " os"
124
+ " path/filepath"
125
+
126
+ // Packages
127
+ ffmpeg " github.com/mutablelogic/go-media/pkg/ffmpeg"
128
+
129
+ // Namespace imports
130
+ . " github.com/mutablelogic/go-media"
114
131
)
115
132
116
133
func main () {
117
- manager , err := media.NewManager ()
134
+ // Open a media file for reading. The format of the file is guessed.
135
+ input , err := ffmpeg.Open (os.Args [1 ])
118
136
if err != nil {
119
137
log.Fatal (err)
120
138
}
121
139
122
- media , err := manager.Open (" etc/test/sample.mp4" , nil )
123
- if err != nil {
124
- log.Fatal (err)
140
+ // Make a map function which can be used to decode the streams and set
141
+ // the parameters we want from the decode. The audio and video streams
142
+ // are resampled and resized to fit the parameters we pass back the decoder.
143
+ mapfunc := func (stream int , par *ffmpeg.Par ) (*ffmpeg.Par , error ) {
144
+ if par.Type () == VIDEO {
145
+ // Convert frame to yuv420p to frame size and rate as source
146
+ return ffmpeg.VideoPar (" yuv420p" , par.WidthHeight (), par.FrameRate ()), nil
147
+ }
148
+ // Ignore other streams
149
+ return nil , nil
125
150
}
126
- defer media.Close ()
127
151
128
- // Create a decoder for the media file. Only video streams are decoded
129
- decoder , err := media.Decoder (func (stream Stream) (Parameters, error ) {
130
- if stream.Type () == VIDEO {
131
- // Copy video
132
- return stream.Parameters (), nil
133
- } else {
134
- // Ignore other stream types
135
- return nil , nil
136
- }
137
- })
152
+ // Make a folder where we're going to store the thumbnails
153
+ tmp , err := os.MkdirTemp (" " , " decode" )
138
154
if err != nil {
139
155
log.Fatal (err)
140
156
}
141
157
142
- // The frame function is called for each frame in the stream
143
- framefn := func (frame Frame) error {
144
- image , err := frame.Image ()
158
+ // Decode the streams and receive the video frame
159
+ // If the map function is nil, the frames are copied. In this example,
160
+ // we get a yuv420p frame at the same size as the original.
161
+ n := 0
162
+ err = input.Decode (context.Background (), mapfunc, func (stream int , frame *ffmpeg.Frame ) error {
163
+ // Write the frame to a file
164
+ w , err := os.Create (filepath.Join (tmp, fmt.Sprintf (" frame-%d -%d .jpg" , stream, n)))
145
165
if err != nil {
146
166
return err
147
167
}
148
- // TODO: Do something with the image here....
149
- return nil
150
- }
168
+ defer w.Close ()
151
169
152
- // decode frames from the stream
153
- if err := decoder.Decode (context.Background (), framefn); err != nil {
170
+ // Convert to an image and encode a JPEG
171
+ if image , err := frame.Image (); err != nil {
172
+ return err
173
+ } else if err := jpeg.Encode (w, image, nil ); err != nil {
174
+ return err
175
+ } else {
176
+ log.Println (" Wrote:" , w.Name ())
177
+ }
178
+
179
+ // End after 10 frames
180
+ n++
181
+ if n >= 10 {
182
+ return io.EOF
183
+ }
184
+ return nil
185
+ })
186
+ if err != nil {
154
187
log.Fatal (err)
155
188
}
156
189
}
@@ -159,68 +192,67 @@ func main() {
159
192
### Encoding - Audio and Video
160
193
161
194
This example shows you how to encode video and audio frames into a media file.
195
+ It creates a testcard signal overlayed with a timestamp, and a 1KHz tone at -5dB
162
196
163
197
``` go
164
198
package main
165
199
166
200
import (
201
+ " fmt"
167
202
" io"
168
203
" log"
169
204
" os"
170
- " time"
171
205
172
- media " github.com/mutablelogic/go-media "
206
+ // Packages
173
207
ffmpeg " github.com/mutablelogic/go-media/pkg/ffmpeg"
174
208
generator " github.com/mutablelogic/go-media/pkg/generator"
175
- ff " github.com/mutablelogic/go-media/sys/ffmpeg61"
176
209
)
177
210
211
+ // This example encodes an audio an video stream to a file
178
212
func main () {
179
213
// Create a new file with an audio and video stream
180
- // 30fps and 22050Hz mono audio
181
214
file , err := ffmpeg.Create (os.Args [1 ],
182
- ffmpeg.OptStream (1 , ffmpeg.VideoPar (" yuv420p" , " 640x480 " , 30 )),
215
+ ffmpeg.OptStream (1 , ffmpeg.VideoPar (" yuv420p" , " 1024x720 " , 30 )),
183
216
ffmpeg.OptStream (2 , ffmpeg.AudioPar (" fltp" , " mono" , 22050 )),
184
217
)
185
218
if err != nil {
186
219
log.Fatal (err)
187
220
}
188
221
defer file.Close ()
189
222
190
- // Make an video generator which can generate YUV420P frames
191
- // with the same parameters as the video stream
192
- video , err := generator.NewYUV420P (file.Stream (1 ).Par ())
223
+ // Make an video generator which can generate frames with the same
224
+ // parameters as the video stream
225
+ video , err := generator.NewEBU (file.Stream (1 ).Par ())
193
226
if err != nil {
194
227
log.Fatal (err)
195
228
}
196
229
defer video.Close ()
197
230
198
- // Make an audio generator which can generate a 440Hz tone
231
+ // Make an audio generator which can generate a 1KHz tone
199
232
// at -5dB with the same parameters as the audio stream
200
- audio , err := generator.NewSine (440 , -5 , file.Stream (2 ).Par ())
233
+ audio , err := generator.NewSine (1000 , -5 , file.Stream (2 ).Par ())
201
234
if err != nil {
202
235
log.Fatal (err)
203
236
}
204
237
defer audio.Close ()
205
238
206
- // Write 1 min of frames , passing video and audio frames to the encoder
239
+ // Write 90 seconds , passing video and audio frames to the encoder
207
240
// and returning io.EOF when the duration is reached
208
- duration := time. Minute
209
- if err : = file.Encode (func (stream int ) (*ff. AVFrame , error ) {
210
- var frame media .Frame
241
+ duration := float64 ( 90 )
242
+ err = file.Encode (func (stream int ) (*ffmpeg. Frame , error ) {
243
+ var frame *ffmpeg .Frame
211
244
switch stream {
212
245
case 1 :
213
246
frame = video.Frame ()
214
247
case 2 :
215
248
frame = audio.Frame ()
216
249
}
217
- if frame.Time () >= duration {
218
- return nil , io.EOF
219
- } else {
220
- log.Println (" Frame" , stream, " =>" , frame.Time ().Truncate (time.Millisecond ))
221
- return frame.(*ffmpeg.Frame ).AVFrame (), nil
250
+ if frame != nil && frame.Ts () < duration {
251
+ return frame, nil
222
252
}
223
- }, nil ); err != nil {
253
+ return nil , io.EOF
254
+ }, nil )
255
+ if err != nil {
224
256
log.Fatal (err)
225
257
}
226
258
}
233
265
### Retrieving Metadata and Artwork from a media file
234
266
235
267
Here is an example of opening a media file and retrieving metadata and artwork.
268
+ You have to read the artwork separately from the metadata.
236
269
237
270
``` go
238
271
package main
@@ -241,20 +274,13 @@ import (
241
274
" log"
242
275
" os"
243
276
244
- media " github.com/mutablelogic/go-media "
245
- file " github.com/mutablelogic/go-media/pkg/file "
277
+ // Packages
278
+ ffmpeg " github.com/mutablelogic/go-media/pkg/ffmpeg "
246
279
)
247
280
248
281
func main () {
249
- manager , err := media.NewManager ()
250
- if err != nil {
251
- log.Fatal (err)
252
- }
253
-
254
282
// Open a media file for reading. The format of the file is guessed.
255
- // Alteratively, you can pass a format as the second argument. Further optional
256
- // arguments can be used to set the format options.
257
- reader , err := manager.Open (os.Args [1 ], nil )
283
+ reader , err := ffmpeg.Open (os.Args [1 ])
258
284
if err != nil {
259
285
log.Fatal (err)
260
286
}
@@ -268,14 +294,13 @@ func main() {
268
294
}
269
295
270
296
// Retrieve artwork by using the MetaArtwork key. The value is of type []byte.
271
- // which needs to be converted to an image. There is a utility method to
272
- // detect the image type.
273
- for _ , artwork := range reader. Metadata (media. MetaArtwork ) {
274
- mimetype , ext , err := file. MimeType (artwork. Value ().([] byte ))
275
- if err != nil {
276
- log.Fatal (err )
297
+ // which needs to be converted to an image.
298
+ for _ , artwork := range reader. Metadata (ffmpeg. MetaArtwork ) {
299
+ mimetype := artwork. Value ()
300
+ if mimetype != " " {
301
+ // Retrieve the data using the metadata.Bytes() method
302
+ log.Print ( " We got some artwork of mimetype " , mimetype )
277
303
}
278
- log.Print (" got artwork" , mimetype, ext)
279
304
}
280
305
}
281
306
```
0 commit comments