Last active
January 11, 2024 14:02
-
-
Save masuidrive/e70e9f0cced5a9d0cf14489649926901 to your computer and use it in GitHub Desktop.
opusをoggコンテナに入れるためのコード
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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