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>
5051 * @author tobigr
5152 */
5253public 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 = 61_440 ;
6990
7091 private boolean done = false ;
7192 private boolean parsed = false ;
@@ -330,12 +351,13 @@ private int makePacketHeader(final long granPos, @NonNull final ByteBuffer buffe
330351 * @ImplNote See <a href="https://datatracker.ietf.org/doc/html/rfc7845.html#section-5.2">
331352 * RFC7845 5.2</a>
332353 *
333- * @return
354+ * @return the metadata header as a byte array, or null if the codec is not supported
355+ * for metadata generation
334356 */
335357 @ Nullable
336358 private byte [] makeMetadata () {
337359 if (DEBUG ) {
338- Log .d ("OggFromWebMWriter" , "Downloading media with codec ID " + webmTrack .codecId );
360+ Log .d (TAG , "Downloading media with codec ID " + webmTrack .codecId );
339361 }
340362
341363 if ("A_OPUS" .equals (webmTrack .codecId )) {
@@ -355,18 +377,17 @@ private byte[] makeMetadata() {
355377 }
356378
357379 if (DEBUG ) {
358- Log .d ("OggFromWebMWriter" , "Creating metadata header with this data:" );
359- metadata .forEach (p -> Log .d ("OggFromWebMWriter" , p .first + "=" + p .second ));
380+ Log .d (TAG , "Creating metadata header with this data:" );
381+ metadata .forEach (p -> Log .d (TAG , p .first + "=" + p .second ));
360382 }
361383
362384 return makeOpusTagsHeader (metadata );
363385 } 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- */
386+ // See https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-620004.2.1
387+ // for the Vorbis comment header format
388+ // TODO: add Vorbis metadata: same as Opus, but with the Vorbis comment header format
368389 return new byte []{
369- 0x03 , // ???
390+ 0x03 , // packet type for Vorbis comment header
370391 0x76 , 0x6f , 0x72 , 0x62 , 0x69 , 0x73 , // "vorbis" binary string
371392 0x00 , 0x00 , 0x00 , 0x00 , // writing application string size (not present)
372393 0x00 , 0x00 , 0x00 , 0x00 // additional tags count (zero means no tags)
@@ -401,23 +422,31 @@ private static byte[] makeOpusMetadataTag(final Pair<String, String> pair) {
401422 * containing the provided bitmap as cover art.
402423 *
403424 * <p>
404- * One could also use the COVERART tag instead, but it is not as widely supported
405- * as METADATA_BLOCK_PICTURE.
425+ * The {@code METADATA_BLOCK_PICTURE} tag is defined in the FLAC specification (RFC 9639)
426+ * and is supported by Opus metadata headers.
427+ * One could also use the {@code COVERART} tag instead, but it is not as widely supported
428+ * as {@code METADATA_BLOCK_PICTURE}.
406429 * </p>
407430 *
431+ * @see <a href="https://www.rfc-editor.org/rfc/rfc9639.html#section-8.8">
432+ * RFC 9639 8.8 Picture</a>
433+ *
408434 * @param bitmap The bitmap to use as cover art
409435 * @return The key-value pair representing the tag
410436 */
411437 private static Pair <String , String > makeOpusPictureTag (final Bitmap bitmap ) {
412438 // FLAC picture block format (big-endian):
413439 // uint32 picture_type
414- // uint32 mime_length, mime_string
415- // uint32 desc_length, desc_string
440+ // uint32 mime_length,
441+ // mime_string
442+ // uint32 desc_length,
443+ // desc_string
416444 // uint32 width
417445 // uint32 height
418446 // uint32 color_depth
419447 // uint32 colors_indexed
420- // uint32 data_length, data_bytes
448+ // uint32 data_length,
449+ // data_bytes
421450
422451 final ByteArrayOutputStream baos = new ByteArrayOutputStream ();
423452 bitmap .compress (Bitmap .CompressFormat .JPEG , 100 , baos );
@@ -428,7 +457,8 @@ private static Pair<String, String> makeOpusPictureTag(final Bitmap bitmap) {
428457 // fixed ints + mime + desc
429458 final int headerSize = 4 * 8 + mimeBytes .length + descBytes .length ;
430459 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
460+ // See https://www.rfc-editor.org/rfc/rfc9639.html#table-13 for the complete list
461+ // of picture types
432462 // TODO: allow specifying other picture types, i.e. cover (front) for music albums;
433463 // but this info needs to be provided by the extractor first.
434464 buf .putInt (3 ); // picture type: 0 = Other, 2 = Cover (front)
@@ -476,6 +506,7 @@ private static byte[] makeOpusTagsHeader(final List<Pair<String, String>> keyVal
476506
477507 final var head = ByteBuffer .allocate (byteCount );
478508 head .order (ByteOrder .LITTLE_ENDIAN );
509+ // See RFC7845 5.2: https://datatracker.ietf.org/doc/html/rfc7845.html#section-5.2
479510 head .put (new byte []{
480511 0x4F , 0x70 , 0x75 , 0x73 , 0x54 , 0x61 , 0x67 , 0x73 , // "OpusTags" binary string
481512 0x00 , 0x00 , 0x00 , 0x00 , // vendor (aka. Encoder) string of length 0
@@ -559,9 +590,10 @@ private boolean addPacketSegment(final SimpleBlock block) {
559590 }
560591
561592 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 ));
593+ if (size > OPUS_MAX_PACKETS_PAGE_SIZE ) {
594+ throw new UnsupportedOperationException (String .format (
595+ "page size is %s but cannot be larger than %s" ,
596+ size , OPUS_MAX_PACKETS_PAGE_SIZE ));
565597 }
566598
567599 int available = (segmentTable .length - segmentTableSize ) * OGG_SEGMENT_SIZE ;
0 commit comments