Skip to content

Commit

Permalink
- Add audio support
Browse files Browse the repository at this point in the history
- Some fixes
  • Loading branch information
soywiz committed Nov 12, 2012
1 parent efc7c7b commit 40cd970
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 33 deletions.
Binary file modified ndll/Windows/nme-webm.ndll
Binary file not shown.
243 changes: 214 additions & 29 deletions project/common/ExternalInterface.cpp
Expand Up @@ -13,9 +13,11 @@
#include <hxcpp.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include "HxWebm.h"
#include "../libwebm/mkvparser.hpp"
#include <vorbis/codec.h>

#define DEFINE_FUNC(COUNT, NAME, ...) value NAME(__VA_ARGS__); DEFINE_PRIM(NAME, COUNT); value NAME(__VA_ARGS__)
#define DEFINE_FUNC_0(NAME) DEFINE_FUNC(0, NAME)
Expand Down Expand Up @@ -241,6 +243,178 @@ extern "C" {
return val;
}

ogg_packet InitOggPacket(int &mPacketCount, unsigned char* aData, size_t aLength, bool aBOS, bool aEOS, int64_t aGranulepos)
{
ogg_packet packet;
packet.packet = aData;
packet.bytes = aLength;
packet.b_o_s = aBOS;
packet.e_o_s = aEOS;
packet.granulepos = aGranulepos;
packet.packetno = mPacketCount++;
return packet;
}

class VorbisDecoder {
public:
// Vorbis decoder state
vorbis_info mVorbisInfo;
vorbis_comment mVorbisComment;
vorbis_dsp_state mVorbisDsp;
vorbis_block mVorbisBlock;
uint32_t mPacketCount;
uint32_t mChannels;
ogg_packet oggPacket;

VorbisDecoder() {
resetPacketNumber();
}

void resetPacketNumber() {
oggPacket.packetno = -1;
}

void preparePacket(unsigned char * data, const long size) {
oggPacket.packet = data;
oggPacket.bytes = size;
oggPacket.b_o_s = (oggPacket.packetno == -1);
oggPacket.e_o_s = false;
oggPacket.packetno++;
oggPacket.granulepos = -1;
}

int parseData(unsigned char * data, const long size, long long int time_ns, value decode_audio) {
int r;
preparePacket(data, size);

if (oggPacket.bytes > 0) {
r = vorbis_synthesis(&mVorbisBlock, &oggPacket);
if (r < 0) {
fprintf(stderr, "vorbis_synthesis failed, error: %d\n", r);
return -1;
}

r = vorbis_synthesis_blockin(&mVorbisDsp, &mVorbisBlock);
if (r < 0) {
fprintf(stderr, "vorbis_synthesis failed, error: %d\n", r);
return -1;
}

float **pcm = NULL;
int nsamples = 0;

if ((nsamples = vorbis_synthesis_pcmout(&mVorbisDsp, &pcm)) > 0) {
int nchannels = 2;
buffer pcm_data = alloc_buffer_len(nsamples * nchannels * sizeof(float));
float *samples = (float *)buffer_data(pcm_data);

for (int sample = 0; sample < nsamples; sample++) {
for (int channel = 0; channel < nchannels; channel++) {
float fval = pcm[channel][sample];
//short sval = (short)(fval * 32767);
*samples++ = fval;
}
}

val_call2(decode_audio, alloc_float((double)(time_ns / 1000) / (double)(1000 * 1000)), buffer_val(pcm_data));

//printf("%p: %d\n", pcm, samples);
vorbis_synthesis_read(&mVorbisDsp, nsamples);
}
}

return 0;
}

static uint64_t ne_xiph_lace_value(unsigned char ** np)
{
uint64_t lace;
uint64_t value;
unsigned char * p = *np;

lace = *p++;
value = lace;
while (lace == 255) {
lace = *p++;
value += lace;
}

*np = p;

return value;
}

// https://groups.google.com/a/webmproject.org/group/webm-discuss/tree/browse_frm/month/2010-09/be9ab6c87fb2bca2?rnum=41&_done=%2Fa%2Fwebmproject.org%2Fgroup%2Fwebm-discuss%2Fbrowse_frm%2Fmonth%2F2010-09%3F
int parseHeader(unsigned char * data, const long size) {
unsigned char * data_start = data;
int status;

vorbis_info_init(&mVorbisInfo);
vorbis_comment_init(&mVorbisComment);
memset(&mVorbisDsp, 0, sizeof(mVorbisDsp));
memset(&mVorbisBlock, 0, sizeof(mVorbisBlock));

int packetCount = *data++ + 1;
if (packetCount > 3) {
fprintf(stderr, "packetCount > 3: %d\n", packetCount);
return -1;
}

//printf("vorbis.parseHeader.packetCount: %d\n", packetCount);

int lengths[3] = {0};
int total = 0;

for (int header_num = 0; header_num < packetCount - 1; header_num++) {
int length = ne_xiph_lace_value(&data);
total += length;
lengths[header_num] = length;
}

lengths[2] = size - total;

for (int header_num = 0; header_num < packetCount; header_num++) {
//printf(" packer header[%d]: %d\n", header_num, lengths[header_num]);
}


for (int header_num = 0; header_num < packetCount; header_num++) {
uint64_t psize = lengths[header_num];
unsigned char * pdata = data;
preparePacket(pdata, psize);
data += psize;

//printf(" packet: %p, %d\n", pdata, psize);
//for (int m = 0; m < psize; m++) printf("%02X ", (unsigned char)pdata[m]);
//printf("\n");

assert(oggPacket.packetno == header_num);
status = vorbis_synthesis_headerin(&mVorbisInfo, &mVorbisComment, &oggPacket);
if (status < 0) {
fprintf(stderr, "vorbis_synthesis_headerin failed(%d), error: %d\n", header_num, status);
return -1;
}
}

printf("Vorbis: version:%d, channels:%d, rate:%d\n", mVorbisInfo.version, mVorbisInfo.channels, mVorbisInfo.rate);

status = vorbis_synthesis_init(&mVorbisDsp, &mVorbisInfo);
if (status < 0) {
fprintf(stderr, "vorbis_synthesis_init failed, error: %d\n", status);
return -1;
}

status = vorbis_block_init(&mVorbisDsp, &mVorbisBlock);
if (status < 0) {
fprintf(stderr, "vorbis_block_init failed, error: %d\n", status);
return -1;
}

return 0;
}
};

