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

Recording support? #59

Closed
postacik opened this issue Jul 18, 2021 · 21 comments
Closed

Recording support? #59

postacik opened this issue Jul 18, 2021 · 21 comments
Labels
enhancement New feature or request

Comments

@postacik
Copy link

The server looks very promising since it's supported with mobile and web SDKs.

However I could not see any section about recording in the documentation. Do you plan to add support for recording + processing recordings?

@davidzhao
Copy link
Member

LiveKit doesn't support recording yet. we are certainly looking at it though!

@davidzhao davidzhao added the enhancement New feature or request label Jul 19, 2021
@rodrigo-brito
Copy link

A good way to implement this is:

  • A external service that connects to the room as a viewer
  • The service get each track and store it or merge the tracks and store a single stream.

@postacik
Copy link
Author

You can check how Janus Webrtc Server does it. If you turn recording on in a room, the server saves video and audio streams of each participant as a raw recording file without any transcoding. Then it is your job to process and transcode those files in a single file.

@davidzhao
Copy link
Member

we are working on this now, if folks are interested in beta testing this, please 👍 on this post. I'll add you to the repo.

@sibis
Copy link
Contributor

sibis commented Aug 6, 2021

@davidzhao interested in this and looking to contribute as well if anything required

@davidzhao
Copy link
Member

hey all, we are ready for you to play with the recorder! Docs are here: https://docs.livekit.io/guides/recording/

Join us on slack and let us know what you think! would love to get your help in making this better.

@jibon57
Copy link

jibon57 commented Oct 13, 2021

@davidzhao First of all thanks for excellent library. Do you have any plan/example to capture media using ffmpeg similar way mediasoup is doing? For my case I don't want to use screen recorder. Instated of that capture media & later process.

@davidzhao
Copy link
Member

hey @jibon57, check out our Go SDK for this use case. It lets you sit in a room as a participant and save the tracks into whichever format that suits your needs.

@jibon57
Copy link

jibon57 commented Oct 13, 2021

@davidzhao perfect! Thank you!

@mikhailbolshakov
Copy link

@davidzhao perfect! Thank you!

@davidzhao First of all thanks for excellent library. Do you have any plan/example to capture media using ffmpeg similar way mediasoup is doing? For my case I don't want to use screen recorder. Instated of that capture media & later process.

@jibon57
I'm also interested in this. Could you pls share some code or ideas how it could be implemented using server api?

@davidzhao
Copy link
Member

Here's a snippet involving GStreamer used by Tandem. It basically uses an appsrc and then feed in the RTP packets received on TrackRemote.

If you ended up building this, it'd be amazing if you are open to open sourcing it. We would love to have a library that would do this more easily (i.e. pion WebRTC track to file).

@jibon57
Copy link

jibon57 commented Oct 18, 2021

@mikhailbolshakov I haven't implemented it yet. Just wanted to know the possible ways. Thanks @davidzhao

@davidzhao
Copy link
Member

@jibon57 this is incredible work! I didn't expect the JS sdk to mostly work on Node. I wonder if we can make this a bit easier in the SDK itself so folks don't have to modify it.

@jibon57
Copy link

jibon57 commented Oct 26, 2021

@davidzhao thank you! I've deleted my last comment because my solution isn't working as expected because of wrtc library limitation for RTCVideoSink & RTCAudioSink. I ain't expert about webrtc. I'll recommend you to have a look bbb-sfurec-adapter library which BigBlueButton is using to record RTP stream using FFMPEG here: https://github.com/bigbluebutton/bbb-webrtc-sfu/blob/949f3c08d07abdca92dc4bddbc8b84fc14f52cf5/lib/mcs-core/lib/adapters/mediasoup/recorder-element.js#L242
I think it will be easier for you to figure it out & implement it in livekit.

@jibon57
Copy link

jibon57 commented Oct 31, 2021

@davidzhao I've tried another approach using go sdk (I never used go before :-D) following example: https://github.com/pion/webrtc/blob/master/examples/save-to-disk/main.go

package main

import (
	"fmt"
	"strings"
	"time"

	lksdk "github.com/livekit/server-sdk-go"
	"github.com/pion/webrtc/v3"
	"github.com/pion/webrtc/v3/pkg/media"
	"github.com/pion/webrtc/v3/pkg/media/ivfwriter"
	"github.com/pion/webrtc/v3/pkg/media/oggwriter"
)

func saveToDisk(i media.Writer, track *webrtc.TrackRemote) {
	defer func() {
		if err := i.Close(); err != nil {
			panic(err)
		}
	}()

	for {
		rtpPacket, _, err := track.ReadRTP()
		if err != nil {
			panic(err)
		}
		if err := i.WriteRTP(rtpPacket); err != nil {
			panic(err)
		}
	}
}

func main() {
	host := "host"
	apiKey := "api-key"
	apiSecret := "api-secret"
	roomName := "myroom"
	identity := "botuser"
	room, err := lksdk.ConnectToRoom(host, lksdk.ConnectInfo{
		APIKey:              apiKey,
		APISecret:           apiSecret,
		RoomName:            roomName,
		ParticipantIdentity: identity,
	})
	if err != nil {
		panic(err)
	}

	room.Callback.OnTrackSubscribed = trackSubscribed

	time.Sleep(time.Second * 30) // testing
	room.Disconnect()
}

