ogg icon indicating copy to clipboard operation
ogg copied to clipboard

Can't decode/encode without screwing the stream

Open athoune opened this issue 4 years ago • 2 comments

package main

import (
	"fmt"
	"os"

	"github.com/mccoyst/ogg"
)

func main() {
	f, err := os.Open(os.Args[1])
	if err != nil {
		panic(err)
	}
	decoder := ogg.NewDecoder(f)
	var encoder *ogg.Encoder
	for {
		page, err := decoder.Decode()
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			break
		}
		if encoder == nil {
			encoder = ogg.NewEncoder(page.Serial, os.Stdout)
			encoder.EncodeBOS(page.Granule, page.Packet)
		} else {
			encoder.Encode(page.Granule, page.Packet)
		}

	}
}

./copy a.ogg > b.ogg

This code decode a stream, encodes it to stdout.

ogginfo works well with my original stream, but doesn't handle the output stream.

athoune avatar Apr 14 '20 17:04 athoune

I tried something similar on my end and the only issue I found was with EOS. An ffmpeg-recorded .ogg file contains an EOS packet that also has data. This is lost due to the current API being exposed.

Using something like

func (w *Encoder) EncodeEOSPacket(granule int64, packet []byte) error {
	return w.writePacket(EOS, granule, packet)
}

fixes the problem on my end

edit: NVM. This fixes the last EOS, but it looks like there are still errors somewhere in the packet writing logic.

gurupras avatar May 04 '22 04:05 gurupras

As of today, I can confirm this issue still exists as I am trying something similar on my end (a copy logic), using the code listed below given filePath, dupFilePath pre-defined. After checking the binary generated, I am sure that there is a difference in many packets even though they have the same stats (page.Type, page.Granule, page.Serial, len(page.Packet)). And if using ffprobe, the generated file will show [opus @ 0x558cf915d580] Error parsing the packet header. I have no idea why this copy logic won't work.

	f, _:= os.Open(filePath)
	defer f.Close()
	w, _ := os.Create(dupFilePath)
	defer w.Close()
	decoder := ogg.NewDecoder(f)
	encoder := ogg.NewEncoder(482915044, w)
	for {
		page, err := decoder.Decode()
		fmt.Printf(
			"page: type: %d, granule: %d, serial: %d, length: %d bytes\n",
			page.Type, page.Granule, page.Serial, len(page.Packet))
		if err != nil {
			break
		}
		if page.Type == ogg.BOS {
			encoder.EncodeBOS(page.Granule, page.Packet)
		} else if page.Type == ogg.EOS {
			if len(page.Packet) != 0 {
				encoder.Encode(page.Granule, page.Packet)
			}
			encoder.EncodeEOS()
		} else {
			encoder.Encode(page.Granule, page.Packet)
		}
	}

XyLearningProgramming avatar May 30 '22 04:05 XyLearningProgramming

Long story short: I figured out the root cause, and it was me misunderstanding the lacing values part of the spec. I should have a fix ready this week.

mccoyst avatar Oct 27 '22 03:10 mccoyst

I believe I've solved the many problems. The downside is that I had to change the API so that the library can encode/decode pages that contain multiple packets. See otest.go for an example — it's pretty much like the test programs posted to this issue, but uses the new methods.

Let me know if you find any files in the wild that break the new API. Thanks!

mccoyst avatar Nov 03 '22 02:11 mccoyst

Keeping in mind that I've tested entirely via prog rock oggs from wikipedia, I haven't found a file that breaks yet, so I'm closing this. If anybody does ever find a file (vorbis or otherwise) that this can't handle, feel free to make a new ticket for the specific file (if possible).

mccoyst avatar Nov 07 '22 23:11 mccoyst