Skip to content

Commit c593329

Browse files
committed
More docs and constants
1 parent 3a17881 commit c593329

File tree

1 file changed

+70
-33
lines changed

1 file changed

+70
-33
lines changed

app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* The following specifications are used for the implementation:
3939
* </p>
4040
* <ul>
41+
* <li>FLAC: <a href="https://www.rfc-editor.org/rfc/rfc9639">RFC 9639</a></li>
4142
* <li>Opus: All specs can be found at <a href="https://opus-codec.org/docs/">
4243
* https://opus-codec.org/docs/</a>.
4344
* <a href="https://datatracker.ietf.org/doc/html/rfc7845.html">RFC7845</a>
@@ -50,10 +51,24 @@
5051
* @author tobigr
5152
*/
5253
public class OggFromWebMWriter implements Closeable {
54+
private static final String TAG = OggFromWebMWriter.class.getSimpleName();
55+
56+
/**
57+
* No flags set.
58+
*/
5359
private static final byte FLAG_UNSET = 0x00;
54-
//private static final byte FLAG_CONTINUED = 0x01;
60+
/**
61+
* The packet is continued from previous the previous page.
62+
*/
63+
private static final byte FLAG_CONTINUED = 0x01;
64+
/**
65+
* BOS (beginning of stream).
66+
*/
5567
private static final byte FLAG_FIRST = 0x02;
56-
private static final byte FLAG_LAST = 0x04;
68+
/**
69+
* EOS (end of stream).
70+
*/
71+
private static final byte FLAG_LAST = 0x04;;
5772

5873
private static final byte HEADER_CHECKSUM_OFFSET = 22;
5974
private static final byte HEADER_SIZE = 27;
@@ -66,6 +81,12 @@ public class OggFromWebMWriter implements Closeable {
6681
*/
6782
private static final int OGG_SEGMENT_SIZE = 255;
6883

84+
/**
85+
* The maximum size of the Opus packet in bytes, to be included in the Ogg page.
86+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc7845.html#section-6">
87+
* RFC7845 6. Packet Size Limits</a>
88+
*/
89+
private static final int OPUS_MAX_PACKETS_PAGE_SIZE = 65_025;
6990

7091
private boolean done = false;
7192
private boolean parsed = false;
@@ -234,7 +255,7 @@ public void build() throws IOException {
234255
}
235256

236257
/* step 3: create packet with metadata */
237-
final byte[] buffer = makeMetadata();
258+
final byte[] buffer = makeCommentHeader();
238259
if (buffer != null) {
239260
addPacketSegment(buffer.length);
240261
makePacketHeader(0x00, header, buffer);
@@ -325,17 +346,21 @@ private int makePacketHeader(final long granPos, @NonNull final ByteBuffer buffe
325346
/**
326347
* Creates the metadata header for the selected codec (Opus or Vorbis).
327348
*
328-
* Opus metadata can contain
349+
* @see <a href="https://datatracker.ietf.org/doc/html/rfc7845.html#section-5.2">
350+
* RFC7845 5.2. Comment Header</a> for OPUS metadata header format
351+
* @see <a href="https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2">
352+
* Vorbis I 4.2. Header decode and decode setup</a> and
353+
* <a href="https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-820005">
354+
* Vorbis 5. comment field and header specification</a>
355+
* for VORBIS metadata header format
329356
*
330-
* @ImplNote See <a href="https://datatracker.ietf.org/doc/html/rfc7845.html#section-5.2">
331-
* RFC7845 5.2</a>
332-
*
333-
* @return
357+
* @return the metadata header as a byte array, or null if the codec is not supported
358+
* for metadata generation
334359
*/
335360
@Nullable
336-
private byte[] makeMetadata() {
361+
private byte[] makeCommentHeader() {
337362
if (DEBUG) {
338-
Log.d("OggFromWebMWriter", "Downloading media with codec ID " + webmTrack.codecId);
363+
Log.d(TAG, "Downloading media with codec ID " + webmTrack.codecId);
339364
}
340365

341366
if ("A_OPUS".equals(webmTrack.codecId)) {
@@ -350,23 +375,22 @@ private byte[] makeMetadata() {
350375
.getLocalDateTime()
351376
.format(DateTimeFormatter.ISO_DATE)));
352377
if (thumbnail != null) {
353-
metadata.add(makeOpusPictureTag(thumbnail));
378+
metadata.add(makeFlacPictureTag(thumbnail));
354379
}
355380
}
356381

357382
if (DEBUG) {
358-
Log.d("OggFromWebMWriter", "Creating metadata header with this data:");
359-
metadata.forEach(p -> Log.d("OggFromWebMWriter", p.first + "=" + p.second));
383+
Log.d(TAG, "Creating metadata header with this data:");
384+
metadata.forEach(p -> Log.d(TAG, p.first + "=" + p.second));
360385
}
361386

362387
return makeOpusTagsHeader(metadata);
363388
} else if ("A_VORBIS".equals(webmTrack.codecId)) {
364-
/**
365-
* See <a href="https://datatracker.ietf.org/doc/html/rfc7845.html#section-5.2">
366-
* RFC7845 5.2</a>
367-
*/
389+
// See https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2
390+
// for the Vorbis comment header format
391+
// TODO: add Vorbis metadata: same as Opus, but with the Vorbis comment header format
368392
return new byte[]{
369-
0x03, // ???
393+
0x03, // packet type for Vorbis comment header
370394
0x76, 0x6f, 0x72, 0x62, 0x69, 0x73, // "vorbis" binary string
371395
0x00, 0x00, 0x00, 0x00, // writing application string size (not present)
372396
0x00, 0x00, 0x00, 0x00 // additional tags count (zero means no tags)
@@ -397,27 +421,37 @@ private static byte[] makeOpusMetadataTag(final Pair<String, String> pair) {
397421
}
398422

399423
/**
400-
* Adds the {@code METADATA_BLOCK_PICTURE} tag to the Opus metadata,
401-
* containing the provided bitmap as cover art.
424+
* Generates a FLAC picture block for the provided bitmap.
402425
*
403426
* <p>
404-
* One could also use the COVERART tag instead, but it is not as widely supported
405-
* as METADATA_BLOCK_PICTURE.
427+
* The {@code METADATA_BLOCK_PICTURE} tag is defined in the FLAC specification (RFC 9639)
428+
* and is supported by Opus and Vorbis metadata headers.
429+
* The picture block contains the image data which is converted to JPEG
430+
* and associated metadata such as picture type, dimensions, and color depth.
431+
* The image data is Base64-encoded as per specification.
406432
* </p>
407433
*
408-
* @param bitmap The bitmap to use as cover art
409-
* @return The key-value pair representing the tag
434+
* @see <a href="https://www.rfc-editor.org/rfc/rfc9639.html#section-8.8">
435+
* RFC 9639 8.8 Picture</a>
436+
*
437+
* @param bitmap The bitmap to use for the picture block
438+
* @return The key-value pair representing the tag.
439+
* The key is {@code METADATA_BLOCK_PICTURE}
440+
* and the value is the Base64-encoded FLAC picture block.
410441
*/
411-
private static Pair<String, String> makeOpusPictureTag(final Bitmap bitmap) {
442+
private static Pair<String, String> makeFlacPictureTag(final Bitmap bitmap) {
412443
// FLAC picture block format (big-endian):
413444
// uint32 picture_type
414-
// uint32 mime_length, mime_string
415-
// uint32 desc_length, desc_string
445+
// uint32 mime_length,
446+
// mime_string
447+
// uint32 desc_length,
448+
// desc_string
416449
// uint32 width
417450
// uint32 height
418451
// uint32 color_depth
419452
// uint32 colors_indexed
420-
// uint32 data_length, data_bytes
453+
// uint32 data_length,
454+
// data_bytes
421455

422456
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
423457
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
@@ -428,7 +462,8 @@ private static Pair<String, String> makeOpusPictureTag(final Bitmap bitmap) {
428462
// fixed ints + mime + desc
429463
final int headerSize = 4 * 8 + mimeBytes.length + descBytes.length;
430464
final ByteBuffer buf = ByteBuffer.allocate(headerSize + imageData.length);
431-
// See https://id3.org/id3v2.3.0#Attached_picture for a full list of picture types
465+
// See https://www.rfc-editor.org/rfc/rfc9639.html#table-13 for the complete list
466+
// of picture types
432467
// TODO: allow specifying other picture types, i.e. cover (front) for music albums;
433468
// but this info needs to be provided by the extractor first.
434469
buf.putInt(3); // picture type: 0 = Other, 2 = Cover (front)
@@ -442,7 +477,7 @@ private static Pair<String, String> makeOpusPictureTag(final Bitmap bitmap) {
442477
buf.putInt(bitmap.getWidth());
443478
buf.putInt(bitmap.getHeight());
444479
buf.putInt(24); // color depth for JPEG and PNG is usually 24 bits
445-
buf.putInt(0); // colors indexed (0 for non-indexed images, i.e. JPEG, PNG)
480+
buf.putInt(0); // colors indexed (0 for non-indexed images like JPEG)
446481
buf.putInt(imageData.length);
447482
buf.put(imageData);
448483
final String b64 = Base64.getEncoder().encodeToString(buf.array());
@@ -476,6 +511,7 @@ private static byte[] makeOpusTagsHeader(final List<Pair<String, String>> keyVal
476511

477512
final var head = ByteBuffer.allocate(byteCount);
478513
head.order(ByteOrder.LITTLE_ENDIAN);
514+
// See RFC7845 5.2: https://datatracker.ietf.org/doc/html/rfc7845.html#section-5.2
479515
head.put(new byte[]{
480516
0x4F, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73, // "OpusTags" binary string
481517
0x00, 0x00, 0x00, 0x00, // vendor (aka. Encoder) string of length 0
@@ -559,9 +595,10 @@ private boolean addPacketSegment(final SimpleBlock block) {
559595
}
560596

561597
private boolean addPacketSegment(final int size) {
562-
if (size > 65025) {
563-
throw new UnsupportedOperationException(
564-
String.format("page size is %s but cannot be larger than 65025", size));
598+
if (size > OPUS_MAX_PACKETS_PAGE_SIZE) {
599+
throw new UnsupportedOperationException(String.format(
600+
"page size is %s but cannot be larger than %s",
601+
size, OPUS_MAX_PACKETS_PAGE_SIZE));
565602
}
566603

567604
int available = (segmentTable.length - segmentTableSize) * OGG_SEGMENT_SIZE;

0 commit comments

Comments
 (0)