pion / example-webrtc-applications

Examples of WebRTC applications that are large, or use 3rd party libraries

Home Page:https://pion.ly/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Receive audio with ffmpeg

projetoarduino opened this issue · comments

I'm trying to change the gstreamer-receive example to use ffmpeg, can someone tell me what I'm doing wrong because I don't hear any audio when I run the software

package main

import (
	"fmt"
	"time"
	"os"
    "os/exec"

	"github.com/pion/rtcp"
	"github.com/pion/webrtc/v3"
	//gst "gstreamer-sink"
)

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func main() {
	// Prepare the configuration
	config := webrtc.Configuration{
		ICEServers: []webrtc.ICEServer{
			{
				URLs: []string{"stun:stun.l.google.com:19302"},
			},
		},
	}

	// Create a new RTCPeerConnection
	peerConnection, err := webrtc.NewPeerConnection(config)
	if err != nil {
		panic(err)
	}

	// Set a handler for when a new remote track starts, this handler creates a gstreamer pipeline
	// for the given codec
	peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {

		// Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval
		go func() {
			ticker := time.NewTicker(time.Second * 3)
			for range ticker.C {
				rtcpSendErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}})
				if rtcpSendErr != nil {
					fmt.Println(rtcpSendErr)
				}				
			}
		}()

		//codecName := strings.Split(track.Codec().RTPCodecCapability.MimeType, "/")[1]
		//fmt.Printf("Track has started, of type %d: %s \n", track.PayloadType(), codecName)
		//pipeline := gst.CreatePipeline(track.PayloadType(), strings.ToLower(codecName))
		//pipeline.Start()
		
		buf := make([]byte, 1400)
		chBuff := make(chan []byte, 1400)

		go playTrack(chBuff)

		for {
			i, _, readErr := track.Read(buf)
			if readErr != nil {
				panic(err)
			}
			chBuff <- buf[:i]
			//pipeline.Push(buf[:i])
			//fmt.Printf("%x", buf[:i])
			//fmt.Println(track.PayloadType())			
		}
	})

	// Set the handler for ICE connection state
	// This will notify you when the peer has connected/disconnected
	peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
		fmt.Printf("Connection State has changed %s \n", connectionState.String())
	})

	// Wait for the offer to be pasted
	offer := webrtc.SessionDescription{}
	Decode(MustReadStdin(), &offer)

	// Set the remote SessionDescription
	err = peerConnection.SetRemoteDescription(offer)
	if err != nil {
		panic(err)
	}

	// Create an answer
	answer, err := peerConnection.CreateAnswer(nil)
	if err != nil {
		panic(err)
	}

	// Create channel that is blocked until ICE Gathering is complete
	gatherComplete := webrtc.GatheringCompletePromise(peerConnection)

	// Sets the LocalDescription, and starts our UDP listeners
	err = peerConnection.SetLocalDescription(answer)
	if err != nil {
		panic(err)
	}

	// Block until ICE Gathering is complete, disabling trickle ICE
	// we do this because we only can exchange one signaling message
	// in a production application you should exchange ICE Candidates via OnICECandidate
	<-gatherComplete

	// Output the answer in base64 so we can paste it in browser
	fmt.Println(Encode(*peerConnection.LocalDescription()))

	// Block forever
	select {}
}

func playTrack(ch <-chan []byte){
	//cmd := exec.Command("ffmpeg", "-i", "pipe:0", "-f", "alsa", "default")
	cmd:= exec.Command("ffmpeg", "-i", "pipe:0", "-c:a", "copy", "-sample_fmt", "s16p", "-ssrc", "1", "-payload_type", "111",  "-b", "96k", "-f", "alsa", "default")

    cmd.Stderr = os.Stderr // bind log stream to stderr
    //cmd.Stdout = resultBuffer // stdout result will be written here

    stdin, err := cmd.StdinPipe() // Open stdin pipe
    check(err)

    err = cmd.Start() // Start a process on another goroutine
    check(err)

	for {
    	_, err = stdin.Write(<-ch) // pump audio data to stdin pipe
    	check(err)
	}

    err = stdin.Close() // close the stdin, or ffmpeg will wait forever
    check(err)

    err = cmd.Wait() // wait until ffmpeg finish
    check(err)
}

Hey @projetoarduino

Instead of using stdin/stdout I would send RTP over loopback. See rtp-forwarder

If that example doesn't work for you please re-open or reach out via Slack thanks!

Hi @Sean-Der

I would like to thank you for the quick response

I want to use stdin for the sake of practicality in my project, using loopback doesn't seem right

maybe if you could show me a way so i can continue my journey

Transport is hard to do with audio/video over stdin unfortunately. You will have to put audio+video inside a container (webm) but that will add extra complexity.

I have done this a few times and loopback is the best way. It allows you to send multiple streams and is portable. You also aren't ever going to run out of ports. You can listen on anything in 127.0. 0.0 – 127.255.255.255