Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for RTSP #115

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions ffmpeg/ffmpeg.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package ffmpeg

import (
"fmt"
"github.com/brutella/hc/log"
"github.com/brutella/hc/rtp"
"image"
"io/ioutil"
"os"
"sync"

"github.com/brutella/hc/log"
"github.com/brutella/hc/rtp"
)

// StreamID is the type of the stream identifier
Expand Down Expand Up @@ -45,7 +46,7 @@ type ffmpeg struct {
// If cfg specifies a video loopback, ffmpeg configures a loopback to support simultaneous access to the video device.
func New(cfg Config) *ffmpeg {
var loop *loopback = nil
if cfg.LoopbackFilename != "" {
if cfg.LoopbackFilename != "" && cfg.InputDevice != "rtsp" {
loop = NewLoopback(cfg.InputDevice, cfg.InputFilename, cfg.LoopbackFilename)
}

Expand Down Expand Up @@ -194,7 +195,7 @@ func (f *ffmpeg) videoInputDevice() string {
}

func (f *ffmpeg) videoInputFilename() string {
if f.cfg.LoopbackFilename != "" {
if f.cfg.LoopbackFilename != "" && f.cfg.InputDevice != "rtsp" {
return f.cfg.LoopbackFilename
}

Expand Down
5 changes: 4 additions & 1 deletion ffmpeg/loopback.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ func (l *loopback) Stop() {

// cmd returns a new command to stream video from the input file to the loopback file.
func (l *loopback) execCmd() *exec.Cmd {
cmd := exec.Command("ffmpeg", "-f", l.inputDevice, "-i", l.inputFilename, "-codec:v", "copy", "-f", l.inputDevice, l.loopbackFilename)
var cmd *exec.Cmd
if l.inputDevice != "rtsp" {
cmd = exec.Command("ffmpeg", "-f", l.inputDevice, "-i", l.inputFilename, "-codec:v", "copy", "-f", l.inputDevice, l.loopbackFilename)
}

log.Debug.Println(cmd)

Expand Down
8 changes: 7 additions & 1 deletion ffmpeg/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ func snapshot(width, height uint, inputDevice, inputFilename string) (*image.Ima
filePath := path.Join(os.TempDir(), fileName)

// height "-2" keeps the aspect ratio
arg := fmt.Sprintf("-f %s -framerate 30 -i %s -vf scale=%d:-2 -frames:v 1 %s", inputDevice, inputFilename, width, filePath)
var arg string
if inputDevice == "rtsp" {
arg = fmt.Sprintf("-i %s -vf scale=%d:-2 -frames:v 1 %s", inputFilename, width, filePath)
} else {
// local
arg = fmt.Sprintf("-f %s -framerate 30 -i %s -vf scale=%d:-2 -frames:v 1 %s", inputDevice, inputFilename, width, filePath)
}
args := strings.Split(arg, " ")

cmd := exec.Command("ffmpeg", args[:]...)
Expand Down
94 changes: 52 additions & 42 deletions ffmpeg/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package ffmpeg

import (
"fmt"
"github.com/brutella/hc/log"
"github.com/brutella/hc/rtp"
"os/exec"
"strings"
"syscall"

"github.com/brutella/hc/log"
"github.com/brutella/hc/rtp"
)

type stream struct {
Expand Down Expand Up @@ -42,46 +43,55 @@ func (s *stream) start(video rtp.VideoParameters, audio rtp.AudioParameters) err
// -vsync 2: Fixes "Frame rate very high for a muxer not efficiently supporting it."
// -framerate before -i specifies the framerate for the input, after -i sets it for the output https://stackoverflow.com/questions/38498599/webcam-with-ffmpeg-on-mac-selected-framerate-29-970030-is-not-supported-by-th#38549528

ffmpegVideo := fmt.Sprintf("-f %s", s.inputDevice) +
fmt.Sprintf(" -framerate %d", s.framerate(video.Attributes)) +
fmt.Sprintf("%s", s.videoDecoderOption(video)) +
fmt.Sprintf(" -re -i %s", s.inputFilename) +
" -an" +
fmt.Sprintf(" -codec:v %s", s.videoEncoder(video)) +
" -pix_fmt yuv420p -vsync vfr" +

// height "-2" keeps the aspect ratio
fmt.Sprintf(" -video_size %d:-2", video.Attributes.Width) +
fmt.Sprintf(" -framerate %d", video.Attributes.Framerate) +

// 2019-06-20 (mah)
// Specifying profiles in h264_omx was added in ffmpeg 3.3
// https://github.com/FFmpeg/FFmpeg/commit/13332504c98918447159da2a1a34e377dca360e2#diff-36301d4a4bc7200caee9fbe8e8d8cc20
// hkcam currently uses ffmpeg 3.2
// 2018-08-18 (mah)
// Disable profile arguments because it cannot be parsed
// [h264_omx @ 0x93a410] [Eval @ 0xbeaad160] Undefined constant or missing '(' in 'high'
// fmt.Sprintf(" -profile:v %s", videoProfile(video.CodecParams)) +
fmt.Sprintf(" -level:v %s", videoLevel(video.CodecParams)) +
" -f rawvideo" +
fmt.Sprintf(" -b:v %dk", s.videoBitrate(video)) +
fmt.Sprintf(" -payload_type %d", video.RTP.PayloadType) +
fmt.Sprintf(" -ssrc %d", s.resp.SsrcVideo) +
" -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80" +
fmt.Sprintf(" -srtp_out_params %s", s.req.Video.SrtpKey()) +
fmt.Sprintf(" srtp://%s:%d?rtcpport=%d&localrtcpport=%d&pkt_size=%s&timeout=60", s.req.ControllerAddr.IPAddr, s.req.ControllerAddr.VideoRtpPort, s.req.ControllerAddr.VideoRtpPort, s.req.ControllerAddr.VideoRtpPort, videoMTU(s.req))

// FIXME (mah) Audio doesn't work yet
// ffmpegAudio := "-vn" +
// fmt.Sprintf(" %s", audioCodecOption(audio)) +
// // compression-level 0-10 (fastest-slowest)
// fmt.Sprintf(" -b:a %dk -bufsize 48k", audio.RTP.Bitrate) +
// fmt.Sprintf(" -ar %s", audioSamplingRate(audio)) +
// fmt.Sprintf(" -payload_type %d", audio.RTP.PayloadType) +
// fmt.Sprintf(" -ssrc %d", s.resp.SsrcAudio) +
// " -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80" +
// fmt.Sprintf(" -srtp_out_params %s", s.req.Audio.SrtpKey()) +
// fmt.Sprintf(" srtp://%s:%d?rtcpport=%d&localrtcpport=%d&timeout=60", s.req.ControllerAddr.IPAddr, s.req.ControllerAddr.AudioRtpPort, s.req.ControllerAddr.AudioRtpPort, s.req.ControllerAddr.AudioRtpPort)
var ffmpegVideo string
if s.inputDevice == "rtsp" {
// rtsp support
ffmpegVideo = fmt.Sprintf("-re -i %s", s.inputFilename) +
fmt.Sprintf(" -an -vcodec %s -pix_fmt yuv420p -r 30 -x264-params bframes=0", s.videoEncoder(video)) +
fmt.Sprintf(" -video_size %d:-2", video.Attributes.Width) +
fmt.Sprintf(" -level:v %s", videoLevel(video.CodecParams)) +
" -f rawvideo" +
fmt.Sprintf(" -b:v %dk", s.videoBitrate(video)) +
fmt.Sprintf(" -payload_type %d", video.RTP.PayloadType) +
fmt.Sprintf(" -ssrc %d", s.resp.SsrcVideo) +
" -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80" +
fmt.Sprintf(" -srtp_out_params %s", s.req.Video.SrtpKey()) +
fmt.Sprintf(" srtp://%s:%d?rtcpport=%d&localrtcpport=%d&pkt_size=%s&timeout=120", s.req.ControllerAddr.IPAddr, s.req.ControllerAddr.VideoRtpPort, s.req.ControllerAddr.VideoRtpPort, s.req.ControllerAddr.VideoRtpPort, videoMTU(s.req))
// audio support may be added later
// fmt.Sprintf(" -map 0:1 -acodec libfdk_aac -profile:a aac_eld -flags +global_header -f null -ar 16k -b:a 24k -bufsize 24k -ac 1 -payload_type 110")+
// fmt.Sprintf(" -ssrc %d -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80 -srtp_out_params %s",s.resp.SsrcAudio, s.req.Audio.SrtpKey())+
// fmt.Sprintf(" srtp://%s:%d?rtcpport=%d&localrtcpport=%d&timeout=120", s.req.ControllerAddr.IPAddr, s.req.ControllerAddr.AudioRtpPort, s.req.ControllerAddr.AudioRtpPort, s.req.ControllerAddr.AudioRtpPort)
} else {
// default
ffmpegVideo = fmt.Sprintf("-f %s", s.inputDevice) +
fmt.Sprintf(" -framerate %d", s.framerate(video.Attributes)) +
fmt.Sprintf("%s", s.videoDecoderOption(video)) +
fmt.Sprintf(" -re -i %s", s.inputFilename) +
" -an" +
fmt.Sprintf(" -codec:v %s", s.videoEncoder(video)) +
" -pix_fmt yuv420p -vsync vfr" +

// height "-2" keeps the aspect ratio
fmt.Sprintf(" -video_size %d:-2", video.Attributes.Width) +
fmt.Sprintf(" -framerate %d", video.Attributes.Framerate) +

// 2019-06-20 (mah)
// Specifying profiles in h264_omx was added in ffmpeg 3.3
// https://github.com/FFmpeg/FFmpeg/commit/13332504c98918447159da2a1a34e377dca360e2#diff-36301d4a4bc7200caee9fbe8e8d8cc20
// hkcam currently uses ffmpeg 3.2
// 2018-08-18 (mah)
// Disable profile arguments because it cannot be parsed
// [h264_omx @ 0x93a410] [Eval @ 0xbeaad160] Undefined constant or missing '(' in 'high'
// fmt.Sprintf(" -profile:v %s", videoProfile(video.CodecParams)) +
fmt.Sprintf(" -level:v %s", videoLevel(video.CodecParams)) +
" -f rawvideo" +
fmt.Sprintf(" -b:v %dk", s.videoBitrate(video)) +
fmt.Sprintf(" -payload_type %d", video.RTP.PayloadType) +
fmt.Sprintf(" -ssrc %d", s.resp.SsrcVideo) +
" -f rtp -srtp_out_suite AES_CM_128_HMAC_SHA1_80" +
fmt.Sprintf(" -srtp_out_params %s", s.req.Video.SrtpKey()) +
fmt.Sprintf(" srtp://%s:%d?rtcpport=%d&localrtcpport=%d&pkt_size=%s&timeout=60", s.req.ControllerAddr.IPAddr, s.req.ControllerAddr.VideoRtpPort, s.req.ControllerAddr.VideoRtpPort, s.req.ControllerAddr.VideoRtpPort, videoMTU(s.req))
}

args := strings.Split(ffmpegVideo, " ")
cmd := exec.Command("ffmpeg", args[:]...)
Expand Down