oggopus

# Streaming Ogg Opus file

This example shows how to stream an Ogg Opus file to a voice channel. This example requires some additional dependencies, namely `libogg` and `opusfile`.

    #include<dpp/dpp.h>
    #include<iomanip>
    #include<sstream>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<ogg/ogg.h>
    #include<opus/opusfile.h>

    intmain(){
    /*Loadanoggopusfileintomemory.
    *Thebotexpectsopuspacketstobe2channelstereo,48000Hz.
    *
    *Youmayuseffmpegtoencodesongstooggopus:
    *ffmpeg-i/path/to/song-c:alibopus-ar48000-ac2-vn-b:a96K/path/to/opus.ogg
    */

    dpp::clusterbot("token");

    bot.on_log(dpp::utility::cout_logger());

    /*Theeventisfiredwhensomeoneissuesyourcommands*/
    bot.on_slashcommand([](constdpp::slashcommand_t&event){

    /*Checkwhichcommandtheyran*/
    if(event.command.get_command_name()=="join"){

    /*Gettheguild*/
    dpp::guild*g=dpp::find_guild(event.command.guild_id);

    /*Attempttoconnecttoavoicechannel,returnsfalseifwefailtoconnect.*/
    if(!g->connect_member_voice(*event.owner,event.command.get_issuing_user().id)){
    event.reply("Youdon'tseemtobeinavoicechannel!");
    return;
    }

    /*Telltheuserwejoinedtheirchannel.*/
    event.reply("Joinedyourchannel!");
    }elseif(event.command.get_command_name()=="play"){

    /*Getthevoicechannelthebotisin,inthiscurrentguild.*/
    dpp::voiceconn*v=event.from()->get_voice(event.command.guild_id);

    /*Ifthevoicechannelwasinvalid,orthereisanissuewithit,thentelltheuser.*/
    if(!v||!v->voiceclient||!v->voiceclient->is_ready()){
    event.reply("Therewasanissuewithgettingthevoicechannel.MakesureI'minavoicechannel!");
    return;
    }

    ogg_sync_stateoy;
    ogg_stream_stateos;
    ogg_pageog;
    ogg_packetop;
    OpusHeadheader;
    char*buffer;

    FILE*fd;

    fd=fopen("/path/to/opus.ogg","rb");

    fseek(fd,0L,SEEK_END);
    size_tsz=ftell(fd);
    rewind(fd);

    ogg_sync_init(&oy);

    buffer=ogg_sync_buffer(&oy,sz);
    fread(buffer,1,sz,fd);

    ogg_sync_wrote(&oy,sz);

    if(ogg_sync_pageout(&oy,&og)!=1){
    fprintf(stderr,"Doesnotappeartobeoggstream.\n");
    exit(1);
    }

    ogg_stream_init(&os,ogg_page_serialno(&og));

    if(ogg_stream_pagein(&os,&og)<0){
    fprintf(stderr,"Errorreadinginitialpageofoggstream.\n");
    exit(1);
    }

    if(ogg_stream_packetout(&os,&op)!=1){
    fprintf(stderr,"Errorreadingheaderpacketofoggstream.\n");
    exit(1);
    }

    /*Wemustensurethattheoggstreamactuallycontainsopusdata*/
    if(!(op.bytes>8&&!memcmp("OpusHead",op.packet,8))){
    fprintf(stderr,"Notanoggopusstream.\n");
    exit(1);
    }

    /*Parsetheheadertogetstreaminfo*/
    interr=opus_head_parse(&header,op.packet,op.bytes);
    if(err){
    fprintf(stderr,"Notaoggopusstream\n");
    exit(1);
    }

    /*NowweensuretheencodingiscorrectforDiscord*/
    if(header.channel_count!=2&&header.input_sample_rate!=48000){
    fprintf(stderr,"WrongencodingforDiscord,mustbe48000Hzsampleratewith2channels.\n");
    exit(1);
    }

    /*Nowloopthoughallthepagesandsendthepacketstothevc*/
    while(ogg_sync_pageout(&oy,&og)==1){
    ogg_stream_init(&os,ogg_page_serialno(&og));

    if(ogg_stream_pagein(&os,&og)<0){
    fprintf(stderr,"ErrorreadingpageofOggbitstreamdata.\n");
    exit(1);
    }

    while(ogg_stream_packetout(&os,&op)!=0){

    /*Readremainingheaders*/
    if(op.bytes>8&&!memcmp("OpusHead",op.packet,8)){
    interr=opus_head_parse(&header,op.packet,op.bytes);
    if(err){
    fprintf(stderr,"Notaoggopusstream\n");
    exit(1);
    }

    if(header.channel_count!=2&&header.input_sample_rate!=48000){
    fprintf(stderr,"WrongencodingforDiscord,mustbe48000Hzsampleratewith2channels.\n");
    exit(1);
    }

    continue;
    }

    /*Skiptheopustags*/
    if(op.bytes>8&&!memcmp("OpusTags",op.packet,8))
    continue;

    /*Sendtheaudio*/
    intsamples=opus_packet_get_samples_per_frame(op.packet,48000);

    v->voiceclient->send_audio_opus(op.packet,op.bytes,samples/48);
    }
    }

    /*Cleanup*/
    ogg_stream_clear(&os);
    ogg_sync_clear(&oy);

    event.reply("Finishedplayingtheaudiofile!");
    }
    });

    bot.on_ready([&bot](constdpp::ready_t&event){
    if(dpp::run_once<struct register_bot_commands>()){
    /*Createanewcommand.*/
    dpp::slashcommandjoincommand("join","Joinsyourvoicechannel.",bot.me.id);
    dpp::slashcommandplaycommand("play","Playsanoggfile.",bot.me.id);

    bot.global_bulk_command_create({joincommand,playcommand});
    }
    });

    /*Startbot*/
    bot.start(dpp::st_wait);

    return0;
    }

You can compile this example using the following command

    c++/path/to/source.cc-ldpp-lopus-lopusfile-logg-I/usr/include/opus

## Using liboggz

You can use `liboggz` to stream an Ogg Opus file to discord voice channel. `liboggz` provides higher level abstraction and useful APIs. Some features `liboggz` provides include: seeking and timestamp interpretation. Read more on the documentation.

    #include<dpp/dpp.h>
    #include<iomanip>
    #include<sstream>
    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    #include<oggz/oggz.h>

    intmain(){
    /*Loadanoggopusfileintomemory.
    *Thebotexpectsopuspacketstobe2channelstereo,48000Hz.
    *
    *Youmayuseffmpegtoencodesongstooggopus:
    *ffmpeg-i/path/to/song-c:alibopus-ar48000-ac2-vn-b:a96K/path/to/opus.ogg
    */

    /*Setupthebot*/
    dpp::clusterbot("token");

    bot.on_log(dpp::utility::cout_logger());

    /*Theeventisfiredwhensomeoneissuesyourcommands*/
    bot.on_slashcommand([](constdpp::slashcommand_t&event){

    /*Checkwhichcommandtheyran*/
    if(event.command.get_command_name()=="join"){
    /*Gettheguild*/
    dpp::guild*g=dpp::find_guild(event.command.guild_id);

    /*Attempttoconnecttoavoicechannel,returnsfalseifwefailtoconnect.*/
    if(!g->connect_member_voice(*event.owner,event.command.get_issuing_user().id)){
    event.reply("Youdon'tseemtobeinavoicechannel!");
    return;
    }

    /*Telltheuserwejoinedtheirchannel.*/
    event.reply("Joinedyourchannel!");
    }elseif(event.command.get_command_name()=="play"){
    /*Getthevoicechannelthebotisin,inthiscurrentguild.*/
    dpp::voiceconn*v=event.from()->get_voice(event.command.guild_id);

    /*Ifthevoicechannelwasinvalid,orthereisanissuewithit,thentelltheuser.*/
    if(!v||!v->voiceclient||!v->voiceclient->is_ready()){
    event.reply("Therewasanissuewithgettingthevoicechannel.MakesureI'minavoicechannel!");
    return;
    }

    /*loadtheaudiofilewithoggz*/
    OGGZ*track_og=oggz_open("/path/to/opus.ogg",OGGZ_READ);

    /*Iftherewasanissuereadingthefile,telltheuserandstop*/
    if(!track_og){
    fprintf(stderr,"Erroropeningfile\n");
    event.reply("Therewasanissueopeningthefile!");
    return;
    }

    /*setreadcallback,thiscallbackwillbecalledonpacketswiththeserialno,
    *-1meanseverypacketwillbehandledwiththiscallback.
    */
    oggz_set_read_callback(
    track_og,-1,
    [](OGGZ*oggz,oggz_packet*packet,longserialno,
    void*user_data){
    dpp::voiceconn*voiceconn=(dpp::voiceconn*)user_data;

    /*sendtheaudio*/
    voiceconn->voiceclient->send_audio_opus(packet->op.packet,
    packet->op.bytes);

    /*makesuretoalwaysreturn0here,readmoreonoggzdocumentation*/
    return0;
    },
    /*thiswillbethevalueofvoid*user_data*/
    (void*)v
    );

    //readloop
    while(v&&v->voiceclient&&!v->voiceclient->terminating){
    /*youcantweakthistowhatever.HereIuseBUFSIZ,definedin
    *stdio.has8192.
    */
    staticconstconstexprlongCHUNK_READ=BUFSIZ*2;

    constlongread_bytes=oggz_read(track_og,CHUNK_READ);

    /*breakoneof*/
    if(!read_bytes){
    break;
    }
    }

    /*Don'tforgettofreethememory*/
    oggz_close(track_og);

    event.reply("Finishedplayingtheaudiofile!");
    }
    });

    bot.on_ready([&bot](constdpp::ready_t&event){
    if(dpp::run_once<struct register_bot_commands>()){
    /*Createanewcommand.*/
    dpp::slashcommandjoincommand("join","Joinsyourvoicechannel.",bot.me.id);
    dpp::slashcommandplaycommand("play","Playsanoggfile.",bot.me.id);

    bot.global_bulk_command_create({joincommand,playcommand});
    }
    });

    /*Startbot*/
    bot.start(dpp::st_wait);

    return0;
    }

You can compile this example using the following command:

    c++/path/to/source.cc-ldpp-loggz