[opus] Why does this code not generate a valid opus file?

Timothy B. Terriberry tterriberry at mozilla.com
Sat Oct 17 15:14:33 PDT 2015


Comments inline.

Daniel Armyr <daniel at armyr.se> wrote:
> Hi.
> I assume that I am deeply stupid and have missed something obvious,   
> but why does this bare-bones code not generate a valid (If trivial)   
> opus file?
>
> Running opusinfo I ge the following which I interpret this to mean   
> that not even the first packet gets a pass:
> New logical stream (#1, serial: 1f0cce42): type opus
> WARNING: Could not decode Opus header packet 0 - invalid Opus stream (1)
> WARNING: Could not decode Opus header packet 0 - invalid Opus stream (1)
> Opus stream 1:
> 	WARNING: stream 1 is empty
> Logical stream 1 ended
>
> Here is my code:
>
> #include <stdio.h>
> #include <stdlib.h>
> #include "ogg/ogg.h"
>
> //Some structs to help build the headers.
> struct OggHeader {
>     unsigned char magicSignature[8];
>     unsigned char version;
>     unsigned char channelCount;
>     unsigned short preSkip;
>     unsigned int sampleRate;
>     unsigned short outputGain;
>     unsigned char mappingFamily;
> };
>
> struct OggCommentHeader {
>     unsigned char magicSignature[8];
>     unsigned int vendorStringLength; //Must be set to 0
>     //In-between here we would place out vendor string, if we had one.
>     unsigned int userCommentListLength; //Must be set to 0
>     //And after here we would place our comments, if we had any.
> };
>
> int main(){
>     ogg_stream_state os;
>     ogg_page         og;
>
>     FILE* fout = fopen("/tmp/trivial.opus", "wb");
>     if ( fout == 0 ) {
>         printf( "Error opening output file.\n" );
>         return -1;
>     }
>
>     //**************
>     //Initialize the stream
>     srand(0);
>     ogg_stream_init(&os,rand());
>
>     //**************
>     //Create the header
>     //*****************
>     struct OggHeader headerContent = {
>         {'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'},
>         1, //Version
>         2, //Channels
>         0, //pre-skip

This will generate warnings, as no reasonable Opus encoder would set  
this smaller than 120 (and then only if it were a CELT-only stream).

>         44100, //Original samplerate
>         0, //output gain
>         0}; //Mapping family
>
>     ogg_packet header;
>     header.packet = (unsigned char*)&headerContent;
>     header.bytes = sizeof(struct OggHeader);

Are you sure this is returning the correct number, and not adding  
padding for alignment purposes? opusinfo will reject headers with more  
than 19 bytes for channel mapping family 0 and minor versions 0 or 1.

Then, because it failed to parse the initial header, it will attempt  
to parse the comment header as if it were the initial header, and that  
will fail again.

>     header.b_o_s = 1; //Header must set beginning_of_stream
>     header.e_o_s = 0;
>     header.granulepos = -1; //This packet does not contain audio   
> data, so granule-position = -1

https://tools.ietf.org/html/draft-ietf-codec-oggopus-08#section-3
"There are two mandatory header packets.  The granule position of the  
pages on which these packets complete MUST be zero."

>     header.packetno = 0;
>
>     ogg_stream_packetin(&os,&header);
>
>     //**************
>     //Create the comments header
>     struct OggCommentHeader commentHeaderContent = {
>         {'O', 'p', 'u', 's', 'T', 'a', 'g', 's'},
>         0, //We do not support a vandor string in this implementation.
>         0 //We do not support any user comments in this implementation.
>     };
>
>     ogg_packet header_comm;
>     header_comm.packet = (unsigned char*)&commentHeaderContent;
>     header_comm.bytes = sizeof(struct OggCommentHeader);
>     header_comm.b_o_s = 0;
>     header_comm.e_o_s = 1; //We are ending the stream as there will   
> be no data.
>     header_comm.granulepos = -1; //This packet does not contain   
> audio data, so granule-position = -1
>     header_comm.packetno = 1; //Second packet.
>
>     ogg_stream_packetin(&os,&header_comm);
>
>     //***************
>     //Write everything out.
>     while(1){
>         int result=ogg_stream_flush(&os,&og);
>         if(result==0)break;
>         fwrite(og.header,1,og.header_len,fout);
>         fwrite(og.body,1,og.body_len,fout);
>     }
>
>     //And clean up.
>     ogg_stream_clear(&os);
>
>     fclose(fout);
>
>     printf("Done.\n");
>     return 0;
> }
>
> //Sincerely
> Daniel Armyr
> Beginner opus user




More information about the opus mailing list