func trackSubscribed(track *webrtc.TrackRemote, publication lksdk.TrackPublication, rp *lksdk.RemoteParticipant) {

	codec := track.Codec()
	if strings.EqualFold(codec.MimeType, webrtc.MimeTypeOpus) {

		fileName := track.ID() + "_audio.ogg"
		fmt.Printf("Got Opus track, saving to disk as %s (48 kHz, 2 channels)", fileName)

		oggFile, err := oggwriter.New(fileName, 48000, 2)
		if err != nil {
			panic(err)
		}
		saveToDisk(oggFile, track)

	} else if strings.EqualFold(codec.MimeType, webrtc.MimeTypeVP8) {

		fileName := track.ID() + "_video.ivf"
		fmt.Printf("Got VP8 track, saving to disk as %s", fileName)

		ivfFile, err := ivfwriter.New(fileName)
		if err != nil {
			panic(err)
		}

		saveToDisk(ivfFile, track)
	}
}

It's working fine but video isn't good enough. Will be helpful if you have a look & give suggestions. I think need to implement this part: https://github.com/pion/webrtc/blob/979aefd702bfe50552cd830a084f9bc339e7d7df/examples/save-to-disk/main.go#L108 but I don't have idea how to do :-(

@Wallacy
Copy link

Wallacy commented Nov 4, 2021

FWIW: I used the gstcefsrc ( https://github.com/centricular/gstcefsrc ) point my own custom route instead of livekit recorder to gain some memory/cpu.

@lxShaDoWxl
Copy link

@Wallacy Hi! you run by cli or usage golang wrapper gst?
it would be interesting to see an example using golang and gstcefsrc

@thusinh1969
Copy link

I can not get it working at all

  1. Even with the basic example on guide, the video is not playable after recording
  2. With standalone mode docker pose error on log and another is recorded although there are 2 persons chatting video in the same room using livekit-sdk-js example.

I filed an issue here: livekit/livekit-recorder#34

Steve

@Wallacy
Copy link

Wallacy commented Dec 6, 2021

@Wallacy Hi! you run by cli or usage golang wrapper gst? it would be interesting to see an example using golang and gstcefsrc

I use the CLI; O build a docker image with everything that i need and just run.

@theleapofcode
Copy link

@davidzhao I've tried another approach using go sdk (I never used go before :-D) following example: https://github.com/pion/webrtc/blob/master/examples/save-to-disk/main.go

package main

import (
	"fmt"
	"strings"
	"time"

	lksdk "github.com/livekit/server-sdk-go"
	"github.com/pion/webrtc/v3"
	"github.com/pion/webrtc/v3/pkg/media"
	"github.com/pion/webrtc/v3/pkg/media/ivfwriter"
	"github.com/pion/webrtc/v3/pkg/media/oggwriter"
)

func saveToDisk(i media.Writer, track *webrtc.TrackRemote) {
	defer func() {
		if err := i.Close(); err != nil {
			panic(err)
		}
	}()

	for {
		rtpPacket, _, err := track.ReadRTP()
		if err != nil {
			panic(err)
		}
		if err := i.WriteRTP(rtpPacket); err != nil {
			panic(err)
		}
	}
}

func main() {
	host := "host"
	apiKey := "api-key"
	apiSecret := "api-secret"
	roomName := "myroom"
	identity := "botuser"
	room, err := lksdk.ConnectToRoom(host, lksdk.ConnectInfo{
		APIKey:              apiKey,
		APISecret:           apiSecret,
		RoomName:            roomName,
		ParticipantIdentity: identity,
	})
	if err != nil {
		panic(err)
	}

	room.Callback.OnTrackSubscribed = trackSubscribed

	time.Sleep(time.Second * 30) // testing
	room.Disconnect()
}

func trackSubscribed(track *webrtc.TrackRemote, publication lksdk.TrackPublication, rp *lksdk.RemoteParticipant) {

	codec := track.Codec()
	if strings.EqualFold(codec.MimeType, webrtc.MimeTypeOpus) {

		fileName := track.ID() + "_audio.ogg"
		fmt.Printf("Got Opus track, saving to disk as %s (48 kHz, 2 channels)", fileName)

		oggFile, err := oggwriter.New(fileName, 48000, 2)
		if err != nil {
			panic(err)
		}
		saveToDisk(oggFile, track)

	} else if strings.EqualFold(codec.MimeType, webrtc.MimeTypeVP8) {

		fileName := track.ID() + "_video.ivf"
		fmt.Printf("Got VP8 track, saving to disk as %s", fileName)

		ivfFile, err := ivfwriter.New(fileName)
		if err != nil {
			panic(err)
		}

		saveToDisk(ivfFile, track)
	}
}

It's working fine but video isn't good enough. Will be helpful if you have a look & give suggestions. I think need to implement this part: https://github.com/pion/webrtc/blob/979aefd702bfe50552cd830a084f9bc339e7d7df/examples/save-to-disk/main.go#L108 but I don't have idea how to do :-(

@jibon57 did you get this working properly?

@davidzhao
Copy link
Member

davidzhao commented May 26, 2022

We've just released Egress service! Check out our announcement and docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

10 participants