I am not familiar at all with any of the internals/packets/pages/whatevers of live streams. I can get my plugin to connect to a server, get the data it's outchucking, parse out the HTTP header, parse the seperate Ogg Pages, get the OpusHead, and get the OpusTags, but that's it. I tried sending, just the Ogg pages, a chain of Ogg pages, individual segments in the pages, to all of the in-memory functions of the libraries but they all either return error code -133 (OP_EBADHEADER), -137 (OP_EBADLINK), or -139 (OP_EBADTIMESTAMP).
Live streams are exactly like files. You shouldn't need to do any parsing, you should be able to pass the data to opusfile using the callback apis.
Opusfile has good http support internally, but unfortunately its POSIX-api only at this point so it doesn't include that functionality on Windows. I think it would be great if someone wanted to work on that.
Can somebody tell me how to decode an Ogg Opus stream with those libraries or if it's not possible to?
Can you post your code? It's easier to point out problems in existing code then to walk over the whole process and hopefully clarify something for you by accident.
#include "pshpack1.h"
struct OggHead
{
char MagicNumber[4];
BYTE Version;
BYTE HeaderType;//bit 0=Segment Cont from prev page; bit 1=Beginning of stream; bit 2=End of stream
unsigned __int64 GranulePosition;
DWORD BitStreamSerialNumber;
DWORD PageSequenceNumber;
DWORD CheckSum;
BYTE PageSegments;
};
struct SuperOggHead
{
OggHead oh;
BYTE SegmentTable[255];
};
#include "poppack.h"
//---------------------------------------------------------------------------
void __fastcall TPlaybackThread::ProcPlayURL(Messages::TMessage & Msg)
{
// Plugin specific code snipped...
AnsiString Get="GET /"+URL+" HTTP/1.0\r\n";
Get+="Host: "+Host+":"+Port+"\r\n";
Get+="Server: "+Host+":"+Port+"\r\n";
Get+="User-Agent: Winamp (Experimental BogProg Opus Support)\r\n";
Get+="\r\n";
ProcessMessages();
AnsiString Res;
TWinSocketStream *pStream=NULL;
char *buff=NULL;
try
{
cs->Open();
if(cs->Active)
{
buff=new char[65535];
int sr;
pStream = new TWinSocketStream(cs->Socket,Timeout);
pStream->WaitForData(0);
pStream->TimeOut=Timeout;
pStream->Write(Get.c_str(),Get.Length());
int tto=0;
bool ready=false;
while(tto<Timeout && !ready && !WantStop)
{
if(!pStream->WaitForData(100))
{
tto+=100;
}
else{ready=true;break;}
}
while(ready && !WantStop)
{
sr=pStream->Read(buff,65535);
if(sr<=0)break;
if(!URLData(buff,sr)){break;}
if(sr<0)break;
ready=false;tto=0;
while(tto<Timeout && !ready && !WantStop)
{
if(!pStream->WaitForData(100))
{
tto+=100;
}
else{ready=true;break;}
}
}
if(!ready)ErrorStop();
delete pStream;
cs->Close();
delete [] buff;
}
}catch(...)
{
try{if(pStream)delete pStream;}catch(...){}
try{cs->Close();}catch(...){}
try{if(buff)delete [] buff;}catch(...){}
//try{}catch(...){}
//try{}catch(...){}
ErrorStop();
}
delete cs;
URLDataStage=0;
// Plugin specific code snipped...
}
//---------------------------------------------------------------------------
bool __fastcall TPlaybackThread::URLData(char * buff, int len)
{
switch(URLDataStage)
{
case 0://Get the stream type, info and data start
URLDataStreamHead(buff,len);
break;
case 1:
URLDataStreamData(buff,len);
break;
}
return !WantStop;
}
//---------------------------------------------------------------------------
void __fastcall TPlaybackThread::URLDataStreamHead(char * buff, int len)
{
char *la=buff;
int lal=len;
if(URLPrev)
{
char *ack=new char[URLPrevLen+len];
memcpy(ack,URLPrev,URLPrevLen);
delete [] URLPrev;
URLPrev=ack;
ack=URLPrev+URLPrevLen;
URLPrevLen=URLPrevLen+len;
memcpy(ack,buff,len);
la=URLPrev;
lal=URLPrevLen;
}
int ds=-1;
for(int i=0;i<lal-3;i++)
{
if(la[i]=='\r')
{
if(la[i+1]=='\n')
{
if(la[i+2]=='\r')
{
if(la[i+3]=='\n')
{
//Found the http head end
ds=i+4;
URLHead.SetLength(ds);
memcpy(URLHead.c_str(),la,ds);
URLDataStage=1;
if(la==URLPrev)
{
int nl=URLPrevLen-ds;
if(nl>0)
{
char *ack=new char[nl];
memcpy(ack,URLPrev+ds,nl);
delete [] URLPrev;
URLPrev=ack;
URLPrevLen=nl;
}
else
{
delete [] URLPrev;URLPrev=0;
URLPrevLen=0;
}
}
else
{
int nl=lal-ds;
if(nl>0)
{
char *ack=new char[nl];
memcpy(ack,la+ds,nl);
delete [] URLPrev;
URLPrev=ack;
URLPrevLen=nl;
}
else
{
if(URLPrev)delete [] URLPrev;
URLPrev=NULL;
URLPrevLen=0;
}
}
return;
}
}
}
}
}
if(URLPrev==NULL)
{
URLPrev=new char[lal];
URLPrevLen=lal;
memcpy(URLPrev,buff,lal);
}
}
//---------------------------------------------------------------------------
void __fastcall TPlaybackThread::URLDataStreamData(char * buff, int len)
{
char *la=buff;
int lal=len;
if(URLPrev)
{
char *ack=new char[URLPrevLen+len];
memcpy(ack,URLPrev,URLPrevLen);
delete [] URLPrev;
URLPrev=ack;
ack=URLPrev+URLPrevLen;
URLPrevLen=URLPrevLen+len;
memcpy(ack,buff,len);
la=URLPrev;
lal=URLPrevLen;
}
{
OggHead *oh=(OggHead *)la;
if(lal>=sizeof(OggHead))
{
if(
oh->MagicNumber[0]!='O' ||
oh->MagicNumber[1]!='g' ||
oh->MagicNumber[2]!='g' ||
oh->MagicNumber[3]!='S' ||
oh->Version!=0
)
{
ErrorStop();
return;
}
if(lal>sizeof(OggHead))
{
if(URLPrev==NULL)
{
AddURLPrevBytes(la,lal);
la=URLPrev;
}
SuperOggHead *soh=(SuperOggHead *)la;
int nps=soh->oh.PageSegments;
int pl=sizeof(OggHead)+nps;
if(lal<=pl)
{
return;
}
for(int i=0;i<nps;i++)
{
pl+=soh->SegmentTable[i];
}
if(lal<=pl)
{
return;
}
DecodeOggPage(la,pl);
DelURLPrevBytes(pl);
la=URLPrev;
lal=URLPrevLen;
}
}
}
if(URLPrev==NULL)
{
URLPrev=new char[lal];
URLPrevLen=lal;
memcpy(URLPrev,buff,lal);
}
}
//---------------------------------------------------------------------------
void __fastcall TPlaybackThread::DecodeOggPage(char * buff, int len)
{
struct SomethingToDoWithOpus
{
char h[8];
};
OpusHead oh;memset(&oh,0,sizeof(oh));
SuperOggHead *soh=(SuperOggHead *)buff;
int nps=soh->oh.PageSegments;
int ss=sizeof(OggHead)+nps;
for(int i=0;((i<nps) && (!WantStop));i++)
{
char *seg=buff+ss;
{
unsigned char toc[255];memset(toc,0,255);
const unsigned char frames[48]={0};memset((void *)&frames,0,48);
short size[48];memset(size,0,48*sizeof(short));
int payloadoffset=0;
//The object OpusLib is dynamically loaded with the functions exported from libopus*.dll
int nof=OpusLib.opus_packet_parse(seg,soh->SegmentTable[i],toc,(const unsigned char * *)&frames,size,&payloadoffset);
DecodeOggSegment(seg,soh->SegmentTable[i]);
}
int res;
if(soh->SegmentTable[i]>8)
{
SomethingToDoWithOpus *stdwo=(SomethingToDoWithOpus *)seg;
if(
stdwo->h[0]=='O' &&
stdwo->h[1]=='p' &&
stdwo->h[2]=='u' &&
stdwo->h[3]=='s'
)
{
if(
stdwo->h[4]=='H' &&
stdwo->h[5]=='e' &&
stdwo->h[6]=='a' &&
stdwo->h[7]=='d'
)
{
if(URLOpusTags)
{
//The object Opus is a struct that is dynamically loaded with the functions exported by libopusfile*.dll
Opus.opus_tags_clear(URLOpusTags);
delete URLOpusTags;URLOpusTags=NULL;
}
if(URLOpusHead)
{
delete URLOpusHead;URLOpusHead=NULL;
}
res=Opus.opus_head_parse(&oh,seg,soh->SegmentTable[i]);
if(res==0)
{
URLOpusHead=new OpusHead;
memcpy(URLOpusHead,&oh,sizeof(oh));
if(CurPPL)
{
CurPPL->Out->Close();
CurPPL->Out->Open(48000,URLOpusHead->channel_count,16,0,0);
}
int error=0;
URLOpusDecoder=OpusLib.opus_decoder_create(48000,URLOpusHead->channel_count,&error);
if(error)
{
ErrorStop();
return;
}
}
}
else
if(
stdwo->h[4]=='T' &&
stdwo->h[5]=='a' &&
stdwo->h[6]=='g' &&
stdwo->h[7]=='s'
)
{
if(URLOpusTags)
{
Opus.opus_tags_clear(URLOpusTags);
delete URLOpusTags;URLOpusTags=NULL;
}
URLOpusTags=new OpusTags;memset(URLOpusTags,0,sizeof(OpusTags));
Opus.opus_tags_init(URLOpusTags);
res=Opus.opus_tags_parse(URLOpusTags,seg,soh->SegmentTable[i]);
if(res)
{
Opus.opus_tags_clear(URLOpusTags);
delete URLOpusTags;URLOpusTags=NULL;
}
}
}
}
ss+=soh->SegmentTable[i];
}
}
//---------------------------------------------------------------------------
void __fastcall TPlaybackThread::DecodeOggSegment(char * buff, int len)
{
if(URLOpusDecoder)
{
//need 240ms worth of samples at 48khz per channel (x2 for DSPs)
int numofframes=11520;//48000 * 240/1000
int numofsamples=numofframes*URLOpusHead->channel_count;
int len=numofsamples*sizeof(opus_int16);
len*=2;//Extra padding for DSPs
short *nbuffers=(short *)new unsigned char[len];
len/=2;
int sr=OpusLib.opus_decode(URLOpusDecoder,buff,len,nbuffers,numofframes,0);
//At this point, opus_decode is only returning -4 for everything.
if(sr>0)
{
if(CurPPL)
{
CurPPL->Out->Write((char *)nbuffers,sr*sizeof(opus_int16));
}
}
else if(sr==0)
{
//Do nothing
}
else
{
ErrorStop();
}
delete [] nbuffers;
}
}
//---------------------------------------------------------------------------