Skip to content

Instantly share code, notes, and snippets.

@masuidrive
Last active January 11, 2024 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masuidrive/e70e9f0cced5a9d0cf14489649926901 to your computer and use it in GitHub Desktop.
Save masuidrive/e70e9f0cced5a9d0cf14489649926901 to your computer and use it in GitHub Desktop.
opusをoggコンテナに入れるためのコード
#include <ogg/ogg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
// get_opus_raw関数のプロトタイプ
// この関数は外部で定義され、Opusエンコードされたraw binaryを提供します。
int16_t get_opus_raw(uint8_t *buffer);
#define OPUS_CHANNELS 1
#define PRE_SKIP 3840
#define SAMPLING_RATE 48000
#define GAIN 0
#define CHANNEL_MAPPING 0
uint16_t little_endian_uint16(uint16_t x)
{
return (x >> 8) | (x << 8);
}
// Opusヘッダの構造体
struct opus_header
{
char signature[8]; // "OpusHead"
uint8_t version;
uint8_t channel_count;
uint16_t pre_skip;
uint32_t input_sample_rate;
int16_t output_gain;
uint8_t channel_mapping;
} __attribute__((packed));
// Opusヘッダを準備する関数
int create_opus_header(unsigned char **header_data, int *header_len)
{
struct opus_header header;
// Opusヘッダのデータを設定
memcpy(header.signature, "OpusHead", 8);
header.version = 1;
header.channel_count = (OPUS_CHANNELS);
header.pre_skip = (PRE_SKIP);
header.input_sample_rate = (SAMPLING_RATE);
header.output_gain = (GAIN);
header.channel_mapping = (CHANNEL_MAPPING);
*header_len = sizeof(header);
*header_data = malloc(*header_len);
if (*header_data == NULL)
{
return -1;
}
memcpy(*header_data, &header, *header_len);
return 0;
}
int main()
{
int ret;
ogg_stream_state os; // OGGストリームステート
ogg_packet op; // OGGパケット
unsigned char *header_data; // Opusヘッダデータ
int header_len; // Opusヘッダの長さ
uint8_t buffer[4096]; // Opus rawデータバッファ
int16_t packet_size; // Opus rawパケットサイズ
ogg_page og_page;
FILE *output_file = fopen("output.ogg", "wb");
if (output_file == NULL)
{
fprintf(stderr, "Failed to open output file\n");
ogg_stream_clear(&os);
return 1;
}
// Opusヘッダを準備
ret = create_opus_header(&header_data, &header_len);
if (ret != 0)
{
fprintf(stderr, "Failed to create Opus header\n");
return 1;
}
// OGGストリームを初期化
ogg_stream_init(&os, rand()); // ランダムなシリアル番号で初期化
int packetno = 0;
// OGGパケットにOpusヘッダをセット
memset(&op, 0, sizeof(op));
op.packet = header_data;
op.bytes = header_len;
op.b_o_s = (1); // ストリームの開始を示す
op.e_o_s = 0; // ストリームの終了ではない
op.granulepos = 0;
op.packetno = packetno++; // 最初のパケット
// OGGストリームにヘッダパケットを追加
ogg_stream_packetin(&os, &op);
while (ogg_stream_flush(&os, &og_page) > 0)
{
fprintf(stderr, "Writing OpusHead page... \n");
fwrite(og_page.header, 1, og_page.header_len, output_file);
fwrite(og_page.body, 1, og_page.body_len, output_file);
}
free(header_data); // ヘッダデータのメモリを解放
const char *opus_tags_header = "OpusTags";
const char *vendor_string = "masuidrive";
const uint32_t vendor_string_len = strlen(vendor_string);
const uint32_t opus_tags_header_len = strlen(opus_tags_header);
const uint32_t total_len = opus_tags_header_len + 4 + vendor_string_len + 4; // 4はベンダー文字列とコメントリスト長さのため
uint8_t opus_tags_packet_data[total_len];
// OpusTagsヘッダのコピー
memcpy(opus_tags_packet_data, opus_tags_header, opus_tags_header_len);
// ベンダー文字列の長さとベンダー文字列を追加
uint32_t length = (vendor_string_len); // ネットワークバイトオーダーに変換
memcpy(opus_tags_packet_data + opus_tags_header_len, &length, 4);
memcpy(opus_tags_packet_data + opus_tags_header_len + 4, vendor_string, vendor_string_len);
// コメントリストの長さ(ここでは0、つまりコメントなし)
length = 0;
memcpy(opus_tags_packet_data + opus_tags_header_len + 4 + vendor_string_len, &length, 4);
// OpusTagsパケットの準備
ogg_packet opus_tags_packet;
memset(&opus_tags_packet, 0, sizeof(opus_tags_packet));
opus_tags_packet.packet = opus_tags_packet_data;
opus_tags_packet.bytes = total_len;
opus_tags_packet.b_o_s = 0; // ヘッダの始まりではない
opus_tags_packet.e_o_s = 0; // ストリームの終わりではない
opus_tags_packet.granulepos = 0;
opus_tags_packet.packetno = packetno++; // OpusHeadに続く二番目のパケット
// OpusTagsパケットをストリームに追加
ogg_stream_packetin(&os, &opus_tags_packet);
while (ogg_stream_flush(&os, &og_page) > 0)
{
fprintf(stderr, "Writing OpusTags page... %d\n", (int)total_len);
fwrite(og_page.header, 1, og_page.header_len, output_file);
fwrite(og_page.body, 1, og_page.body_len, output_file);
}
ogg_int64_t granulepos = PRE_SKIP;
// Opus raw binaryを読み込み、OGGストリームに追加
while ((packet_size = get_opus_raw(buffer)) > 0)
{
// printf("packet_size: %d\n", packet_size);
// OGGパケットにOpus raw binaryをセット
memset(&op, 0, sizeof(op));
op.packet = buffer;
op.bytes = packet_size;
op.packetno = packetno++;
op.granulepos = granulepos;
granulepos += 960;
ogg_stream_packetin(&os, &op);
while (ogg_stream_pageout(&os, &og_page) > 0)
{
fwrite(og_page.header, 1, og_page.header_len, output_file);
fwrite(og_page.body, 1, og_page.body_len, output_file);
}
}
// ストリームの終了を示すパケットを設定
memset(&op, 0, sizeof(op));
op.packet = NULL;
op.bytes = 0;
op.b_o_s = 0;
op.e_o_s = 1; // ストリームの終了を示す
op.packetno = packetno++;
op.granulepos = granulepos;
// OGGストリームに終了パケットを追加
ogg_stream_packetin(&os, &op);
// 生成されたページをファイルに書き出す
while (ogg_stream_flush(&os, &og_page) > 0)
{
fwrite(og_page.header, 1, og_page.header_len, output_file);
fwrite(og_page.body, 1, og_page.body_len, output_file);
}
fclose(output_file);
// OGGストリームをクリア
ogg_stream_clear(&os);
return 0;
}
// get_opus_raw関数の実装
int16_t get_opus_raw(uint8_t *buffer)
{
static FILE *input_file = NULL;
if (input_file == NULL)
{
input_file = fopen("input.dat", "rb");
if (input_file == NULL)
{
fprintf(stderr, "Failed to open input file\n");
return -1;
}
}
uint16_t size_to_read;
if (fread(&size_to_read, sizeof(uint16_t), 1, input_file) != 1)
{
// サイズの読み込みに失敗、またはファイルの終わりに達した
if (feof(input_file))
{
// ファイルの終わりに達した
fclose(input_file);
input_file = NULL;
return -1;
}
else
{
// 読み込みエラー
fprintf(stderr, "Failed to read size from input file\n");
fclose(input_file);
input_file = NULL;
return -1;
}
}
size_t bytes_read = fread(buffer, 1, size_to_read, input_file);
if (bytes_read < size_to_read)
{
// 指定されたサイズよりも少ないデータが読み込まれた
fprintf(stderr, "Failed to read expected amount of data\n");
fclose(input_file);
input_file = NULL;
return -1;
}
return bytes_read;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment