Skip to content

Commit

Permalink
HasAudio() generalized to HasStreams()
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexEidt committed Sep 15, 2022
1 parent bbbb230 commit 0bce47c
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 62 deletions.
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Stream() int
Duration() float64
FPS() float64
Codec() string
HasAudio() bool
HasStreams() bool
FrameBuffer() []byte
MetaData() map[string]string
SetFrameBuffer(buffer []byte) error
Expand All @@ -45,7 +45,7 @@ If all frames have been read, `video` will be closed automatically. If not all f

## `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
vidio.NewCamera(stream int) (*vidio.Camera, error)
Expand All @@ -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)

FileName() string
Audio() string
StreamFile() string
Width() int
Height() int
Bitrate() int
Expand All @@ -89,25 +89,25 @@ Close()

```go
type Options struct {
Bitrate int // Bitrate.
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
Delay int // Delay for final frame of GIFs.
Macro int // Macroblock size for determining how to resize frames for codecs.
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.
Codec string // Codec for video.
Format string // Pixel Format for video. Default "rgb24".
Audio string // File path for extra stream data.
Bitrate int // Bitrate.
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
Delay int // Delay for final frame of GIFs.
Macro int // Macroblock size for determining how to resize frames for codecs.
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.
Codec string // Codec for video.
Format string // Pixel Format for video. Default "rgb24".
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

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
Read(filename string, buffer ...[]byte) (int, int, []byte, error)
Expand All @@ -124,8 +124,8 @@ options := vidio.Options{
FPS: video.FPS(),
Bitrate: video.Bitrate(),
}
if video.HasAudio() {
options.Audio = video.FileName()
if video.HasStreams() {
options.StreamFile = video.FileName()
}

writer, _ := vidio.NewVideoWriter("output.mp4", video.Width(), video.Height(), &options)
Expand Down
31 changes: 20 additions & 11 deletions video.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type Video struct {
duration float64 // Duration of video in seconds.
fps float64 // Frames per second.
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.
metadata map[string]string // Video metadata.
pipe *io.ReadCloser // Stdout pipe for ffmpeg process.
Expand Down Expand Up @@ -72,8 +72,9 @@ func (video *Video) Codec() string {
return video.codec
}

func (video *Video) HasAudio() bool {
return video.hasaudio
// Returns true if file has any audio, subtitle, data or attachment streams.
func (video *Video) HasStreams() bool {
return video.hasstreams
}

func (video *Video) FrameBuffer() []byte {
Expand Down Expand Up @@ -125,19 +126,27 @@ func NewVideoStreams(filename string) ([]*Video, error) {
return nil, fmt.Errorf("no video data found in %s", filename)
}

audioData, err := ffprobe(filename, "a")
if err != nil {
return nil, err
// Loop over all stream types. a: Audio, s: Subtitle, d: Data, t: Attachments
hasstream := false
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))
for i, data := range videoData {
video := &Video{
filename: filename,
depth: 3,
stream: i,
hasaudio: len(audioData) > 0,
metadata: data,
filename: filename,
depth: 3,
stream: i,
hasstreams: hasstream,
metadata: data,
}

video.addVideoData(data)
Expand Down
64 changes: 32 additions & 32 deletions videowriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,42 @@ import (
)

type VideoWriter struct {
filename string // Output filename.
audio string // Extra stream data filename.
width int // Frame width.
height int // Frame height.
bitrate int // Output video bitrate.
loop int // Number of times for GIF to loop.
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.
fps float64 // Frames per second for output video. Default 25.
quality float64 // Used if bitrate not given. Default 0.5.
codec string // Codec to encode video with. Default libx264.
format string // Output format. Default rgb24.
pipe *io.WriteCloser // Stdout pipe of ffmpeg process.
cmd *exec.Cmd // ffmpeg command.
filename string // Output filename.
streamfile string // Extra stream data filename.
width int // Frame width.
height int // Frame height.
bitrate int // Output video bitrate.
loop int // Number of times for GIF to loop.
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.
fps float64 // Frames per second for output video. Default 25.
quality float64 // Used if bitrate not given. Default 0.5.
codec string // Codec to encode video with. Default libx264.
format string // Output format. Default rgb24.
pipe *io.WriteCloser // Stdout pipe of ffmpeg process.
cmd *exec.Cmd // ffmpeg command.
}

// Optional parameters for VideoWriter.
type Options struct {
Bitrate int // Bitrate.
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
Delay int // Delay for final frame of GIFs.
Macro int // Macroblock size for determining how to resize frames for codecs.
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.
Codec string // Codec for video.
Format string // Pixel Format for video. Default "rgb24".
Audio string // File path for extra stream data.
Bitrate int // Bitrate.
Loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
Delay int // Delay for final frame of GIFs.
Macro int // Macroblock size for determining how to resize frames for codecs.
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.
Codec string // Codec for video.
Format string // Pixel Format for video. Default "rgb24".
StreamFile string // File path for extra stream data.
}

func (writer *VideoWriter) FileName() string {
return writer.filename
}

// File used to fill in extra stream data.
func (writer *VideoWriter) Audio() string {
return writer.audio
func (writer *VideoWriter) StreamFile() string {
return writer.streamfile
}

func (writer *VideoWriter) Width() int {
Expand Down Expand Up @@ -155,11 +155,11 @@ func NewVideoWriter(filename string, width, height int, options *Options) (*Vide
writer.format = options.Format
}

if options.Audio != "" {
if !exists(options.Audio) {
return nil, fmt.Errorf("file %s does not exist", options.Audio)
if options.StreamFile != "" {
if !exists(options.StreamFile) {
return nil, fmt.Errorf("file %s does not exist", options.StreamFile)
}
writer.audio = options.Audio
writer.streamfile = options.StreamFile
}

return writer, nil
Expand All @@ -184,12 +184,12 @@ func (writer *VideoWriter) init() error {

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.
if writer.audio != "" && !gif {
if writer.streamfile != "" && !gif {
command = append(
command,
"-i", writer.audio,
"-i", writer.streamfile,
"-map", "0:v:0",
"-map", "1:a?", // Add Audio streams if present.
"-c:a", "copy",
Expand Down
5 changes: 3 additions & 2 deletions vidio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func TestVideoMetaData(t *testing.T) {
assertEquals(video.fps, float64(30))
assertEquals(video.codec, "h264")
assertEquals(video.stream, 0)
assertEquals(video.hasstreams, true)
assertEquals(len(video.framebuffer), 0)

if video.pipe != nil {
Expand Down Expand Up @@ -92,8 +93,8 @@ func TestVideoWriting(t *testing.T) {
Bitrate: video.Bitrate(),
Codec: video.Codec(),
}
if video.HasAudio() {
options.Audio = video.FileName()
if video.HasStreams() {
options.StreamFile = video.FileName()
}

writer, err := NewVideoWriter(output, video.width, video.height, &options)
Expand Down

0 comments on commit 0bce47c

Please sign in to comment.