// http://dxr.mozilla.org/mozilla-central/content/media/webm/nsWebMReader.h.html
class MkvProcessor {
public:
IoMkvReader *reader;
Expand All @@ -254,6 +428,8 @@ extern "C" {
bool hasMore;
int width, height;
double frameRate;
double duration_sec;
VorbisDecoder *vorbisDecoder;

MkvProcessor(IoMkvReader *reader) {
//printf("MkvProcessor\n"); fflush(stdout);
Expand All @@ -268,6 +444,8 @@ extern "C" {
this->width = 0;
this->height = 0;
this->frameRate = 30;
this->duration_sec = 0;
this->vorbisDecoder = new VorbisDecoder();
}

~MkvProcessor() {
Expand Down Expand Up @@ -326,13 +504,12 @@ extern "C" {
printf("\t\tTimeCodeScale\t\t: %lld \n", timeCodeScale);
printf("\t\tDuration\t\t: %lld\n", duration_ns);

const double duration_sec = double(duration_ns) / 1000000000;
printf("\t\tDuration(secs)\t\t: %7.3lf\n", duration_sec);
this->duration_sec = double(duration_ns) / 1000000000;
printf("\t\tDuration(secs)\t\t: %7.3lf\n", this->duration_sec);

if (pTitle == NULL)
if (pTitle == NULL) {
printf("\t\tTrack Name\t\t: NULL\n");
else
{
} else {
printf("\t\tTrack Name\t\t: %ls\n", pTitle);
delete [] pTitle;
}
Expand Down Expand Up @@ -362,17 +539,17 @@ extern "C" {

pTracks = pSegment->GetTracks();

unsigned long i = 0;
const unsigned long j = pTracks->GetTracksCount();
unsigned long currentTrack = 0;
const unsigned long totalTracks = pTracks->GetTracksCount();

printf("\n\t\t\t Track Info\n");

while (i != j)
for (int currentTrack = 0; currentTrack < totalTracks; currentTrack++)
{
const Track* const pTrack = pTracks->GetTrackByIndex(i++);
printf("\t\t------------------------\n");
const Track* const pTrack = pTracks->GetTrackByIndex(currentTrack);

if (pTrack == NULL)
continue;
if (pTrack == NULL) continue;

const long trackType = pTrack->GetType();
const long trackNumber = pTrack->GetNumber();
Expand All @@ -383,51 +560,53 @@ extern "C" {
printf("\t\tTrack Number\t\t: %ld\n", trackNumber);
printf("\t\tTrack Uid\t\t: %lld\n", trackUid);

if (pTrackName == NULL)
if (pTrackName == NULL) {
printf("\t\tTrack Name\t\t: NULL\n");
else
{
} else {
printf("\t\tTrack Name\t\t: %ls \n", pTrackName);
delete [] pTrackName;
}

const char* const pCodecId = pTrack->GetCodecId();

if (pCodecId == NULL)
if (pCodecId == NULL) {
printf("\t\tCodec Id\t\t: NULL\n");
else
} else {
printf("\t\tCodec Id\t\t: %s\n", pCodecId);
}

const char* const pCodecName_ = pTrack->GetCodecNameAsUTF8();
const wchar_t* const pCodecName = utf8towcs(pCodecName_);

if (pCodecName == NULL)
if (pCodecName == NULL) {
printf("\t\tCodec Name\t\t: NULL\n");
else
{
} else {
printf("\t\tCodec Name\t\t: %ls\n", pCodecName);
delete [] pCodecName;
}

if (trackType == mkvparser::Track::kVideo)
{
if (trackType == mkvparser::Track::kVideo) {
const VideoTrack* const pVideoTrack = static_cast<const VideoTrack*>(pTrack);
this->width = pVideoTrack->GetWidth();
this->height = pVideoTrack->GetHeight();
this->frameRate = pVideoTrack->GetFrameRate();

printf("\t\tVideo Width\t\t: %lld\n", this->width);
printf("\t\tVideo Height\t\t: %lld\n", this->height);
printf("\t\tVideo Rate\t\t: %f\n", this->frameRate);
printf("\t\tVideo Width\t\t: %d\n", this->width);
printf("\t\tVideo Height\t\t: %d\n", this->height);
printf("\t\tVideo Rate\t\t: %f\n", (float)this->frameRate);
}

if (trackType == mkvparser::Track::kAudio)
{
if (trackType == mkvparser::Track::kAudio) {
const AudioTrack* const pAudioTrack = static_cast<const AudioTrack*>(pTrack);
size_t privateDataSize = 0;
unsigned char *privateDataPointer = (unsigned char *)pAudioTrack->GetCodecPrivate(privateDataSize);

printf("\t\tAudio Channels\t\t: %lld\n", pAudioTrack->GetChannels());
printf("\t\tAudio BitDepth\t\t: %lld\n", pAudioTrack->GetBitDepth());
printf("\t\tAddio Sample Rate\t: %.3f\n", pAudioTrack->GetSamplingRate());
printf("\t\tAddio Private Data\t: %p, %d\n", privateDataPointer, privateDataSize);

this->vorbisDecoder->parseHeader(privateDataPointer, privateDataSize);
}
}

Expand Down Expand Up @@ -510,9 +689,10 @@ extern "C" {
theFrame.Read(reader, (unsigned char *)buffer_data(frame_data));

if (trackType == mkvparser::Track::kVideo) {
//printf("%lld\n", time_ns);
val_call2(decode_video, alloc_float((double)(time_ns / 1000) / (double)(1000 * 1000)), buffer_val(frame_data));
} else {
val_call2(decode_audio, alloc_float((double)(time_ns / 1000) / (double)(1000 * 1000)), buffer_val(frame_data));
} else if (trackType == mkvparser::Track::kAudio) {
vorbisDecoder->parseData((unsigned char *)buffer_data(frame_data), buffer_size(frame_data), time_ns, decode_audio);
}
#endif

Expand Down Expand Up @@ -565,10 +745,11 @@ extern "C" {

DEFINE_FUNC_1(hx_webm_decoder_get_info, processor_value) {
MkvProcessor* processor = _get_MkvProcessor_from_value(processor_value);
value array = alloc_array(3);
value array = alloc_array(4);
val_array_set_i(array, 0, alloc_int(processor->width));
val_array_set_i(array, 1, alloc_int(processor->height));
val_array_set_i(array, 3, alloc_float(processor->frameRate));
val_array_set_i(array, 4, alloc_float(processor->duration_sec));
return array;
}

Expand All @@ -578,5 +759,9 @@ extern "C" {
//return alloc_bool(processor->hasMore());
return alloc_null();
}

DEFINE_FUNC_0(hx_vorbis_codec_dec_init) {
return alloc_null();
}
}
}
3 changes: 3 additions & 0 deletions project/common/HxWebm.h
Expand Up @@ -10,10 +10,13 @@
#include <hx/CFFI.h>
//#include <hx/CFFIAPI.h>

typedef unsigned long long uint64;
typedef unsigned int uint32;
typedef unsigned short uint16;
typedef unsigned char uint8;

typedef uint64 uint64_t;

typedef struct {
value func_read;
value func_seek;
Expand Down

0 comments on commit 40cd970

Please sign in to comment.