cuticle/hi/opus.c

113 lines
3.0 KiB
C

#include"node.h"
#include<opus.h>
#include<string.h>
#include"img.h"
#include<stdlib.h>
#include<stdio.h>
struct CHiEncodeOpusNode {
CHiPubNode pubn;
size_t pcmSamples, pcmCapacity;
int16_t *pcmbuf;
size_t timestamp;
bool firstFrame;
OpusEncoder *enc;
};
static int encodeopus_perform(CHiPubNode *pubn) {
struct CHiEncodeOpusNode *n = (struct CHiEncodeOpusNode*) pubn;
CHiImage *newpcm = CHi_Crawl(&pubn->sinks[0])->data.sample;
if(newpcm) {
if(n->pcmSamples + newpcm->width > n->pcmCapacity) {
n->pcmbuf = realloc(n->pcmbuf, sizeof(*n->pcmbuf) * (n->pcmCapacity = ((n->pcmSamples + newpcm->width + 1023) & ~1023)));
}
memcpy(&n->pcmbuf[n->pcmSamples], newpcm->data16, sizeof(*n->pcmbuf) * newpcm->width);
n->pcmSamples += newpcm->width;
}
CHiBSFrames *frames = malloc(sizeof(*frames));
frames->count = 0;
if(!n->firstFrame) {
frames = CHi_BS_Grow(frames, 1);
struct OpusHead {
uint8_t magic[8];
uint8_t version;
uint8_t channelCount;
uint16_t preSkip;
uint32_t inputSampleRate;
uint16_t outputGain;
uint8_t mappingFamily;
} __attribute__((packed));
struct OpusHead *head = calloc(1, sizeof(*head));
*head = (struct OpusHead) {{'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'}, 1, 1, 3840, 48000, 0, 0};
frames->data[0].timestamp = 0;
frames->data[0].flags = CUTIHI_BS_SETUP_PACKET;
frames->data[0].ptr = head;
frames->data[0].sz = sizeof(*head);
n->firstFrame = true;
}
size_t samp;
for(samp = 0; samp + 960 <= n->pcmSamples; samp += 960) {
frames = CHi_BS_Grow(frames, 1);
frames->data[frames->count - 1].timestamp = n->timestamp;
frames->data[frames->count - 1].ptr = malloc(1276);
frames->data[frames->count - 1].sz = opus_encode(n->enc, &n->pcmbuf[samp], 960, frames->data[frames->count - 1].ptr, 1276);
if(frames->data[frames->count - 1].sz < 0) puts("OPUS ERR");
n->timestamp += 20;
}
memmove(n->pcmbuf, n->pcmbuf + samp, sizeof(*n->pcmbuf) * (n->pcmSamples - samp));
n->pcmSamples -= samp;
pubn->sources[0].type = CUTIHI_VAL_OPUSBS;
pubn->sources[0].data.bitstream = frames;
return 1;
}
static int CHi_EncodeOpus_Start(CHiPubNode *pubn) {
struct CHiEncodeOpusNode *n = (struct CHiEncodeOpusNode*) pubn;
int error;
n->enc = opus_encoder_create(48000, 1, OPUS_APPLICATION_AUDIO, &error);
n->pcmSamples = 0;
n->pcmCapacity = 1024;
n->pcmbuf = malloc(sizeof(*n->pcmbuf) * n->pcmCapacity);
n->timestamp = 0;
return 1;
}
static int CHi_EncodeOpus_Stop(CHiPubNode *pubn) {
struct CHiEncodeOpusNode *n = (struct CHiEncodeOpusNode*) pubn;
opus_encoder_destroy(n->enc);
free(n->pcmbuf);
return 1;
}
CUTIVIS CHiPubNode *CHi_EncodeOpus() {
struct CHiEncodeOpusNode *ret = calloc(1, sizeof(*ret));
ret->pubn.type = CUTIHI_T('CEnc','Opus');
ret->pubn.Start = CHi_EncodeOpus_Start;
ret->pubn.Perform = &encodeopus_perform;
ret->pubn.Stop = CHi_EncodeOpus_Stop;
ret->pubn.sinks = calloc(sizeof(*ret->pubn.sinks), ret->pubn.sinkCount = 1);
ret->pubn.sources = calloc(sizeof(*ret->pubn.sources), ret->pubn.sourceCount = 1);
return &ret->pubn;
}