diago icon indicating copy to clipboard operation
diago copied to clipboard

Empty WAV file is created when recording a call

Open Malfarion opened this issue 6 months ago • 12 comments

When using the Record() function (based on the diago example), an empty WAV file is created at the specified path, but no audio data is written to it.

Code Snippet: The issue occurs after successfully creating a dialog session:

dialog, err := tu.Invite(ctx, client, diago.InviteOptions{
    Username: regOpts.Username, 
    Password: regOpts.Password
})

Recording function implementation:

func Record(outDialog *diago.DialogClientSession) error {

	// Create wav file to store recording
	wawFile, err := os.OpenFile("D:\\data\\"+outDialog.InviteRequest.CallID().Value()+".wav", os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		return err
	}
	defer wawFile.Close()

	// Create recording audio pipeline
	rec, err := outDialog.AudioStereoRecordingCreate(wawFile)
	if err != nil {
		return err
	}

	// Must be closed for correct flushing
	defer func() {
		if err := rec.Close(); err != nil {
			slog.Error("Failed to close recording", "error", err)
		}
	}()

	// Do echo with audio reader and writer from recording object
	_, err = media.Copy(rec.AudioReader(), rec.AudioWriter())

	if errors.Is(err, io.EOF) {
		// Call finished
		return nil
	}
	return err
}

Actual Behavior: A WAV file is created at the specified path, but it remains empty just with format header like RIFF$ WAVEfmt @ } data .

Additional Context:

Followed the example from: https://github.com/emiago/diago/blob/main/examples/wav_record/main.go

No errors are returned during the recording process

The file permissions seem correct (can create/write to the location)

The call itself establishes successfully, just no audio is recorded

Malfarion avatar Jun 19 '25 19:06 Malfarion

Hi @Malfarion . Thanks for opening. Must be some small issue if above is true. I will recheck example, although this is integrated to gophone https://github.com/emiago/gophone

emiago avatar Jun 22 '25 19:06 emiago

Seems all fine. I have now added to dump file location where this is stored. please recheck and reopen if needed.

emiago avatar Jun 22 '25 19:06 emiago

Hi @emiago. I'm still having an issue with client-side recording.
For context:

  • I'm testing from a Windows environment
  • Not using server-side recording
  • Can provide full logs/repro steps if needed

Could you also please change both instances in https://github.com/emiago/diago/blob/03ba39b9b8f8189f7ef13f1ed4b1368935a837ee/audio/monitor_pcm.go#L160 from the hardcoded /tmp/ to os.TempDir()?

PS: I already did it locally, so it's not the reason for the empty WAV.

Malfarion avatar Jun 22 '25 20:06 Malfarion

@Malfarion do you still have the issue? i got the same one.

tarcanscloud avatar Jul 15 '25 09:07 tarcanscloud

@emiago @tarcanscloud Yes, and unfortunately i have no option to reopen issue, button is grey.

Malfarion avatar Jul 15 '25 09:07 Malfarion

hi @Malfarion . Yes that should be fixed. I will add commit for that. Do you think that could be issue? It is interesting that this fails on windows.

emiago avatar Jul 15 '25 21:07 emiago

I used the example code from https://github.com/emiago/diago/blob/main/examples/wav_record/main.go and added a registration step. It answers the call fine, but the recording file ends up empty (actually 44 bytes, but it won’t play).

This is recorded file content. Image

The code:

package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log/slog"
	"os"
	"os/signal"

	"github.com/emiago/diago"
	"github.com/emiago/diago/examples"
	"github.com/emiago/diago/media"
	"github.com/emiago/sipgo"
	"github.com/emiago/sipgo/sip"
)

const (
	user     = "TODO"
	password = "TODO"
	host     = "TODO"
	port     = 5060
)

// Dial this app with
// gophone dial -media=audio "sip:[email protected]"

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	examples.SetupLogger()

	err := start(ctx)
	if err != nil {
		slog.Error("PBX finished with error", "error", err)
	}
}

func start(ctx context.Context) error {
	// Setup our main transaction user
	ua, _ := sipgo.NewUA()
	tu := diago.NewDiago(ua)

	go func() {
		uri := sip.Uri{}

		sip.ParseUri(
			fmt.Sprintf(
				"sip:%s@%s:%d",
				user,
				host,
				port,
			),
			&uri,
		)

		slog.Info("registering", slog.String("uri", uri.String()))

		err := tu.Register(
			ctx,
			uri,
			diago.RegisterOptions{
				Username: user,
				Password: password,
			},
		)

		if err != nil {
			panic(err)
		}
	}()

	return tu.Serve(ctx, func(inDialog *diago.DialogServerSession) {
		slog.Info("New dialog request", "id", inDialog.ID)
		defer slog.Info("Dialog finished", "id", inDialog.ID)
		if err := Record(inDialog); err != nil {
			slog.Error("Record finished with error", "error", err)
		}
	})
}

func Record(inDialog *diago.DialogServerSession) error {
	inDialog.Trying()  // Progress -> 100 Trying
	inDialog.Ringing() // Ringing -> 180 Response
	if err := inDialog.Answer(); err != nil {
		return err
	} // Answer -> 200 Response

	// Create wav file to store recording
	filename := "/tmp/diago_record_" + inDialog.InviteRequest.CallID().Value() + ".wav"
	slog.Info("Creating new recording", "filename", filename)
	wawFile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		return err
	}
	defer wawFile.Close()

	// Create recording audio pipeline
	rec, err := inDialog.AudioStereoRecordingCreate(wawFile)
	if err != nil {
		return err
	}
	// Must be closed for correct flushing
	defer func() {
		if err := rec.Close(); err != nil {
			slog.Error("Failed to close recording", "error", err)
		}
	}()

	// Do echo with audio reader and writer from recording object
	_, err = media.Copy(rec.AudioReader(), rec.AudioWriter())
	if errors.Is(err, io.EOF) {
		// Call finished
		return nil
	}
	return err
}

tarcanscloud avatar Jul 16 '25 01:07 tarcanscloud

is it the same issue as your? @Malfarion

tarcanscloud avatar Jul 16 '25 01:07 tarcanscloud

Thanks @emiago for reopen the issue 🥹

tarcanscloud avatar Jul 16 '25 01:07 tarcanscloud

FYI. Recording is only full if streaming is working. Can you actually create PCAP of your calls?

emiago avatar Jul 16 '25 20:07 emiago

@emiago I think that's the point. I can't see any incoming RTP packet. Can you? Not sure if it only happened on my machine.

tarcanscloud avatar Jul 30 '25 04:07 tarcanscloud

@tarcanscloud I think than this is different issue. Sorry but I have no idea what are you doing. Recording I have no issues, but I could see some Windoes problems which should be resolved on main branch.

emiago avatar Jul 30 '25 22:07 emiago