-
Notifications
You must be signed in to change notification settings - Fork 58
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
Forward error correction #18
Conversation
Thanks Elinor, this looks very promising. I'll review it during the week. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. Some minor changes and consistency issues here and there, but nothing big. My only real concern is documentation: how do we explain to users how to use this? Are supposed to detect packet loss themselves, how are they to know the length of the lost packets, etc. It doesn't have to be a novel; a quick line or two in the readme + on the godoc string can help someone to figure out the rest for themselves.
Thanks for the great work.
encoder.go
Outdated
|
||
// SetInBandFEC configures the encoder's use of inband forward error | ||
// correction (FEC) | ||
func (enc *Encoder) SetInBandFEC(fec int) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this argument could be a boolean, as with SetDTX
encoder.go
Outdated
} | ||
|
||
// InBandFEC gets the encoder's configured inband forward error correction (FEC) | ||
func (enc *Encoder) InBandFEC() (int, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return type boolean
opus_test.go
Outdated
// This is hard to check programatically, but I plotted the graphs in a | ||
// spreadsheet and it looked great. The encoded waves both start out with a | ||
// string of zero samples before they pick up, and the G4 is phase shifted | ||
// by half a period, but that's all fine for lossy audio encoding. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment relates to a stereo signal and isn't accurate here. you can remove it and just say "Commented out for the same reason as in TestStereo", for example.
opus_test.go
Outdated
// This is hard to check programatically, but I plotted the graphs in a | ||
// spreadsheet and it looked great. The encoded waves both start out with a | ||
// string of zero samples before they pick up, and the G4 is phase shifted | ||
// by half a period, but that's all fine for lossy audio encoding. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same on this comment being out of sync with the content.
t.Errorf("Unexpected encoder loss percentage value. Got %d, but expected %d", | ||
lp, lossPerc) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this only test successes, not failures. can you add a test for -1 and 101, ensuring failure?
decoder_test.go
Outdated
t.Fatalf("Couldn't decode data: %v", err) | ||
} | ||
samples, err := dec.LastPacketDuration() | ||
if err!=nil{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure to run gofmt on all your changes. most editors / ides provide hooks to run it automatically on save, e.g. vim-go does it by default, emacs go-mode has a hook you can enable in .emacs. more info: https://blog.golang.org/go-fmt-your-code . I've added a new test to travis which automatically detects this: if you rebase your changes onto v2 it will show you if it worked:
git remote add origin upstream https://github.com/hraban/opus
git fetch upstream
git rebase upstream/v2
git push --force-with-lease
your PR will update to show the travis build status
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about that, for whatever reason Goland is always removing the file watchers. I keep having to set "use gofmt on save"
decoder.go
Outdated
@@ -105,3 +111,64 @@ func (dec *Decoder) DecodeFloat32(data []byte, pcm []float32) (int, error) { | |||
} | |||
return n, nil | |||
} | |||
|
|||
// Decode encoded Opus data into the supplied buffer with forward error | |||
// correction. The supplied buffer will be entirely filled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first word of a godoc comment must be the name of the method. It's an odd convention, but a convention nonetheless. Weʼll have to stick to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sorry, that show that copy/paste is not always your friend...
decoder.go
Outdated
} | ||
|
||
// Decode encoded Opus data into the supplied buffer with forward error | ||
// correction. The supplied buffer will be entirely filled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first word must be method name
(*C.uchar)(&data[0]), | ||
C.opus_int32(len(data)), | ||
(*C.opus_int16)(&pcm[0]), | ||
C.int(cap(pcm)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This brought to my attention a bug in all the Decode* functions :/ I don't remember why I originally used cap
, but I think it's a bug. It's asymmetrical with io.Reader, whose signature I tried to emulate. It was probably originally chosen for alignment with the Encode* functions and the suggestion to resize them (data = data[:n]), but it doesn't make sense for Decode.
Anyway, this is all outside the scope of your PR, so it's all good here.
opus_test.go
Outdated
} | ||
pcmBuffer = pcmBuffer[:n] | ||
if len(pcmBuffer) != n { | ||
t.Fatalf("Length mismatch: %d samples in, %d out", len(pcmBuffer), n) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this error, by the way. the length of the pcmBuffer does not seem to be the amount of samples fed to the encoder in this pass? especially after truncating to :n. What exactly is being tested, here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea of the test is to lose two packets (I guess we can do it random but to be honest it was just easier like this). All of the packets have to be decoded without fec, that's where this line comes from.
In addition to that the packets coming after the "lost ones" have to be decoded with fec on (there you can see that I don't check for the length but just the error).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree the overall test logic itself is ok, but this specific guard is a tautology. after a = a[:n]
, you will always len(a) ≤ n
, and because you start out with len(a) = FRAME_SIZE ⋙ n
this is a tautology. You might be trying to test e.g. if n ≠ PACKET_SIZE
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I just copy/pasted it from the TestCodec test, but I don't mind changing it to n!=PACKET_SIZE
or actually n!=FRAME_SIZE
since I changed it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine :) don't forget to change the error message. Currently you're testing for PACKET_SIZE and n, but reporting len(pcmBuffer) and n.
opus_test.go
Outdated
const SAMPLE_RATE = 48000 | ||
const FRAME_SIZE_MS = 60 | ||
const FRAME_SIZE = SAMPLE_RATE * FRAME_SIZE_MS / 1000 | ||
const PACKET_SIZE = 480 // 10ms packet |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is in fact the frame, not the 60ms defined further up. Your entire audio stream is 60ms, the frame size is 10ms. Packets are more of a "transport level" term, when you start talking about networks. If you send every frame as a separate network message (which will typically be the case), then frame ~= packet. Either way, you'll want to use 10 as your FRAME_SIZE_MS, and use a different name for the 60 constant you defined above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation, I wasn't sure about that.
} | ||
|
||
pcmBuffer := make([]int16, samples) | ||
if err = dec.DecodeFEC(encodedData[i], pcmBuffer); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is cheating :) you're passing the "dropped" packet to decodefec. Should be encodedData[i-1]. This will get you a different output:
EDIT: Scratch that—I was wrong! "Forward" error correction doesn't mean the error correcting data is encoded forward, but backward! Which makes total sense, because you can't predict the data to come. So your test was correct.
Checked again the result graph
I still need to add some explanations about FEC in the README. |
Issue: #17
For the FEC test, I manually "dropped" packets and used the csv with chart method using the code in the opus_test.go for visualizing the results. It looks good I think.
I attached the graphs pictures.