I can write some code like this using liboggz:
OGGZ *oggz = oggz_open(filename, OGGZ_READ | OGGZ_AUTO);
oggz_set_read_callback(oggz, -1, read_callback, 0);
while ((long n = oggz_read(oggz, 32)) > 0);
oggz_close(oggz);
And my read_callback will, conceptually, receive a stream of OGG packets. These packets have a granule position. I'm not talking about liboggz's calculated granule position, which if I understand correctly calls into the underlying codec to work out the semantics of the granule position. I'm talking about the "raw" granule position of the packet that you access at packet->op.granulepos. How is this value calculated?
If I read about the OGG format on Wikipedia, as far as the file format is concerned the only thing that has a granule position if an OGG page, not a packet. Packets can straddle multiple pages and many packets can be on a single page. How does liboggz (which I presume calls libogg in the background) assign granule positions to individual packets?