mirror of
https://github.com/fmang/opustags.git
synced 2024-11-10 15:37:23 +01:00
group ogg encoding/decoding variables together
This commit is contained in:
parent
2905b193b1
commit
07af78519b
@ -76,6 +76,8 @@ int main(int argc, char **argv){
|
||||
int overwrite = 0;
|
||||
int print_help = 0;
|
||||
int c;
|
||||
ot::ogg_reader reader;
|
||||
ot::ogg_writer writer;
|
||||
while((c = getopt_long(argc, argv, "ho:i::yd:a:s:DS", options, NULL)) != -1){
|
||||
switch(c){
|
||||
case 'h':
|
||||
@ -141,7 +143,6 @@ int main(int argc, char **argv){
|
||||
}
|
||||
}
|
||||
}
|
||||
FILE *in;
|
||||
if(strcmp(path_in, "-") == 0){
|
||||
if(set_all){
|
||||
fputs("can't open stdin for input when -S is specified\n", stderr);
|
||||
@ -151,20 +152,20 @@ int main(int argc, char **argv){
|
||||
fputs("cannot modify stdin 'in-place'\n", stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
in = stdin;
|
||||
reader.file = stdin;
|
||||
}
|
||||
else
|
||||
in = fopen(path_in, "r");
|
||||
if(!in){
|
||||
reader.file = fopen(path_in, "r");
|
||||
if(!reader.file){
|
||||
perror("fopen");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
FILE *out = NULL;
|
||||
writer.file = NULL;
|
||||
if(inplace != NULL){
|
||||
path_out = static_cast<char*>(malloc(strlen(path_in) + strlen(inplace) + 1));
|
||||
if(path_out == NULL){
|
||||
fputs("failure to allocate memory\n", stderr);
|
||||
fclose(in);
|
||||
fclose(reader.file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
strcpy(path_out, path_in);
|
||||
@ -172,57 +173,53 @@ int main(int argc, char **argv){
|
||||
}
|
||||
if(path_out != NULL){
|
||||
if(strcmp(path_out, "-") == 0)
|
||||
out = stdout;
|
||||
writer.file = stdout;
|
||||
else{
|
||||
if(!overwrite && !inplace){
|
||||
if(access(path_out, F_OK) == 0){
|
||||
fprintf(stderr, "'%s' already exists (use -y to overwrite)\n", path_out);
|
||||
fclose(in);
|
||||
fclose(reader.file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
out = fopen(path_out, "w");
|
||||
if(!out){
|
||||
writer.file = fopen(path_out, "w");
|
||||
if(!writer.file){
|
||||
perror("fopen");
|
||||
fclose(in);
|
||||
fclose(reader.file);
|
||||
if(inplace)
|
||||
free(path_out);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
ogg_sync_state oy;
|
||||
ogg_stream_state os, enc;
|
||||
ogg_page og;
|
||||
ogg_packet op;
|
||||
ot::opus_tags tags;
|
||||
ogg_sync_init(&oy);
|
||||
ogg_sync_init(&reader.sync);
|
||||
char *buf;
|
||||
size_t len;
|
||||
const char *error = NULL;
|
||||
int packet_count = -1;
|
||||
while(error == NULL){
|
||||
// Read until we complete a page.
|
||||
if(ogg_sync_pageout(&oy, &og) != 1){
|
||||
if(feof(in))
|
||||
if(ogg_sync_pageout(&reader.sync, &reader.page) != 1){
|
||||
if(feof(reader.file))
|
||||
break;
|
||||
buf = ogg_sync_buffer(&oy, 65536);
|
||||
buf = ogg_sync_buffer(&reader.sync, 65536);
|
||||
if(buf == NULL){
|
||||
error = "ogg_sync_buffer: out of memory";
|
||||
break;
|
||||
}
|
||||
len = fread(buf, 1, 65536, in);
|
||||
if(ferror(in))
|
||||
len = fread(buf, 1, 65536, reader.file);
|
||||
if(ferror(reader.file))
|
||||
error = strerror(errno);
|
||||
ogg_sync_wrote(&oy, len);
|
||||
if(ogg_sync_check(&oy) != 0)
|
||||
ogg_sync_wrote(&reader.sync, len);
|
||||
if(ogg_sync_check(&reader.sync) != 0)
|
||||
error = "ogg_sync_check: internal error";
|
||||
continue;
|
||||
}
|
||||
// We got a page.
|
||||
// Short-circuit when the relevant packets have been read.
|
||||
if(packet_count >= 2 && out){
|
||||
if(ot::write_page(&og, out) == -1){
|
||||
if(packet_count >= 2 && writer.file){
|
||||
if(ot::write_page(&reader.page, writer.file) == -1){
|
||||
error = "write_page: fwrite error";
|
||||
break;
|
||||
}
|
||||
@ -230,33 +227,33 @@ int main(int argc, char **argv){
|
||||
}
|
||||
// Initialize the streams from the first page.
|
||||
if(packet_count == -1){
|
||||
if(ogg_stream_init(&os, ogg_page_serialno(&og)) == -1){
|
||||
if(ogg_stream_init(&reader.stream, ogg_page_serialno(&reader.page)) == -1){
|
||||
error = "ogg_stream_init: couldn't create a decoder";
|
||||
break;
|
||||
}
|
||||
if(out){
|
||||
if(ogg_stream_init(&enc, ogg_page_serialno(&og)) == -1){
|
||||
if(writer.file){
|
||||
if(ogg_stream_init(&writer.stream, ogg_page_serialno(&reader.page)) == -1){
|
||||
error = "ogg_stream_init: couldn't create an encoder";
|
||||
break;
|
||||
}
|
||||
}
|
||||
packet_count = 0;
|
||||
}
|
||||
if(ogg_stream_pagein(&os, &og) == -1){
|
||||
if(ogg_stream_pagein(&reader.stream, &reader.page) == -1){
|
||||
error = "ogg_stream_pagein: invalid page";
|
||||
break;
|
||||
}
|
||||
// Read all the packets.
|
||||
while(ogg_stream_packetout(&os, &op) == 1){
|
||||
while(ogg_stream_packetout(&reader.stream, &reader.packet) == 1){
|
||||
packet_count++;
|
||||
if(packet_count == 1){ // Identification header
|
||||
if(strncmp((char*) op.packet, "OpusHead", 8) != 0){
|
||||
if(strncmp((char*) reader.packet.packet, "OpusHead", 8) != 0){
|
||||
error = "opustags: invalid identification header";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(packet_count == 2){ // Comment header
|
||||
if(ot::parse_tags((char*) op.packet, op.bytes, &tags) != ot::parse_result::ok){
|
||||
if(ot::parse_tags((char*) reader.packet.packet, reader.packet.bytes, &tags) != ot::parse_result::ok){
|
||||
error = "opustags: invalid comment header";
|
||||
break;
|
||||
}
|
||||
@ -310,10 +307,10 @@ int main(int argc, char **argv){
|
||||
}
|
||||
for (size_t i = 0; i < count_add; ++i)
|
||||
tags.comments.emplace_back(to_add[i]);
|
||||
if(out){
|
||||
if(writer.file){
|
||||
ogg_packet packet;
|
||||
ot::render_tags(&tags, &packet);
|
||||
if(ogg_stream_packetin(&enc, &packet) == -1)
|
||||
if(ogg_stream_packetin(&writer.stream, &packet) == -1)
|
||||
error = "ogg_stream_packetin: internal error";
|
||||
free(packet.packet);
|
||||
}
|
||||
@ -321,13 +318,13 @@ int main(int argc, char **argv){
|
||||
print_tags(tags);
|
||||
if(raw_tags)
|
||||
free(raw_tags);
|
||||
if(error || !out)
|
||||
if(error || !writer.file)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if(out){
|
||||
if(ogg_stream_packetin(&enc, &op) == -1){
|
||||
if(writer.file){
|
||||
if(ogg_stream_packetin(&writer.stream, &reader.packet) == -1){
|
||||
error = "ogg_stream_packetin: internal error";
|
||||
break;
|
||||
}
|
||||
@ -335,33 +332,34 @@ int main(int argc, char **argv){
|
||||
}
|
||||
if(error != NULL)
|
||||
break;
|
||||
if(ogg_stream_check(&os) != 0)
|
||||
if(ogg_stream_check(&reader.stream) != 0)
|
||||
error = "ogg_stream_check: internal error (decoder)";
|
||||
// Write the page.
|
||||
if(out){
|
||||
ogg_stream_flush(&enc, &og);
|
||||
if(ot::write_page(&og, out) == -1)
|
||||
if(writer.file){
|
||||
ogg_page page;
|
||||
ogg_stream_flush(&writer.stream, &page);
|
||||
if(ot::write_page(&page, writer.file) == -1)
|
||||
error = "write_page: fwrite error";
|
||||
else if(ogg_stream_check(&enc) != 0)
|
||||
else if(ogg_stream_check(&writer.stream) != 0)
|
||||
error = "ogg_stream_check: internal error (encoder)";
|
||||
}
|
||||
else if(packet_count >= 2) // Read-only mode
|
||||
break;
|
||||
}
|
||||
if(packet_count >= 0){
|
||||
ogg_stream_clear(&os);
|
||||
if(out)
|
||||
ogg_stream_clear(&enc);
|
||||
ogg_stream_clear(&reader.stream);
|
||||
if(writer.file)
|
||||
ogg_stream_clear(&writer.stream);
|
||||
}
|
||||
ogg_sync_clear(&oy);
|
||||
fclose(in);
|
||||
if(out)
|
||||
fclose(out);
|
||||
ogg_sync_clear(&reader.sync);
|
||||
fclose(reader.file);
|
||||
if(writer.file)
|
||||
fclose(writer.file);
|
||||
if(!error && packet_count < 2)
|
||||
error = "opustags: invalid file";
|
||||
if(error){
|
||||
fprintf(stderr, "%s\n", error);
|
||||
if(path_out != NULL && out != stdout)
|
||||
if(path_out != NULL && writer.file != stdout)
|
||||
remove(path_out);
|
||||
if(inplace)
|
||||
free(path_out);
|
||||
|
@ -41,6 +41,63 @@ private:
|
||||
* \{
|
||||
*/
|
||||
|
||||
struct ogg_reader {
|
||||
/**
|
||||
* The file is our source of binary data. It is not integrated to libogg, so we need to
|
||||
* handle it ourselves.
|
||||
*
|
||||
* In the future, we should use an std::istream or something.
|
||||
*/
|
||||
FILE* file;
|
||||
/**
|
||||
* The sync layer gets binary data and yields a sequence of pages.
|
||||
*
|
||||
* A page contains packets that we can extract using the #stream state, but we only do that
|
||||
* for the headers. Once we got the OpusHead and OpusTags packets, all the following pages
|
||||
* are simply forwarded to the Ogg writer.
|
||||
*/
|
||||
ogg_sync_state sync;
|
||||
/**
|
||||
* Current page from the sync state.
|
||||
*
|
||||
* Its memory is managed by libogg, inside the sync state, and is valid until the next call
|
||||
* to ogg_sync_pageout.
|
||||
*/
|
||||
ogg_page page;
|
||||
/**
|
||||
* The stream layer receives pages and yields a sequence of packets.
|
||||
*
|
||||
* A single page may contain several packets, and a single packet may span on multiple
|
||||
* pages. The 2 packets we're interested in occupy whole pages though, in theory, but we'd
|
||||
* better ensure there are no extra packets anyway.
|
||||
*
|
||||
* After we've read OpusHead and OpusTags, we don't need the stream layer anymore.
|
||||
*/
|
||||
ogg_stream_state stream;
|
||||
/**
|
||||
* Current packet from the stream state.
|
||||
*
|
||||
* Its memory is managed by libogg, inside the stream state, and is valid until the next
|
||||
* call to ogg_stream_packetout.
|
||||
*/
|
||||
ogg_packet packet;
|
||||
};
|
||||
|
||||
struct ogg_writer {
|
||||
/**
|
||||
* The stream state receives packets and generates pages.
|
||||
*
|
||||
* We only need it to put the OpusHead and OpusTags packets into their own pages. The other
|
||||
* pages are naively written to the output stream.
|
||||
*/
|
||||
ogg_stream_state stream;
|
||||
/**
|
||||
* Output file. It should be opened in binary mode. We use it to write whole pages,
|
||||
* represented as a block of data and a length.
|
||||
*/
|
||||
FILE* file;
|
||||
};
|
||||
|
||||
int write_page(ogg_page *og, FILE *stream);
|
||||
|
||||
/** \} */
|
||||
|
Loading…
Reference in New Issue
Block a user