Tried on several test files but wasn't able to get useful information out of the bitstream file. Examples attached:
Last login: Thu Aug 7 21:52:09 on ttys047
Welcome to fish, the friendly interactive shell
jin@Joy-MBP ~> goa_parse /Users/jin/libmpegh/1_ext_ren_oam_md.bs
Frame 0:
frame_length_code: 16 (nominal_samples=1024)
audio_truncation: 0 (0=none,1=left,2=right)
num_samples: 1024
objects: 10
Object[0]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 1
group_priority: 7
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[1]:
element_id: 1
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[2]:
element_id: 2
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[3]:
element_id: 3
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[4]:
element_id: 4
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[5]:
element_id: 5
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[6]:
element_id: 6
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[7]:
element_id: 7
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[8]:
element_id: 8
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[9]:
element_id: 9
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
extensions: 0
jin@Joy-MBP ~> goa_parse /Users/jin/libmpegh/2_ext_ren_oam_md.bs
Frame 0:
frame_length_code: 16 (nominal_samples=1024)
audio_truncation: 0 (0=none,1=left,2=right)
num_samples: 1024
objects: 13
Object[0]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 1
group_priority: 7
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[1]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[2]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[3]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[4]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[5]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[6]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[7]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[8]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[9]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[10]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[11]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
Object[12]:
element_id: 0
has_dynamic_priority: 1
has_uniform_spread: 0
oam_frames: 1
OAM[0]: present=0
fixed_position_flag: 0
group_priority: 0
diffuseness: 0.000
divergence: 0.000
divergence_az_range: 0.00°
exclusion_sectors: 0
extensions: 0
jin@Joy-MBP ~>
// goa_parse.c
// Parser for mpegh3da_getObjectAudioAndMetadata() object-output interface
// ISO/IEC 23008-3:2022 §17.10.3.2–3 (Tables 269–271) [build: cc -O2 -std=c99 goa_parse.c -lm -o goa_parse]
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
typedef struct {
const uint8_t *buf;
size_t size_bytes;
size_t bitpos; // bit index into buf (for packed mode)
int word32_mode; // if 1: read one 32-bit word per field, MSB-aligned
size_t word_index; // index of 32-bit words consumed (word32 mode)
} BR;
static uint32_t read_bits_packed(BR *br, int nbits) {
uint32_t v = 0;
for (int i = 0; i < nbits; i++) {
size_t byte_off = br->bitpos >> 3;
int bit_off = 7 - (int)(br->bitpos & 7);
if (byte_off >= br->size_bytes) {
fprintf(stderr, "ERROR: bitstream overread in packed mode\n");
exit(2);
}
v = (v << 1) | ((br->buf[byte_off] >> bit_off) & 1);
br->bitpos++;
}
return v;
}
static uint32_t read_bits_word32(BR *br, int nbits) {
// Expect each field in its own 32-bit BE word, MSB-aligned.
size_t byte_off = br->word_index * 4;
if (byte_off + 4 > br->size_bytes) {
fprintf(stderr, "ERROR: bitstream overread in word32 mode\n");
exit(2);
}
uint32_t w = (br->buf[byte_off+0] << 24) |
(br->buf[byte_off+1] << 16) |
(br->buf[byte_off+2] << 8) |
(br->buf[byte_off+3] << 0);
br->word_index++;
// Take the top nbits
if (nbits == 32) return w;
return (nbits == 0) ? 0 : (w >> (32 - nbits)) & ((1u << nbits) - 1u);
}
static uint32_t rb(BR *br, int nbits) {
return br->word32_mode ? read_bits_word32(br, nbits) : read_bits_packed(br, nbits);
}
static void skip_bits(BR *br, int nbits) { (void)rb(br, nbits); }
static void print_indent(int n) { while (n--) fputs(" ", stdout); }
static void parse_one_goa_frame(BR *br, int frame_idx) {
// ---- FRAME CONFIGURATION ----
int goa_frameLength = (int)rb(br, 6);
int goa_audioTruncation = (int)rb(br, 2);
int goa_numSamples;
if (goa_audioTruncation > 0) {
goa_numSamples = (int)rb(br, 13);
} else {
goa_numSamples = (goa_frameLength << 6);
}
printf("Frame %d:\n", frame_idx);
print_indent(1); printf("frame_length_code: %d (nominal_samples=%d)\n",
goa_frameLength, (goa_frameLength << 6)); // :contentReference[oaicite:0]{index=0}
print_indent(1); printf("audio_truncation: %d (0=none,1=left,2=right)\n", goa_audioTruncation); // :contentReference[oaicite:1]{index=1}
print_indent(1); printf("num_samples: %d\n", goa_numSamples); // :contentReference[oaicite:2]{index=2}
// ---- OBJECT METADATA ----
int goa_numberOfOutputObjects = (int)rb(br, 9);
print_indent(1); printf("objects: %d\n", goa_numberOfOutputObjects); // :contentReference[oaicite:3]{index=3}
for (int o = 0; o < goa_numberOfOutputObjects; o++) {
print_indent(1); printf("Object[%d]:\n", o);
int goa_elementID = (int)rb(br, 9);
int goa_hasDynamicPriority = (int)rb(br, 1);
int goa_hasUniformSpread = (int)rb(br, 1);
print_indent(2); printf("element_id: %d\n", goa_elementID); // :contentReference[oaicite:4]{index=4}
print_indent(2); printf("has_dynamic_priority: %d\n", goa_hasDynamicPriority); // :contentReference[oaicite:5]{index=5}
print_indent(2); printf("has_uniform_spread: %d\n", goa_hasUniformSpread); // :contentReference[oaicite:6]{index=6}
int goa_numOAMframes = (int)rb(br, 6);
print_indent(2); printf("oam_frames: %d\n", goa_numOAMframes); // :contentReference[oaicite:7]{index=7}
for (int nf = 0; nf < goa_numOAMframes; nf++) {
int metaPresent = (int)rb(br, 1);
print_indent(3); printf("OAM[%d]: present=%d\n", nf, metaPresent); // :contentReference[oaicite:8]{index=8}
if (metaPresent) {
int az = (int)rb(br, 8);
int el = (int)rb(br, 6);
int rad = (int)rb(br, 4);
int gain = (int)rb(br, 7);
double az_deg = fmin(fmax((az - 128) * 1.5, -180.0), 180.0); // :contentReference[oaicite:9]{index=9}
double el_deg = fmin(fmax((el - 32) * 3.0, -90.0), 90.0); // :contentReference[oaicite:10]{index=10}
double radius = fmin(fmax(pow(2.0, (rad/3.0)) / 2.0, 0.5), 16.0); // :contentReference[oaicite:11]{index=11}
double gain_lin = fmin(fmax(pow(10.0, (gain - 96.0)/40.0), 0.004), 5.957);// :contentReference[oaicite:12]{index=12}
print_indent(4); printf("pos: azimuth=%.2f° elevation=%.2f° radius=%.3f\n", az_deg, el_deg, radius);
print_indent(4); printf("gain: %.4f\n", gain_lin); // :contentReference[oaicite:13]{index=13}
if (goa_hasDynamicPriority) {
int dynPrio = (int)rb(br, 3);
print_indent(4); printf("dynamic_priority: %d\n", dynPrio); // :contentReference[oaicite:14]{index=14}
}
if (goa_hasUniformSpread) {
int spread = (int)rb(br, 7);
double spread_deg = fmin(fmax(spread * 1.5, 0.0), 180.0); // :contentReference[oaicite:15]{index=15}
print_indent(4); printf("spread_uniform: %.2f°\n", spread_deg);
} else {
int sw = (int)rb(br, 7);
int sh = (int)rb(br, 5);
int sd = (int)rb(br, 4);
double sw_deg = fmin(fmax(sw * 1.5, 0.0), 180.0); // :contentReference[oaicite:16]{index=16}
double sh_deg = fmin(fmax(sh * 3.0, 0.0), 90.0); // :contentReference[oaicite:17]{index=17}
double sd_val = fmin(fmax(pow(2.0, (sd/3.0))/2.0 - 0.5, 0.0), 15.5); // :contentReference[oaicite:18]{index=18}
print_indent(4); printf("spread_nonuniform: width=%.2f° height=%.2f° depth=%.2f\n",
sw_deg, sh_deg, sd_val);
}
}
}
int fixedPosition = (int)rb(br, 1);
int groupPriority = (int)rb(br, 3);
print_indent(2); printf("fixed_position_flag: %d\n", fixedPosition); // :contentReference[oaicite:19]{index=19}
print_indent(2); printf("group_priority: %d\n", groupPriority); // :contentReference[oaicite:20]{index=20}
int diffuseness = (int)rb(br, 7);
int divergence = (int)rb(br, 7);
int divAzRange = (int)rb(br, 6);
print_indent(2); printf("diffuseness: %.3f\n", diffuseness / 127.0); // :contentReference[oaicite:21]{index=21}
print_indent(2); printf("divergence: %.3f\n", divergence / 127.0); // :contentReference[oaicite:22]{index=22}
print_indent(2); printf("divergence_az_range: %.2f°\n", divAzRange * 3.0); // :contentReference[oaicite:23]{index=23}
int numExcl = (int)rb(br, 4);
print_indent(2); printf("exclusion_sectors: %d\n", numExcl); // :contentReference[oaicite:24]{index=24}
for (int s = 0; s < numExcl; s++) {
int usePredef = (int)rb(br, 1);
if (usePredef) {
int idx = (int)rb(br, 4);
print_indent(3); printf("sector[%d]: predefined index=%d\n", s, idx); // :contentReference[oaicite:25]{index=25}
} else {
int minAz = (int)rb(br, 7);
int maxAz = (int)rb(br, 7);
int minEl = (int)rb(br, 5);
int maxEl = (int)rb(br, 5);
double minAzDeg = fmin(fmax(3.0 * (minAz - 63), -180.0), 180.0); // :contentReference[oaicite:26]{index=26}
double maxAzDeg = fmin(fmax(3.0 * (maxAz - 63), -180.0), 180.0); // :contentReference[oaicite:27]{index=27}
double minElDeg = fmin(fmax(6.0 * (minEl - 15), -90.0), 90.0); // :contentReference[oaicite:28]{index=28}
double maxElDeg = fmin(fmax(6.0 * (maxEl - 15), -90.0), 90.0); // :contentReference[oaicite:29]{index=29}
print_indent(3); printf("sector[%d]: az=[%.1f, %.1f]° el=[%.1f, %.1f]°\n",
s, minAzDeg, maxAzDeg, minElDeg, maxElDeg);
}
}
}
// ---- GOA EXTENSION ELEMENTS ----
int numExt = (int)rb(br, 3);
print_indent(1); printf("extensions: %d\n", numExt); // :contentReference[oaicite:30]{index=30}
for (int e = 0; e < numExt; e++) {
int type = (int)rb(br, 3);
int length = (int)rb(br, 10);
print_indent(2); printf("ext[%d]: type=%d length_bits=%d\n", e, type, length); // :contentReference[oaicite:31]{index=31}
if (type == 0) { // ID_EXT_GOA_PROD_METADATA
int hasObjDist = (int)rb(br, 1);
print_indent(3); printf("ProductionMetadata: hasObjectDistance=%d\n", hasObjDist); // :contentReference[oaicite:32]{index=32}
if (hasObjDist) {
for (int o = 0; o < goa_numberOfOutputObjects; o++) {
int distCode = (int)rb(br, 9);
double dist = (distCode == 0) ? 0.0
: 0.01 * pow(2.0, 0.0472188798661443 * (distCode - 1)); // :contentReference[oaicite:33]{index=33}
print_indent(4); printf("object[%d].distance = %.3f m (code=%d)\n", o, dist, distCode);
}
}
// If there are remaining padding bits inside this extension, skip them:
// We consumed 1 + 9*objects bits; if less than 'length', skip the rest.
// (Safe no-op for packed streams that already include just the fields.)
} else {
// Unknown extension: skip payload bits
skip_bits(br, length);
}
}
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <.bs file> [--word32] [--frames N]\n", argv[0]);
return 1;
}
const char *path = argv[1];
int word32 = 0;
int frames = 1; // try one frame by default; increase if your dump concatenates frames
for (int i = 2; i < argc; i++) {
if (!strcmp(argv[i], "--word32")) word32 = 1;
else if (!strcmp(argv[i], "--frames") && i+1 < argc) { frames = atoi(argv[++i]); }
}
FILE *f = fopen(path, "rb");
if (!f) { perror("fopen"); return 1; }
fseek(f, 0, SEEK_END);
long sz = ftell(f);
fseek(f, 0, SEEK_SET);
if (sz <= 0) { fprintf(stderr, "ERROR: empty file\n"); fclose(f); return 1; }
uint8_t *buf = (uint8_t*)malloc(sz);
if (!buf) { fprintf(stderr, "OOM\n"); fclose(f); return 1; }
if (fread(buf, 1, sz, f) != (size_t)sz) { fprintf(stderr, "ERROR: fread\n"); free(buf); fclose(f); return 1; }
fclose(f);
BR br = { .buf = buf, .size_bytes = (size_t)sz, .bitpos = 0, .word32_mode = word32, .word_index = 0 };
for (int i = 0; i < frames; i++) {
parse_one_goa_frame(&br, i);
// If your dump includes multiple frames packed back-to-back, set --frames accordingly.
// For "word32" dumps that repeat a pattern, increase --frames to walk through.
}
free(buf);
return 0;
}
Tried on several test files but wasn't able to get useful information out of the bitstream file. Examples attached:
_ext_ren_oam_md.bs.zip
goa_parse, written by chatgpt