Better documentation
This commit is contained in:
parent
1afe1bbfe5
commit
1062e2f4f8
4 changed files with 92 additions and 70 deletions
35
camera.go
35
camera.go
|
@ -11,17 +11,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Camera struct {
|
type Camera struct {
|
||||||
name string
|
name string // Camera device name.
|
||||||
width int
|
width int // Camera frame width.
|
||||||
height int
|
height int // Camera frame height.
|
||||||
depth int
|
depth int // Camera frame depth.
|
||||||
fps float64
|
fps float64 // Camera frame rate.
|
||||||
codec string
|
codec string // Camera codec.
|
||||||
framebuffer []byte
|
framebuffer []byte // Raw frame data.
|
||||||
pipe *io.ReadCloser
|
pipe *io.ReadCloser // Stdout pipe for ffmpeg process streaming webcam.
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd // ffmpeg command.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the webcam device name.
|
||||||
|
// On windows, ffmpeg output from the -list_devices command is parsed to find the device name.
|
||||||
func getDevicesWindows() []string {
|
func getDevicesWindows() []string {
|
||||||
// Run command to get list of devices.
|
// Run command to get list of devices.
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
|
@ -53,6 +55,7 @@ func getDevicesWindows() []string {
|
||||||
return devices
|
return devices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get camera meta data such as width, height, fps and codec.
|
||||||
func getCameraData(device string, camera *Camera) {
|
func getCameraData(device string, camera *Camera) {
|
||||||
// Run command to get camera data.
|
// Run command to get camera data.
|
||||||
// Webcam will turn on and then off in quick succession.
|
// Webcam will turn on and then off in quick succession.
|
||||||
|
@ -62,12 +65,13 @@ func getCameraData(device string, camera *Camera) {
|
||||||
"-f", webcam(),
|
"-f", webcam(),
|
||||||
"-i", device,
|
"-i", device,
|
||||||
)
|
)
|
||||||
|
// The command will fail since we do not give a file to write to, therefore
|
||||||
|
// it will write the meta data to Stderr.
|
||||||
pipe, err := cmd.StderrPipe()
|
pipe, err := cmd.StderrPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
// Start the command.
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -81,12 +85,13 @@ func getCameraData(device string, camera *Camera) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Wait for the command to finish.
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
|
|
||||||
parseWebcamData(buffer[:total], camera)
|
parseWebcamData(buffer[:total], camera)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a new camera struct that can read from the device with the given stream index.
|
||||||
func NewCamera(stream int) *Camera {
|
func NewCamera(stream int) *Camera {
|
||||||
// Check if ffmpeg is installed on the users machine.
|
// Check if ffmpeg is installed on the users machine.
|
||||||
checkExists("ffmpeg")
|
checkExists("ffmpeg")
|
||||||
|
@ -117,6 +122,8 @@ func NewCamera(stream int) *Camera {
|
||||||
return &camera
|
return &camera
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Once the user calls Read() for the first time on a Camera struct,
|
||||||
|
// the ffmpeg command which is used to read the camera device is started.
|
||||||
func initCamera(camera *Camera) {
|
func initCamera(camera *Camera) {
|
||||||
// If user exits with Ctrl+C, stop ffmpeg process.
|
// If user exits with Ctrl+C, stop ffmpeg process.
|
||||||
camera.cleanup()
|
camera.cleanup()
|
||||||
|
@ -146,6 +153,7 @@ func initCamera(camera *Camera) {
|
||||||
camera.framebuffer = make([]byte, camera.width*camera.height*camera.depth)
|
camera.framebuffer = make([]byte, camera.width*camera.height*camera.depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reads the next frame from the webcam and stores in the framebuffer.
|
||||||
func (camera *Camera) Read() bool {
|
func (camera *Camera) Read() bool {
|
||||||
// If cmd is nil, video reading has not been initialized.
|
// If cmd is nil, video reading has not been initialized.
|
||||||
if camera.cmd == nil {
|
if camera.cmd == nil {
|
||||||
|
@ -159,6 +167,7 @@ func (camera *Camera) Read() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closes the pipe and stops the ffmpeg process.
|
||||||
func (camera *Camera) Close() {
|
func (camera *Camera) Close() {
|
||||||
if camera.pipe != nil {
|
if camera.pipe != nil {
|
||||||
(*camera.pipe).Close()
|
(*camera.pipe).Close()
|
||||||
|
@ -169,7 +178,7 @@ func (camera *Camera) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stops the "cmd" process running when the user presses Ctrl+C.
|
// Stops the "cmd" process running when the user presses Ctrl+C.
|
||||||
// https://stackoverflow.com/questions/11268943/is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in-a-defe
|
// https://stackoverflow.com/questions/11268943/is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in-a-defe.
|
||||||
func (camera *Camera) cleanup() {
|
func (camera *Camera) cleanup() {
|
||||||
c := make(chan os.Signal)
|
c := make(chan os.Signal)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
12
utils.go
12
utils.go
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Returns true if file exists, false otherwise.
|
// Returns true if file exists, false otherwise.
|
||||||
// https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go
|
// https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go.
|
||||||
func exists(filename string) bool {
|
func exists(filename string) bool {
|
||||||
_, err := os.Stat(filename)
|
_, err := os.Stat(filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -105,7 +105,7 @@ func parseDevices(buffer []byte) []string {
|
||||||
alt string
|
alt string
|
||||||
}
|
}
|
||||||
// Parses ffmpeg output to get device names. Windows only.
|
// Parses ffmpeg output to get device names. Windows only.
|
||||||
// Uses parsing approach from https://github.com/imageio/imageio/blob/master/imageio/plugins/ffmpeg.py#L681
|
// Uses parsing approach from https://github.com/imageio/imageio/blob/master/imageio/plugins/ffmpeg.py#L681.
|
||||||
|
|
||||||
pairs := []Pair{}
|
pairs := []Pair{}
|
||||||
// Find all device names surrounded by quotes. E.g "Windows Camera Front"
|
// Find all device names surrounded by quotes. E.g "Windows Camera Front"
|
||||||
|
@ -125,7 +125,7 @@ func parseDevices(buffer []byte) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
devices := []string{}
|
devices := []string{}
|
||||||
// If two devices have the same name, use the alternate name of the later device as its name
|
// If two devices have the same name, use the alternate name of the later device as its name.
|
||||||
for _, pair := range pairs {
|
for _, pair := range pairs {
|
||||||
if contains(devices, pair.name) {
|
if contains(devices, pair.name) {
|
||||||
devices = append(devices, pair.alt)
|
devices = append(devices, pair.alt)
|
||||||
|
@ -155,7 +155,7 @@ func parseWebcamData(buffer []byte, camera *Camera) {
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
bufferstr = bufferstr[index:]
|
bufferstr = bufferstr[index:]
|
||||||
// Dimensions. widthxheight
|
// Dimensions. widthxheight.
|
||||||
regex := regexp.MustCompile("\\d{2,}x\\d{2,}")
|
regex := regexp.MustCompile("\\d{2,}x\\d{2,}")
|
||||||
match := regex.FindString(bufferstr)
|
match := regex.FindString(bufferstr)
|
||||||
if len(match) > 0 {
|
if len(match) > 0 {
|
||||||
|
@ -163,7 +163,7 @@ func parseWebcamData(buffer []byte, camera *Camera) {
|
||||||
camera.width = int(parse(split[0]))
|
camera.width = int(parse(split[0]))
|
||||||
camera.height = int(parse(split[1]))
|
camera.height = int(parse(split[1]))
|
||||||
}
|
}
|
||||||
// FPS
|
// FPS.
|
||||||
regex = regexp.MustCompile("\\d+(.\\d+)? fps")
|
regex = regexp.MustCompile("\\d+(.\\d+)? fps")
|
||||||
match = regex.FindString(bufferstr)
|
match = regex.FindString(bufferstr)
|
||||||
if len(match) > 0 {
|
if len(match) > 0 {
|
||||||
|
@ -173,7 +173,7 @@ func parseWebcamData(buffer []byte, camera *Camera) {
|
||||||
}
|
}
|
||||||
camera.fps = parse(match)
|
camera.fps = parse(match)
|
||||||
}
|
}
|
||||||
// Codec
|
// Codec.
|
||||||
regex = regexp.MustCompile("Video: .+,")
|
regex = regexp.MustCompile("Video: .+,")
|
||||||
match = regex.FindString(bufferstr)
|
match = regex.FindString(bufferstr)
|
||||||
if len(match) > 0 {
|
if len(match) > 0 {
|
||||||
|
|
43
video.go
43
video.go
|
@ -9,21 +9,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Video struct {
|
type Video struct {
|
||||||
filename string
|
filename string // Video Filename.
|
||||||
width int
|
width int // Width of frames.
|
||||||
height int
|
height int // Height of frames.
|
||||||
depth int
|
depth int // Depth of frames.
|
||||||
bitrate int
|
bitrate int // Bitrate for video encoding.
|
||||||
frames int
|
frames int // Total number of frames.
|
||||||
duration float64
|
duration float64 // Duration of video in seconds.
|
||||||
fps float64
|
fps float64 // Frames per second.
|
||||||
codec string
|
codec string // Codec used for video encoding.
|
||||||
pix_fmt string
|
pix_fmt string // Pixel format video is stored in.
|
||||||
framebuffer []byte
|
framebuffer []byte // Raw frame data.
|
||||||
pipe *io.ReadCloser
|
pipe *io.ReadCloser // Stdout pipe for ffmpeg process.
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd // ffmpeg command.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a new Video struct.
|
||||||
|
// Uses ffprobe to get video information and fills in the Video struct with this data.
|
||||||
func NewVideo(filename string) *Video {
|
func NewVideo(filename string) *Video {
|
||||||
if !exists(filename) {
|
if !exists(filename) {
|
||||||
panic("File: " + filename + " does not exist")
|
panic("File: " + filename + " does not exist")
|
||||||
|
@ -59,7 +61,7 @@ func NewVideo(filename string) *Video {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Wait for ffprobe command to complete.
|
||||||
if err := cmd.Wait(); err != nil {
|
if err := cmd.Wait(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -68,10 +70,12 @@ func NewVideo(filename string) *Video {
|
||||||
return video
|
return video
|
||||||
}
|
}
|
||||||
|
|
||||||
func initVideoStream(video *Video) {
|
// Once the user calls Read() for the first time on a Video struct,
|
||||||
|
// the ffmpeg command which is used to read the video is started.
|
||||||
|
func initVideo(video *Video) {
|
||||||
// If user exits with Ctrl+C, stop ffmpeg process.
|
// If user exits with Ctrl+C, stop ffmpeg process.
|
||||||
video.cleanup()
|
video.cleanup()
|
||||||
|
// ffmpeg command to pipe video data to stdout in 8-bit RGB format.
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
"-i", video.filename,
|
"-i", video.filename,
|
||||||
|
@ -93,10 +97,12 @@ func initVideoStream(video *Video) {
|
||||||
video.framebuffer = make([]byte, video.width*video.height*video.depth)
|
video.framebuffer = make([]byte, video.width*video.height*video.depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reads the next frame from the video and stores in the framebuffer.
|
||||||
|
// If thelast frame has been read, returns false, otherwise true.
|
||||||
func (video *Video) Read() bool {
|
func (video *Video) Read() bool {
|
||||||
// If cmd is nil, video reading has not been initialized.
|
// If cmd is nil, video reading has not been initialized.
|
||||||
if video.cmd == nil {
|
if video.cmd == nil {
|
||||||
initVideoStream(video)
|
initVideo(video)
|
||||||
}
|
}
|
||||||
total := 0
|
total := 0
|
||||||
for total < video.width*video.height*video.depth {
|
for total < video.width*video.height*video.depth {
|
||||||
|
@ -110,6 +116,7 @@ func (video *Video) Read() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closes the pipe and stops the ffmpeg process.
|
||||||
func (video *Video) Close() {
|
func (video *Video) Close() {
|
||||||
if video.pipe != nil {
|
if video.pipe != nil {
|
||||||
(*video.pipe).Close()
|
(*video.pipe).Close()
|
||||||
|
@ -120,7 +127,7 @@ func (video *Video) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stops the "cmd" process running when the user presses Ctrl+C.
|
// Stops the "cmd" process running when the user presses Ctrl+C.
|
||||||
// https://stackoverflow.com/questions/11268943/is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in-a-defe
|
// https://stackoverflow.com/questions/11268943/is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in-a-defe.
|
||||||
func (video *Video) cleanup() {
|
func (video *Video) cleanup() {
|
||||||
c := make(chan os.Signal)
|
c := make(chan os.Signal)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
|
@ -11,30 +11,32 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type VideoWriter struct {
|
type VideoWriter struct {
|
||||||
filename string
|
filename string // Output filename.
|
||||||
width int
|
width int // Frame width.
|
||||||
height int
|
height int // Frame height.
|
||||||
bitrate int
|
bitrate int // Output video bitrate.
|
||||||
loop int // For GIFs. -1=no loop, 0=loop forever, >0=loop n times
|
loop int // Number of times for GIF to loop.
|
||||||
delay int // Delay for Final Frame of GIFs.
|
delay int // Delay of final frame of GIF. Default -1 (same delay as previous frame).
|
||||||
macro int
|
macro int // Macroblock size for determining how to resize frames for codecs.
|
||||||
fps float64
|
fps float64 // Frames per second for output video. Default 25.
|
||||||
quality float64
|
quality float64 // Used if bitrate not given. Default 0.5.
|
||||||
codec string
|
codec string // Codec to encode video with. Default libx264.
|
||||||
pipe *io.WriteCloser
|
pipe *io.WriteCloser // Stdout pipe of ffmpeg process.
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd // ffmpeg command.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optional parameters for VideoWriter.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
bitrate int
|
bitrate int // Bitrate.
|
||||||
loop int
|
loop int // For GIFs only. -1=no loop, 0=infinite loop, >0=number of loops.
|
||||||
delay int
|
delay int // Delay for final frame of GIFs.
|
||||||
macro int
|
macro int // Macroblock size for determining how to resize frames for codecs.
|
||||||
fps float64
|
fps float64 // Frames per second for output video.
|
||||||
quality float64
|
quality float64 // If bitrate not given, use quality instead. Must be between 0 and 1. 0:best, 1:worst.
|
||||||
codec string
|
codec string // Codec for video.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a new VideoWriter struct with default values from the Options struct.
|
||||||
func NewVideoWriter(filename string, width, height int, options *Options) *VideoWriter {
|
func NewVideoWriter(filename string, width, height int, options *Options) *VideoWriter {
|
||||||
// Check if ffmpeg is installed on the users machine.
|
// Check if ffmpeg is installed on the users machine.
|
||||||
checkExists("ffmpeg")
|
checkExists("ffmpeg")
|
||||||
|
@ -45,12 +47,12 @@ func NewVideoWriter(filename string, width, height int, options *Options) *Video
|
||||||
writer.height = height
|
writer.height = height
|
||||||
|
|
||||||
// Default Parameter options logic from:
|
// Default Parameter options logic from:
|
||||||
// https://github.com/imageio/imageio-ffmpeg/blob/master/imageio_ffmpeg/_io.py#L268
|
// https://github.com/imageio/imageio-ffmpeg/blob/master/imageio_ffmpeg/_io.py#L268.
|
||||||
|
|
||||||
// GIF settings
|
// GIF settings
|
||||||
writer.loop = options.loop // Default to infinite loop
|
writer.loop = options.loop // Default to infinite loop.
|
||||||
if options.delay == 0 {
|
if options.delay == 0 {
|
||||||
writer.delay = -1 // Default to frame delay of previous frame
|
writer.delay = -1 // Default to frame delay of previous frame.
|
||||||
} else {
|
} else {
|
||||||
writer.delay = options.delay
|
writer.delay = options.delay
|
||||||
}
|
}
|
||||||
|
@ -87,26 +89,28 @@ func NewVideoWriter(filename string, width, height int, options *Options) *Video
|
||||||
return &writer
|
return &writer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Once the user calls Write() for the first time on a VideoWriter struct,
|
||||||
|
// the ffmpeg command which is used to write to the video file is started.
|
||||||
func initVideoWriter(writer *VideoWriter) {
|
func initVideoWriter(writer *VideoWriter) {
|
||||||
// If user exits with Ctrl+C, stop ffmpeg process.
|
// If user exits with Ctrl+C, stop ffmpeg process.
|
||||||
writer.cleanup()
|
writer.cleanup()
|
||||||
|
// ffmpeg command to write to video file. Takes in bytes from Stdin and encodes them.
|
||||||
command := []string{
|
command := []string{
|
||||||
"-y", // overwrite output file if it exists
|
"-y", // overwrite output file if it exists.
|
||||||
"-loglevel", "quiet",
|
"-loglevel", "quiet",
|
||||||
"-f", "rawvideo",
|
"-f", "rawvideo",
|
||||||
"-vcodec", "rawvideo",
|
"-vcodec", "rawvideo",
|
||||||
"-s", fmt.Sprintf("%dx%d", writer.width, writer.height), // frame w x h
|
"-s", fmt.Sprintf("%dx%d", writer.width, writer.height), // frame w x h.
|
||||||
"-pix_fmt", "rgb24",
|
"-pix_fmt", "rgb24",
|
||||||
"-r", fmt.Sprintf("%.02f", writer.fps), // frames per second
|
"-r", fmt.Sprintf("%.02f", writer.fps), // frames per second.
|
||||||
"-i", "-", // The input comes from stdin
|
"-i", "-", // The input comes from stdin.
|
||||||
"-an", // Tells ffmpeg not to expect any audio
|
"-an", // Tells ffmpeg not to expect any audio.
|
||||||
"-vcodec", writer.codec,
|
"-vcodec", writer.codec,
|
||||||
"-pix_fmt", "yuv420p", // Output is 8-bit RGB, no alpha
|
"-pix_fmt", "yuv420p", // Output is 8-bit RGB, no alpha.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code from the imageio-ffmpeg project.
|
// Code from the imageio-ffmpeg project.
|
||||||
// https://github.com/imageio/imageio-ffmpeg/blob/master/imageio_ffmpeg/_io.py#L399
|
// https://github.com/imageio/imageio-ffmpeg/blob/master/imageio_ffmpeg/_io.py#L399.
|
||||||
// If bitrate not given, use a default.
|
// If bitrate not given, use a default.
|
||||||
if writer.bitrate == 0 {
|
if writer.bitrate == 0 {
|
||||||
if writer.codec == "libx264" {
|
if writer.codec == "libx264" {
|
||||||
|
@ -119,7 +123,7 @@ func initVideoWriter(writer *VideoWriter) {
|
||||||
} else {
|
} else {
|
||||||
command = append(command, "-b:v", fmt.Sprintf("%d", writer.bitrate))
|
command = append(command, "-b:v", fmt.Sprintf("%d", writer.bitrate))
|
||||||
}
|
}
|
||||||
|
// For GIFs, add looping and delay parameters.
|
||||||
if strings.HasSuffix(strings.ToLower(writer.filename), ".gif") {
|
if strings.HasSuffix(strings.ToLower(writer.filename), ".gif") {
|
||||||
command = append(
|
command = append(
|
||||||
command,
|
command,
|
||||||
|
@ -129,7 +133,7 @@ func initVideoWriter(writer *VideoWriter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code from the imageio-ffmpeg project:
|
// Code from the imageio-ffmpeg project:
|
||||||
// https://github.com/imageio/imageio-ffmpeg/blob/master/imageio_ffmpeg/_io.py#L415
|
// https://github.com/imageio/imageio-ffmpeg/blob/master/imageio_ffmpeg/_io.py#L415.
|
||||||
// Resizes the video frames to a size that works with most codecs.
|
// Resizes the video frames to a size that works with most codecs.
|
||||||
if writer.macro > 1 {
|
if writer.macro > 1 {
|
||||||
if writer.width%writer.macro > 0 || writer.height%writer.macro > 0 {
|
if writer.width%writer.macro > 0 || writer.height%writer.macro > 0 {
|
||||||
|
@ -162,6 +166,7 @@ func initVideoWriter(writer *VideoWriter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes the given frame to the video file.
|
||||||
func (writer *VideoWriter) Write(frame []byte) {
|
func (writer *VideoWriter) Write(frame []byte) {
|
||||||
// If cmd is nil, video writing has not been set up.
|
// If cmd is nil, video writing has not been set up.
|
||||||
if writer.cmd == nil {
|
if writer.cmd == nil {
|
||||||
|
@ -178,6 +183,7 @@ func (writer *VideoWriter) Write(frame []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closes the pipe and stops the ffmpeg process.
|
||||||
func (writer *VideoWriter) Close() {
|
func (writer *VideoWriter) Close() {
|
||||||
if writer.pipe != nil {
|
if writer.pipe != nil {
|
||||||
(*writer.pipe).Close()
|
(*writer.pipe).Close()
|
||||||
|
@ -188,7 +194,7 @@ func (writer *VideoWriter) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stops the "cmd" process running when the user presses Ctrl+C.
|
// Stops the "cmd" process running when the user presses Ctrl+C.
|
||||||
// https://stackoverflow.com/questions/11268943/is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in-a-defe
|
// https://stackoverflow.com/questions/11268943/is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in-a-defe.
|
||||||
func (writer *VideoWriter) cleanup() {
|
func (writer *VideoWriter) cleanup() {
|
||||||
c := make(chan os.Signal)
|
c := make(chan os.Signal)
|
||||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue