Deduce the cover’s MIME type from its signature

This commit is contained in:
Frédéric Mangano 2023-03-02 15:17:41 +09:00
parent 558160d5c3
commit 46cc78bfff
3 changed files with 25 additions and 5 deletions

View File

@ -182,10 +182,30 @@ std::optional<ot::picture> 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<std::pair<ot::byte_string_view, ot::byte_string_view>> 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());
}

View File

@ -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()

View File

@ -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');