diff --git a/src/opus.cc b/src/opus.cc index eb62e82..1c02535 100644 --- a/src/opus.cc +++ b/src/opus.cc @@ -182,10 +182,30 @@ std::optional ot::extract_cover(const ot::opus_tags& tags) return picture(decode_base64(cover_value)); } +/** + * Detect the MIME type of the given data block by checking the first bytes. Only the most common + * image formats are currently supported. Using magic(5) would give better results but that level of + * exhaustiveness is probably not necessary. + */ +static ot::byte_string_view detect_mime_type(ot::byte_string_view data) +{ + static std::initializer_list> magic_numbers = { + { "\xff\xd8\xff"_bsv, "image/jpeg"_bsv }, + { "\x89PNG"_bsv, "image/png"_bsv }, + { "GIF8"_bsv, "image/gif"_bsv }, + }; + for (auto [magic, mime] : magic_numbers) { + if (data.starts_with(magic)) + return mime; + } + fputs("warning: Could not identify the MIME type of the picture; defaulting to application/octet-stream.\n", stderr); + return "application/octet-stream"_bsv; +} + std::string ot::make_cover(ot::byte_string_view picture_data) { picture pic; - pic.mime_type = "application/octet-stream"_bsv; + pic.mime_type = detect_mime_type(picture_data); pic.picture_data = picture_data; return "METADATA_BLOCK_PICTURE=" + encode_base64(pic.serialize()); } diff --git a/t/opus.cc b/t/opus.cc index e142e39..8188530 100644 --- a/t/opus.cc +++ b/t/opus.cc @@ -169,16 +169,16 @@ static void make_cover() { ot::byte_string_view picture_block = ""_bsv "\x00\x00\x00\x03" // Picture type 3. - "\x00\x00\x00\x18" "application/octet-stream" // MIME type. + "\x00\x00\x00\x09" "image/png" // MIME type. "\x00\x00\x00\x00" "" // Description. "\x00\x00\x00\x00" // Width. "\x00\x00\x00\x00" // Height. "\x00\x00\x00\x00" // Color depth. "\x00\x00\x00\x00" // Palette size. - "\x00\x00\x00\x0C" "Picture data"; + "\x00\x00\x00\x11" "\x89PNG Picture data"; std::string expected = "METADATA_BLOCK_PICTURE=" + ot::encode_base64(picture_block); - is(ot::make_cover("Picture data"_bsv), expected, "build the picture tag"); + is(ot::make_cover("\x89PNG Picture data"_bsv), expected, "build the picture tag"); } int main() diff --git a/t/opustags.t b/t/opustags.t index 4915096..a9545c3 100755 --- a/t/opustags.t +++ b/t/opustags.t @@ -333,7 +333,7 @@ unlink('out.opus'); is_deeply(opustags(qw(-D --set-cover pixel.png gobble.opus -o out.opus), ), ['', '', 0], 'set the cover'); is_deeply(opustags(qw(--output-cover out.png out.opus), ), [<<'END_OUT', '', 0], 'extract the cover'); -METADATA_BLOCK_PICTURE=AAAAAwAAABhhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEWJUE5HDQoaCgAAAA1JSERSAAAAAQAAAAEIAgAAAJB3U94AAAAMSURBVAjXY/j//z8ABf4C/tzMWecAAAAASUVORK5CYII= +METADATA_BLOCK_PICTURE=AAAAAwAAAAlpbWFnZS9wbmcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEWJUE5HDQoaCgAAAA1JSERSAAAAAQAAAAEIAgAAAJB3U94AAAAMSURBVAjXY/j//z8ABf4C/tzMWecAAAAASUVORK5CYII= END_OUT is(md5('out.png'), md5('pixel.png'), 'the extracted cover is identical to the one set'); unlink('out.opus');