diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18ff679..2555324 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,12 +5,12 @@ jobs: test_on_mac: runs-on: macos-latest steps: - - name: install gstreamer - run: brew install libusb pkg-config gstreamer gst-plugins-bad gst-plugins-good gst-plugins-base gst-plugins-ugly + #- name: install gstreamer + # run: brew install libusb pkg-config gstreamer gst-plugins-bad gst-plugins-good gst-plugins-base gst-plugins-ugly - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.16.x + go-version: 1.19.x - name: Checkout code uses: actions/checkout@v2 - name: compile @@ -23,17 +23,17 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.16.x + go-version: 1.19.x - name: Checkout code uses: actions/checkout@v2 - name: update run: sudo apt-get update - name: install libusb run: sudo apt-get install -y libusb-1.0-0-dev - - name: installlibglib - run: sudo apt-get install -y libglib2.0-dev - - name: install gstreamer - run: sudo apt-get install -y libgstreamer1.0-0 libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio + #- name: installlibglib + # run: sudo apt-get install -y libglib2.0-dev + #- name: install gstreamer + # run: sudo apt-get install -y libgstreamer1.0-0 libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio - name: compile run: go build - name: run go test diff --git a/go.mod b/go.mod index 44bfa4d..9929c1d 100644 --- a/go.mod +++ b/go.mod @@ -1,17 +1,22 @@ module github.com/danielpaulus/quicktime_video_hack -go 1.16 +go 1.19 require ( github.com/danielpaulus/go-ios v1.0.13 - github.com/danielpaulus/gst v0.0.0-20200201205042-e6d2974fceb8 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 - github.com/google/gousb v2.1.0+incompatible - github.com/lijo-jose/glib v0.0.0-20191012030101-93ee72d7d646 - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/google/gousb v1.1.2 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.6.0 github.com/stretchr/testify v1.6.1 - golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect - gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + howett.net/plist v0.0.0-20200419221736-3b63eb3a43b5 // indirect ) diff --git a/go.sum b/go.sum index f5ea51a..60d8f3d 100644 --- a/go.sum +++ b/go.sum @@ -1,49 +1,41 @@ github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/danielpaulus/go-ios v1.0.12 h1:B0pirHZibKBanZgxZGrOwoejLlEb9zgtde7eHORxLVA= -github.com/danielpaulus/go-ios v1.0.12/go.mod h1:k+X7QB2ffFOhTkly/nNwvV1VIysif1MWS1s8GgYXpU4= github.com/danielpaulus/go-ios v1.0.13 h1:MFc/QOcNXRY2vv/KRijES/sI0+aHTXfzgEeK6qsZOOU= github.com/danielpaulus/go-ios v1.0.13/go.mod h1:k+X7QB2ffFOhTkly/nNwvV1VIysif1MWS1s8GgYXpU4= -github.com/danielpaulus/gst v0.0.0-20200201205042-e6d2974fceb8 h1:+XiTgRoo1bCA3paC4e/0WYWI7+J2O7hR/IYuSikANMw= -github.com/danielpaulus/gst v0.0.0-20200201205042-e6d2974fceb8/go.mod h1:JbhjLST5AaUXpKQK65g9144BK8QHftbpuFoYuhDuONw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/google/gousb v2.1.0+incompatible h1:ApzMDjF3FeO219QwWybJxYfFhXQzPLOEy0o+w9k5DNI= -github.com/google/gousb v2.1.0+incompatible/go.mod h1:Tl4HdAs1ThE3gECkNwz+1MWicX6FXddhJEw7L8jRDiI= +github.com/google/gousb v1.1.2 h1:1BwarNB3inFTFhPgUEfah4hwOPuDz/49I0uX8XNginU= +github.com/google/gousb v1.1.2/go.mod h1:GGWUkK0gAXDzxhwrzetW592aOmkkqSGcj5KLEgmCVUg= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lijo-jose/glib v0.0.0-20191012030101-93ee72d7d646 h1:7I8sylThkL59rDoHMANuIxtB490DvrLAIZosNY9fDMM= -github.com/lijo-jose/glib v0.0.0-20191012030101-93ee72d7d646/go.mod h1:wzypjnJX+g/LKnKDVvJni/u0gNlQestTwv6Kt/Qf3fk= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a h1:i47hUS795cOydZI4AwJQCKXOr4BvxzvikwDoDtHhP2Y= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index bc22e68..4a5bd2a 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ Usage: qvh deactivate [--udid=] [-v] qvh record [--udid=] [-v] qvh audio (--mp3 | --ogg | --wav) [--udid=] [-v] - qvh gstreamer [--pipeline=] [--examples] [--udid=] [-v] + qvh gstreamer [--examples] [--udid=] [-v] qvh diagnostics [--dump=] [--udid=] qvh --version | version @@ -57,8 +57,7 @@ The commands work as following: gstreamer If no additional param is provided, qvh will open a new window and push AV data to gstreamer. If "qvh gstreamer --examples" is provided, qvh will print some common gstreamer pipeline examples. - If --pipeline is provided, qvh will use the provided gstreamer pipeline instead of - displaying audio and video in a window. + diagnostics The diagnostics mode is added for running longterm tests to debug and ensure stability. It will log several metrics and debug logs. Optionally specify a dump file with the --dump option that @@ -122,10 +121,10 @@ The commands work as following: return } if ogg { - recordAudioGst(outfile, device, gstadapter.OGG) + // recordAudioGst(outfile, device, gstadapter.OGG) TODO: fixme return } - recordAudioGst(outfile, device, gstadapter.MP3) + //recordAudioGst(outfile, device, gstadapter.MP3) TODO: fixme return } @@ -172,12 +171,8 @@ The commands work as following: printExamples() return } - gstPipeline, _ := arguments.String("--pipeline") - if gstPipeline == "" { - startGStreamer(device) - return - } - startGStreamerWithCustomPipeline(device, gstPipeline) + startGStreamer(device) + return } } @@ -230,13 +225,13 @@ func printExamples() { } func recordAudioGst(outfile string, device screencapture.IosDevice, audiotype string) { - log.Debug("Starting Gstreamer with audio pipeline") + /*log.Debug("Starting Gstreamer with audio pipeline") gStreamer, err := gstadapter.NewWithAudioPipeline(outfile, audiotype) if err != nil { printErrJSON(err, "Failed creating custom pipeline") return } - startWithConsumer(gStreamer, device, true) + startWithConsumer(gStreamer, device, true)*/ } func runDiagnostics(outfile string, dump bool, dumpFile string, device screencapture.IosDevice) { @@ -281,20 +276,15 @@ func recordAudioWav(outfile string, device screencapture.IosDevice) { startWithConsumer(wavFileWriter, device, true) } -func startGStreamerWithCustomPipeline(device screencapture.IosDevice, pipelineString string) { - log.Debug("Starting Gstreamer with custom pipeline") - gStreamer, err := gstadapter.NewWithCustomPipeline(pipelineString) +func startGStreamer(device screencapture.IosDevice) { + log.Debug("Starting Gstreamer") + tcpServerWriter, err := gstadapter.StartTcpWriter() if err != nil { - printErrJSON(err, "Failed creating custom pipeline") + printErrJSON(err, "Failed starting TCP servers") return } - startWithConsumer(gStreamer, device, false) -} - -func startGStreamer(device screencapture.IosDevice) { - log.Debug("Starting Gstreamer") - gStreamer := gstadapter.New() - startWithConsumer(gStreamer, device, false) + log.Fatalf("boom") + startWithConsumer(tcpServerWriter, device, false) } // Just dump a list of what was discovered to the console @@ -329,17 +319,17 @@ func activate(device screencapture.IosDevice) { } func deactivate(device screencapture.IosDevice) { - log.Debugf("Disabling device: %v", device) - var err error - device, err = screencapture.DisableQTConfig(device) - if err != nil { - printErrJSON(err, "Error disabling QT config") - return - } - - printJSON(map[string]interface{}{ - "device_activated": device.DetailsMap(), - }) + log.Debugf("Disabling device: %v", device) + var err error + device, err = screencapture.DisableQTConfig(device) + if err != nil { + printErrJSON(err, "Error disabling QT config") + return + } + + printJSON(map[string]interface{}{ + "device_activated": device.DetailsMap(), + }) } func record(h264FilePath string, wavFilePath string, device screencapture.IosDevice) { diff --git a/screencapture/gstadapter/gst_adapter.go b/screencapture/gstadapter/gst_adapter.go index 88c71c6..22a5e64 100644 --- a/screencapture/gstadapter/gst_adapter.go +++ b/screencapture/gstadapter/gst_adapter.go @@ -1,5 +1,6 @@ package gstadapter +/* import ( "encoding/binary" "fmt" @@ -310,3 +311,4 @@ func prependMarker(nalu []byte, length uint32) []byte { copy(naluWithAnnexBMarker[4:], nalu) return naluWithAnnexBMarker } +*/ diff --git a/screencapture/gstadapter/gst_adapter_test.go b/screencapture/gstadapter/gst_adapter_test.go index 9025319..7c507a3 100644 --- a/screencapture/gstadapter/gst_adapter_test.go +++ b/screencapture/gstadapter/gst_adapter_test.go @@ -1,5 +1,6 @@ package gstadapter_test +/* import ( "os" "testing" @@ -36,3 +37,4 @@ func TestCustomPipelineParsing(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, gsta) } +*/ diff --git a/screencapture/gstadapter/gst_pipeline_builder_linux.go b/screencapture/gstadapter/gst_pipeline_builder_linux.go index 81c3a22..1e90f3f 100644 --- a/screencapture/gstadapter/gst_pipeline_builder_linux.go +++ b/screencapture/gstadapter/gst_pipeline_builder_linux.go @@ -1,7 +1,9 @@ +//go:build linux // +build linux package gstadapter +/* import "github.com/danielpaulus/gst" func setupLivePlayAudio(pl *gst.Pipeline) { @@ -9,7 +11,7 @@ func setupLivePlayAudio(pl *gst.Pipeline) { /*hack: I do not know why, but audio on my linux box wont play when using a simple wavpars. On MAC OS it works without any problems though. A hacky workaround to get audio playing that I came up with was to encode audio into ogg/vorbis and directly decode it again. - */ +*/ /* vorbisenc := gst.ElementFactoryMake("vorbisenc", "vorbisenc_01") checkElem(vorbisenc, "vorbisenc_01") @@ -79,3 +81,4 @@ func setUpVideoPipeline(pl *gst.Pipeline) *gst.AppSrc { queue3.Link(sink) return asrc } +*/ diff --git a/screencapture/gstadapter/gst_pipeline_builder_mac.go b/screencapture/gstadapter/gst_pipeline_builder_mac.go index 820b44c..3dbb1a8 100644 --- a/screencapture/gstadapter/gst_pipeline_builder_mac.go +++ b/screencapture/gstadapter/gst_pipeline_builder_mac.go @@ -1,7 +1,9 @@ +//go:build darwin // +build darwin package gstadapter +/* import "github.com/danielpaulus/gst" func setupLivePlayAudio(pl *gst.Pipeline) { @@ -51,3 +53,4 @@ func setUpVideoPipeline(pl *gst.Pipeline) *gst.AppSrc { queue3.Link(sink) return asrc } +*/ diff --git a/screencapture/gstadapter/tcp_server_adapter.go b/screencapture/gstadapter/tcp_server_adapter.go new file mode 100644 index 0000000..dad6437 --- /dev/null +++ b/screencapture/gstadapter/tcp_server_adapter.go @@ -0,0 +1,158 @@ +package gstadapter + +import ( + "encoding/binary" + "github.com/danielpaulus/quicktime_video_hack/screencapture/coremedia" + log "github.com/sirupsen/logrus" + "io" + "net" +) + +var startCode = []byte{00, 00, 00, 01} + +//AVFileWriter writes nalus into a file using 0x00000001 as a separator (h264 ANNEX B) and raw pcm audio into a wav file +//Note that you will have to call WriteWavHeader() on the audiofile when you are done to write a wav header and get a valid file. +type TCPServerWriter struct { + h264FileWriter io.Writer + wavFileWriter io.Writer + outFilePath string + audioOnly bool + videoConnWaiter chan net.Conn + audioConnWaiter chan net.Conn + errReceiver chan error +} + +const ( + CONN_HOST = "localhost" + CONN_PORT = "3333" + AUDIO_CONN_PORT = "3334" + CONN_TYPE = "tcp" +) + +//NewAVFileWriter binary writes nalus in annex b format to the given writer and audio buffers into a wav file. +//Note that you will have to call WriteWavHeader() on the audiofile when you are done to write a wav header and get a valid file. +func StartTcpWriter() (TCPServerWriter, error) { + // Listen for incoming connections. + videoConnWaiter := make(chan net.Conn) + audioConnWaiter := make(chan net.Conn) + errReceiver := make(chan error) + go func() { + l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+CONN_PORT) + if err != nil { + log.Error("Error listening:", err.Error()) + errReceiver <- err + return + } + log.Info("waiting for video connection... on 3333") + conn, err := l.Accept() + if err != nil { + log.Error("Error accepting: ", err.Error()) + errReceiver <- err + return + } + log.Info("ok!") + videoConnWaiter <- conn + }() + + go func() { + l, err := net.Listen(CONN_TYPE, CONN_HOST+":"+AUDIO_CONN_PORT) + if err != nil { + log.Error("Error listening:", err.Error()) + errReceiver <- err + return + } + log.Info("waiting for audio connection... on 3334") + conn, err := l.Accept() + if err != nil { + log.Error("Error accepting: ", err.Error()) + errReceiver <- err + return + } + log.Info("ok!") + audioConnWaiter <- conn + }() + + var wavWriter io.Writer + var h264Writer io.Writer + select { + case ac := <-audioConnWaiter: + wavWriter = ac + case err := <-errReceiver: + return TCPServerWriter{}, err + } + select { + case vc := <-videoConnWaiter: + h264Writer = vc + case err := <-errReceiver: + return TCPServerWriter{}, err + } + return TCPServerWriter{h264FileWriter: h264Writer, wavFileWriter: wavWriter, audioOnly: false}, nil +} + +//Consume writes PPS and SPS as well as sample bufs into a annex b .h264 file and audio samples into a wav file +//Note that you will have to call WriteWavHeader() on the audiofile when you are done to write a wav header and get a valid file. +func (avfw TCPServerWriter) Consume(buf coremedia.CMSampleBuffer) error { + if buf.MediaType == coremedia.MediaTypeSound { + return avfw.consumeAudio(buf) + } + if avfw.audioOnly { + return nil + } + return avfw.consumeVideo(buf) +} + +//Nothing currently +func (avfw TCPServerWriter) Stop() {} + +func (avfw TCPServerWriter) consumeVideo(buf coremedia.CMSampleBuffer) error { + if buf.HasFormatDescription { + err := avfw.writeNalu(buf.FormatDescription.PPS) + if err != nil { + return err + } + err = avfw.writeNalu(buf.FormatDescription.SPS) + if err != nil { + return err + } + } + if !buf.HasSampleData() { + return nil + } + return avfw.writeNalus(buf.SampleData) +} + +func (avfw TCPServerWriter) writeNalus(bytes []byte) error { + slice := bytes + for len(slice) > 0 { + length := binary.BigEndian.Uint32(slice) + err := avfw.writeNalu(slice[4 : length+4]) + if err != nil { + return err + } + slice = slice[length+4:] + } + return nil +} + +func (avfw TCPServerWriter) writeNalu(naluBytes []byte) error { + _, err := avfw.h264FileWriter.Write(startCode) + if err != nil { + return err + } + _, err = avfw.h264FileWriter.Write(naluBytes) + if err != nil { + return err + } + return nil +} + +func (avfw TCPServerWriter) consumeAudio(buffer coremedia.CMSampleBuffer) error { + if !buffer.HasSampleData() { + return nil + } + _, err := avfw.wavFileWriter.Write(buffer.SampleData) + if err != nil { + return err + } + return nil +}