HasAudio() generalized to HasStreams()
This commit is contained in:
parent
bbbb23041a
commit
0bce47ccae
4 changed files with 72 additions and 62 deletions
34
README.md
34
README.md
|
@ -32,7 +32,7 @@ Stream() int
|
||||||
Duration() float64
|
Duration() float64
|
||||||
FPS() float64
|
FPS() float64
|
||||||
Codec() string
|
Codec() string
|
||||||
HasAudio() bool
|
HasStreams() bool
|
||||||
FrameBuffer() []byte
|
FrameBuffer() []byte
|
||||||
MetaData() map[string]string
|
MetaData() map[string]string
|
||||||
SetFrameBuffer(buffer []byte) error
|
SetFrameBuffer(buffer []byte) error
|
||||||
|
@ -45,7 +45,7 @@ If all frames have been read, `video` will be closed automatically. If not all f
|
||||||
|
|
||||||
## `Camera`
|
## `Camera`
|
||||||
|
|
||||||
The `Camera` can read from any cameras on the device running Vidio. It takes in the stream index. On most machines the webcam device has index 0.
|
The `Camera` can read from any cameras on the device running `Vidio`. It takes in the stream index. On most machines the webcam device has index 0.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
vidio.NewCamera(stream int) (*vidio.Camera, error)
|
vidio.NewCamera(stream int) (*vidio.Camera, error)
|
||||||
|
@ -71,7 +71,7 @@ The `VideoWriter` is used to write frames to a video file. The only required par
|
||||||
vidio.NewVideoWriter(filename string, width, height int, options *vidio.Options) (*vidio.VideoWriter, error)
|
vidio.NewVideoWriter(filename string, width, height int, options *vidio.Options) (*vidio.VideoWriter, error)
|
||||||
|
|
||||||
FileName() string
|
FileName() string
|
||||||
Audio() string
|
StreamFile() string
|
||||||
Width() int
|
Width() int
|
||||||
Height() int
|
Height() int
|
||||||
Bitrate() int
|
Bitrate() int
|
||||||
|
@ -89,25 +89,25 @@ Close()
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Bitrate int // Bitrate.
|
Bitrate int // Bitrate.
|
||||||
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
|
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
|
||||||
Delay int // Delay for final frame of GIFs.
|
Delay int // Delay for final frame of GIFs.
|
||||||
Macro int // Macroblock size for determining how to resize frames for codecs.
|
Macro int // Macroblock size for determining how to resize frames for codecs.
|
||||||
FPS float64 // Frames per second for output video.
|
FPS float64 // Frames per second for output video.
|
||||||
Quality float64 // If bitrate not given, use quality instead. Must be between 0 and 1. 0:best, 1:worst.
|
Quality float64 // If bitrate not given, use quality instead. Must be between 0 and 1. 0:best, 1:worst.
|
||||||
Codec string // Codec for video.
|
Codec string // Codec for video.
|
||||||
Format string // Pixel Format for video. Default "rgb24".
|
Format string // Pixel Format for video. Default "rgb24".
|
||||||
Audio string // File path for extra stream data.
|
StreamFile string // File path for extra stream data.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `Options.Audio` parameter is intended for users who wish to process a video stream and keep the audio. Instead of having to process the video and store in a file and then combine with the original audio later, the user can simply pass in the original file path via the `Options.Video` parameter. This will combine the video with all other streams in the given file (Audio, Subtitle, Data, and Attachments Streams) and will cut all streams to be the same length. Note that `vidio` is not a audio/video editing library.
|
The `Options.StreamFile` parameter is intended for users who wish to process a video stream and keep the audio (or other streams). Instead of having to process the video and store in a file and then combine with the original audio later, the user can simply pass in the original file path via the `Options.StreamFile` parameter. This will combine the video with all other streams in the given file (Audio, Subtitle, Data, and Attachments Streams) and will cut all streams to be the same length. **Note that `Vidio` is not a audio/video editing library.**
|
||||||
|
|
||||||
Note that this means that adding extra stream data from a file will only work if the filename being written to is a container format.
|
This means that adding extra stream data from a file will only work if the filename being written to is a container format.
|
||||||
|
|
||||||
## Images
|
## Images
|
||||||
|
|
||||||
Vidio provides some convenience functions for reading and writing to images using an array of bytes. Currently, only `png` and `jpeg` formats are supported. When reading images, an optional `buffer` can be passed in to avoid array reallocation.
|
`Vidio` provides some convenience functions for reading and writing to images using an array of bytes. Currently, only `png` and `jpeg` formats are supported. When reading images, an optional `buffer` can be passed in to avoid array reallocation.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
Read(filename string, buffer ...[]byte) (int, int, []byte, error)
|
Read(filename string, buffer ...[]byte) (int, int, []byte, error)
|
||||||
|
@ -124,8 +124,8 @@ options := vidio.Options{
|
||||||
FPS: video.FPS(),
|
FPS: video.FPS(),
|
||||||
Bitrate: video.Bitrate(),
|
Bitrate: video.Bitrate(),
|
||||||
}
|
}
|
||||||
if video.HasAudio() {
|
if video.HasStreams() {
|
||||||
options.Audio = video.FileName()
|
options.StreamFile = video.FileName()
|
||||||
}
|
}
|
||||||
|
|
||||||
writer, _ := vidio.NewVideoWriter("output.mp4", video.Width(), video.Height(), &options)
|
writer, _ := vidio.NewVideoWriter("output.mp4", video.Width(), video.Height(), &options)
|
||||||
|
|
31
video.go
31
video.go
|
@ -21,7 +21,7 @@ type Video struct {
|
||||||
duration float64 // Duration of video in seconds.
|
duration float64 // Duration of video in seconds.
|
||||||
fps float64 // Frames per second.
|
fps float64 // Frames per second.
|
||||||
codec string // Codec used for video encoding.
|
codec string // Codec used for video encoding.
|
||||||
hasaudio bool // Flag storing whether file has Audio.
|
hasstreams bool // Flag storing whether file has additional data streams.
|
||||||
framebuffer []byte // Raw frame data.
|
framebuffer []byte // Raw frame data.
|
||||||
metadata map[string]string // Video metadata.
|
metadata map[string]string // Video metadata.
|
||||||
pipe *io.ReadCloser // Stdout pipe for ffmpeg process.
|
pipe *io.ReadCloser // Stdout pipe for ffmpeg process.
|
||||||
|
@ -72,8 +72,9 @@ func (video *Video) Codec() string {
|
||||||
return video.codec
|
return video.codec
|
||||||
}
|
}
|
||||||
|
|
||||||
func (video *Video) HasAudio() bool {
|
// Returns true if file has any audio, subtitle, data or attachment streams.
|
||||||
return video.hasaudio
|
func (video *Video) HasStreams() bool {
|
||||||
|
return video.hasstreams
|
||||||
}
|
}
|
||||||
|
|
||||||
func (video *Video) FrameBuffer() []byte {
|
func (video *Video) FrameBuffer() []byte {
|
||||||
|
@ -125,19 +126,27 @@ func NewVideoStreams(filename string) ([]*Video, error) {
|
||||||
return nil, fmt.Errorf("no video data found in %s", filename)
|
return nil, fmt.Errorf("no video data found in %s", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
audioData, err := ffprobe(filename, "a")
|
// Loop over all stream types. a: Audio, s: Subtitle, d: Data, t: Attachments
|
||||||
if err != nil {
|
hasstream := false
|
||||||
return nil, err
|
for _, c := range "asdt" {
|
||||||
|
data, err := ffprobe(filename, string(c))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(data) > 0 {
|
||||||
|
hasstream = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
streams := make([]*Video, len(videoData))
|
streams := make([]*Video, len(videoData))
|
||||||
for i, data := range videoData {
|
for i, data := range videoData {
|
||||||
video := &Video{
|
video := &Video{
|
||||||
filename: filename,
|
filename: filename,
|
||||||
depth: 3,
|
depth: 3,
|
||||||
stream: i,
|
stream: i,
|
||||||
hasaudio: len(audioData) > 0,
|
hasstreams: hasstream,
|
||||||
metadata: data,
|
metadata: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
video.addVideoData(data)
|
video.addVideoData(data)
|
||||||
|
|
|
@ -12,33 +12,33 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type VideoWriter struct {
|
type VideoWriter struct {
|
||||||
filename string // Output filename.
|
filename string // Output filename.
|
||||||
audio string // Extra stream data filename.
|
streamfile string // Extra stream data filename.
|
||||||
width int // Frame width.
|
width int // Frame width.
|
||||||
height int // Frame height.
|
height int // Frame height.
|
||||||
bitrate int // Output video bitrate.
|
bitrate int // Output video bitrate.
|
||||||
loop int // Number of times for GIF to loop.
|
loop int // Number of times for GIF to loop.
|
||||||
delay int // Delay of final frame of GIF. Default -1 (same delay as previous frame).
|
delay int // Delay of final frame of GIF. Default -1 (same delay as previous frame).
|
||||||
macro int // Macroblock size for determining how to resize frames for codecs.
|
macro int // Macroblock size for determining how to resize frames for codecs.
|
||||||
fps float64 // Frames per second for output video. Default 25.
|
fps float64 // Frames per second for output video. Default 25.
|
||||||
quality float64 // Used if bitrate not given. Default 0.5.
|
quality float64 // Used if bitrate not given. Default 0.5.
|
||||||
codec string // Codec to encode video with. Default libx264.
|
codec string // Codec to encode video with. Default libx264.
|
||||||
format string // Output format. Default rgb24.
|
format string // Output format. Default rgb24.
|
||||||
pipe *io.WriteCloser // Stdout pipe of ffmpeg process.
|
pipe *io.WriteCloser // Stdout pipe of ffmpeg process.
|
||||||
cmd *exec.Cmd // ffmpeg command.
|
cmd *exec.Cmd // ffmpeg command.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional parameters for VideoWriter.
|
// Optional parameters for VideoWriter.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Bitrate int // Bitrate.
|
Bitrate int // Bitrate.
|
||||||
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
|
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
|
||||||
Delay int // Delay for final frame of GIFs.
|
Delay int // Delay for final frame of GIFs.
|
||||||
Macro int // Macroblock size for determining how to resize frames for codecs.
|
Macro int // Macroblock size for determining how to resize frames for codecs.
|
||||||
FPS float64 // Frames per second for output video.
|
FPS float64 // Frames per second for output video.
|
||||||
Quality float64 // If bitrate not given, use quality instead. Must be between 0 and 1. 0:best, 1:worst.
|
Quality float64 // If bitrate not given, use quality instead. Must be between 0 and 1. 0:best, 1:worst.
|
||||||
Codec string // Codec for video.
|
Codec string // Codec for video.
|
||||||
Format string // Pixel Format for video. Default "rgb24".
|
Format string // Pixel Format for video. Default "rgb24".
|
||||||
Audio string // File path for extra stream data.
|
StreamFile string // File path for extra stream data.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (writer *VideoWriter) FileName() string {
|
func (writer *VideoWriter) FileName() string {
|
||||||
|
@ -46,8 +46,8 @@ func (writer *VideoWriter) FileName() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// File used to fill in extra stream data.
|
// File used to fill in extra stream data.
|
||||||
func (writer *VideoWriter) Audio() string {
|
func (writer *VideoWriter) StreamFile() string {
|
||||||
return writer.audio
|
return writer.streamfile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (writer *VideoWriter) Width() int {
|
func (writer *VideoWriter) Width() int {
|
||||||
|
@ -155,11 +155,11 @@ func NewVideoWriter(filename string, width, height int, options *Options) (*Vide
|
||||||
writer.format = options.Format
|
writer.format = options.Format
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Audio != "" {
|
if options.StreamFile != "" {
|
||||||
if !exists(options.Audio) {
|
if !exists(options.StreamFile) {
|
||||||
return nil, fmt.Errorf("file %s does not exist", options.Audio)
|
return nil, fmt.Errorf("file %s does not exist", options.StreamFile)
|
||||||
}
|
}
|
||||||
writer.audio = options.Audio
|
writer.streamfile = options.StreamFile
|
||||||
}
|
}
|
||||||
|
|
||||||
return writer, nil
|
return writer, nil
|
||||||
|
@ -184,12 +184,12 @@ func (writer *VideoWriter) init() error {
|
||||||
|
|
||||||
gif := strings.HasSuffix(strings.ToLower(writer.filename), ".gif")
|
gif := strings.HasSuffix(strings.ToLower(writer.filename), ".gif")
|
||||||
|
|
||||||
// Assumes "writer.file" is a container format.
|
// Assumes "writer.streamfile" is a container format.
|
||||||
// gif check is included since they are a common format.
|
// gif check is included since they are a common format.
|
||||||
if writer.audio != "" && !gif {
|
if writer.streamfile != "" && !gif {
|
||||||
command = append(
|
command = append(
|
||||||
command,
|
command,
|
||||||
"-i", writer.audio,
|
"-i", writer.streamfile,
|
||||||
"-map", "0:v:0",
|
"-map", "0:v:0",
|
||||||
"-map", "1:a?", // Add Audio streams if present.
|
"-map", "1:a?", // Add Audio streams if present.
|
||||||
"-c:a", "copy",
|
"-c:a", "copy",
|
||||||
|
|
|
@ -46,6 +46,7 @@ func TestVideoMetaData(t *testing.T) {
|
||||||
assertEquals(video.fps, float64(30))
|
assertEquals(video.fps, float64(30))
|
||||||
assertEquals(video.codec, "h264")
|
assertEquals(video.codec, "h264")
|
||||||
assertEquals(video.stream, 0)
|
assertEquals(video.stream, 0)
|
||||||
|
assertEquals(video.hasstreams, true)
|
||||||
assertEquals(len(video.framebuffer), 0)
|
assertEquals(len(video.framebuffer), 0)
|
||||||
|
|
||||||
if video.pipe != nil {
|
if video.pipe != nil {
|
||||||
|
@ -92,8 +93,8 @@ func TestVideoWriting(t *testing.T) {
|
||||||
Bitrate: video.Bitrate(),
|
Bitrate: video.Bitrate(),
|
||||||
Codec: video.Codec(),
|
Codec: video.Codec(),
|
||||||
}
|
}
|
||||||
if video.HasAudio() {
|
if video.HasStreams() {
|
||||||
options.Audio = video.FileName()
|
options.StreamFile = video.FileName()
|
||||||
}
|
}
|
||||||
|
|
||||||
writer, err := NewVideoWriter(output, video.width, video.height, &options)
|
writer, err := NewVideoWriter(output, video.width, video.height, &options)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue