This change also includes mapping the numeric ID3v1 codes to their
string equivalents before setting them into `MediaMetadata`. This
mapping already existed, but it was previously only used when parsing
MP4 `gnre` atoms.
Issue: androidx/media#1305
PiperOrigin-RevId: 629113480
When refreshing the media playlist, we should try to request via a url with [delivery directives](https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.2.5). However, when initially loading the media playlist or when the last loading with delivery directives encountered an error, we should not allow using those directives.
PiperOrigin-RevId: 629060177
This change adds a 'free-form' text genre to the existing
`bear-id3.mp3` sample, and adds a new sample with a 'numeric' genre ([9
is Metal](https://mutagen-specs.readthedocs.io/en/latest/id3/id3v2.4.0-frames.html#appendix-a-genre-list-from-id3v1)).
The samples were modified with:
```shell
$ id3edit --set-genre "Gorpcore" bear-id3.mp3
$ id3edit --set-genre "9" bear-id3-numeric-genre.mp3
```
Reading the numeric genre with `exiftool` shows the mapping back to
'Metal':
```
$ exiftool bear-id3-numeric-genre.mp3 | grep Genre
Genre : Metal
```
The playback dumps don't contain the genre because it's not yet
propagated to `MediaMetadata.genre`. That is done in a follow-up
change.
Issue: androidx/media#1305
PiperOrigin-RevId: 629043506
Add ctts box implementation to handle muxing B-frame videos.
Add method convertPresentationTimestampsToCompositionOffset to
provide sample offsets. Return empty ctts box in case of video
does not contain B-frame. Add ctts box to MoovStructure to handle
muxing the video containing B-frames.
PiperOrigin-RevId: 628346257
The first has a string genre, and various other values set, generated
from `sample.mp4` with the command below [1].
The second has a numeric genre, to test `gnre` atom parsing. This
parsing is currently broken, the fix is in a follow-up change. This
file was also generated from `sample.mp4` with the command below [2].
This change also includes `CommentFrame.text` in its `toString`
representation, otherwise there's no difference between e.g. different
levels of `ITUNESADVISORY` in the extractor dump files.
Issue: androidx/media#1305
-----
[1]
```shell
$ AP_PADDING="DEFAULT_PAD=0" \
AtomicParsley sample.mp4 \
--artist "Test Artist" \
--album "Test Album" \
--tracknum 2/12 \
--disk 2/3 \
--year 2024 \
--genre "Gorpcore" \
--bpm 120 \
--compilation true \
--advisory clean \
--gapless true \
--sortOrder artist "Sorting Artist" \
--sortOrder album "Sorting Album" \
--preventOptimizing \
-o sample_with_metadata.mp4
```
[2]
```shell
$ AP_PADDING="DEFAULT_PAD=0" \
AtomicParsley sample.mp4 \
--genre "Metal" \
--preventOptimizing \
-o sample_with_numeric_genre.mp4
```
PiperOrigin-RevId: 628345458
This change also adds some runtime redirection of calls to
`Dumper.add(String, Object)` to `add(String, byte[])` if
`value instanceof byte[]`. This simplifies the implementation of
`PlaybackOutput.dumpIfNotEqual` and seems like a reasonable amound of
'magic' for a test utility.
Issue: androidx/media#1305
PiperOrigin-RevId: 628119473
For LL-HLS, the non-primary playlists originally keep reloading even after the primary playlist has been changed to another one. The reason being this is to check if the hinted(#EXT-X-PRELOAD-HINT) resource has been published or removed. If removed, the loading of it should be canceled, per the suggestion in the HLS spec:
"A Client SHOULD cancel a request for a hinted resource if it is not present in a subsequent Playlist update, such as in an EXT-X-PRELOAD-HINT tag or as part of another tag such as EXT-X-PART. The client SHOULD ignore the results of such requests."
However, keeping the non-primary playlists reloading is not optimal. As a solution, we trigger the playlist reloading only when there is a preload chunk loading instead of every time after we have processed the playlist. Compared to the original implementation, this will save the requests of reloading non-primary playlist after we have taken action upon the preload chunk being published or removed.
Issue: androidx/media#1240
PiperOrigin-RevId: 626038032
After this CL, Transformer will throw if the clipping start and end
positions are the same because MediaMuxer doesn't support writing a
file with no samples. This should work once we default to the in-app
muxer.
Issue: androidx/media#1242
PiperOrigin-RevId: 624861950
Reduces flakiness of tests that assert on PCM audio. Tests now have to
clearly choose how they want the capturing muxer to handle pcm audio.
Note that the only dump files that have changed are those that deal
with PCM audio (.wav, sowt, twos, silence). Because of the continuous
nature of PCM, timestamps are not part of the dump.
PiperOrigin-RevId: 623155302
With this CL:
- The large timestamp offset added by ExoPlayer so that decoders
don't see any negative timestamps is removed before passing the
timestamp to the AudioGraph.
- Clipped media timestamps are offset by the clipping start position
before being passed to the AudioGraph.
- The offset added to the audio buffer timestamps is removed when
computing the audio sink position, to convert them back to the
timestamps passed by the player.
PiperOrigin-RevId: 619225990
Issue: androidx/media#725
Ideally the test to transmux a `ts` file (having 3 bytes NAL start code)
should go into `Mp4MuxerEndToEndTest`but `Mp4MuxerEndToEndTest` uses `MediaExtractor` and `MediaExtractor` is returning samples with `4 byte NAL start code` which will not exercise the newly added code path.
Hence the test is added in `TransformerWithInAppMuxerEndToEndTest` which
internally uses `media3 extractor` and feeds samples with `3 bytes NAL start code`
only.
PiperOrigin-RevId: 617985866
It's a bit arguable whether the `Subtitle` implementation supports
zero-duration events, since `getEventTimeCount` is documented as
effectively "the number of times the cues returns by `getCues(long)`
changes", and zero-duration events violate that. However, the current
`WebvttSubtitle` impl **does** produce zero-duration events, so it
seems safer to handle them gracefully here and then, as a possible
follow-up, fix the `WebvttSubtitle` impl (or remove it completely).
Issue: androidx/media#1177
#minor-release
PiperOrigin-RevId: 616095798
Renderers may be enabled for subsequent media items as soon as the current media item's renderer's isEnded() returns true. When a renderer is being enabled and the player is 'playing', that renderer is also started. When playing a mixed playlist of images and content with audio & video, the player may skip some image items because the early-starting of the audio renderer causes a clock update.
A solution is to only start the "early-enabled" renderers at the point of media transition and add a condition on DefaultMediaClock to use the standalone clock when reading-ahead and the renderer clock source is not in a started state.
Issue: androidx/media#1017
PiperOrigin-RevId: 613231227
This change aims to prioritise tracks that have a 'smooth enough for
video' frame rate, without always selecting the track with the highest
frame rate.
In particular MP4 files extracted from motion photos sometimes have two
HEVC tracks, with the higher-res one having a very low frame rate (not
intended for use in video playback). Before this change
`DefaultTrackSelector` would pick the low-fps, high-res track.
This change adds a somewhat arbitrary 10fps threshold for "smooth video
playback", meaning any tracks above this threshold are selected in
preference to tracks below it. Within the tracks above the threshold
other attributes are used to select the preferred track. We deliberately
don't pick the highest-fps track (over pixel count and bitrate), because
most users would prefer to see a 30fps 4k track over a 60fps 720p track.
This change also includes a test MP4 file, extracted from the existing
`jpeg/pixel-motion-photo-2-hevc-tracks.jpg` file by logging
`mp4StartPosition` in
[`MotionPhotoDescription.getMotionPhotoMetadata`](b930b40a16/libraries/extractor/src/main/java/androidx/media3/extractor/jpeg/MotionPhotoDescription.java (L123))
and then using `dd`:
```
mp4StartPosition=2603594
$ dd if=jpeg/pixel-motion-photo-2-hevc-tracks.jpg \
of=mp4/pixel-motion-photo-2-hevc-tracks.mp4 \
bs=1 \
skip=2603594
```
----
This solution is in addition to the `JpegMotionPhotoExtractor` change
made specifically for these two-track motion photos in
5266c71b3a.
We will keep both changes, even though that change is not strictly
needed after this one, because adding the role flags helps to
communicate more clearly the intended usage of these tracks. This
change to consider FPS seems like a generally useful improvement to
`DefaultTrackSelector`, since it seems unlikely we would prefer a 5fps
video track over a 30fps one.
Issue: androidx/media#1051
PiperOrigin-RevId: 611015459
This new test is for `ExoPlayer.setVideoEffects()`. It plays the
one-second-long video, applies an overlay that prints the video frame timestamp
onto the frame, captures the output frame and compares the captured output
frame with golden.
PiperOrigin-RevId: 610781590
These audio offload failure recovery tests model the DefaultAudioSink failing at audio track init and write operations in offload mode. Playback should recover and try again as DefaultAudioSink will disable offload mode.
PiperOrigin-RevId: 610372935
MediaCodec docs already allude to potentially mismatching H.264 level
between container and bitstream. Relax the initialization data check to
reflect this.
PiperOrigin-RevId: 608942322
There is no super test which covers whole MP4 structure.
In all the E2E test, it verified against `ExtractorOutput` which
would remain same if there are changes in the box structure.
PiperOrigin-RevId: 608310006
Our previous test video was difficult to use for testing our tone-mapping
algorithm, because it didn't have many different colors. Use a better
video for tone-map tests, by having one with more different colors
PiperOrigin-RevId: 606274843
The new API will take both `creation time` and `modification time`.
Till now, Mp4Muxer wrote `modification time` in both
`creation time` and `modification time` field, which was
incorrect.
PiperOrigin-RevId: 605590623
Previously, we missed the BT2020->BT709 color-space conversion.
A user-visible impact of this is that red and green channels used to be
undersaturated, but now are more correctly saturated.
PiperOrigin-RevId: 605411926
The track id must be the index in the list of published tracks
as it's used as such elsewhere. This is currently not true if we
skip an empty track as all subsequent tracks get a wrong or even
invalid id.
#minor-release
PiperOrigin-RevId: 604929178
Earlier implementation compared the whole file against a golden
data. The new implementation compares only the metadata being tested.
This will avoid updating the golden data when any unrelated change
(unrelated to scenario being tested) is made.
Added a separate test to compare the whole output file against a golden data.
PiperOrigin-RevId: 604692985
The current implementation of `JpegMotionPhotoExtractor.sniff` returns
`true` for any image with Exif data (not just motion photos). Improving
this is tracked by b/324033919. In the meantime, when we 'extract' a
non-motion photo with `JpegMotionPhotoExtractor`, the result is
currently a single empty image track and no video track (since there's
no video, since this isn't a motion photo). This 'empty' image track
is usually used to transmit metadata about the video parts of the
image file (in the form of `MotionPhotoMetadata`), but this metadata is
also (understandably) absent for non-motion photos. Therefore there's
no need to emit this image track at all, and it's clearer to emit no
tracks at all when extracting a non-motion photo using
`JpegMotionPhotoExtractor`.
This change also removes a `TODO` that is misplaced, since there's no
image bytes being emitted here (and never was).
PiperOrigin-RevId: 604688053
These are often the same for image tracks, since we usually drop the
whole image file (both the container and actual encoded image bytes)
into a single sample, but there are cases where we emit a track with
`containerMimeType=image/jpeg` but **no** samples (from
`JpegMotionPhotoExtractor`, to carry some metadata about the image +
video byte offsets).
It's therefore more correct to implement the `supportsFormat` check
based on `sampleMimeType`, so that these 'empty' image tracks are not
considered 'supported' by `ImageRenderer`.
#minor-release
PiperOrigin-RevId: 604672331
Before supporting transmuxing when both no op effects and regular rotations are set, move setting the muxerWrapper rotation out of shouldTranscodeVideo() to ensure the muxerWrapper rotation is only set at the appropriate times.
This cl also ensures the state between the muxerWrapper and the list of video effects is consistent by clearing the list of videoEffects in trim optimization. If trim optimisation is being applied, then EditedMediItem.effects.videoEffects only contains no-op effects or regular rotations that get be applied in the muxer wrapper. Therefore, we should clear the list of video effects to ensure that no effect gets applied twice.
PiperOrigin-RevId: 604292052
The new test introduced in 45bd5c6f0a is flaky because we only
wait until the media is fully buffered. However, we can't fully
control how much of this data is initially read by the Robolectric
codec and thus the output dump files (containing these codec
interactions) are flaky.
This can be fixed by fully playing the media once and then seeking
back instead.
#minor-release
PiperOrigin-RevId: 603324068
If seeking between last image sample and end of the file where the current stream is not final, then EoS sample will not be provided to `ImageRenderer`. ImageRenderer must still produce the last image sample.
PiperOrigin-RevId: 603312090
The implementation of fragmented MP4 caused a regression where muxer
started writing empty tracks even for non fragmented MP4.
PiperOrigin-RevId: 603091348
Previously, input assets had to be all SDR or all HDR.
After this CL, if tone-mapping is requested, HDR and SDR may mix in any order. If tone-mapping is not requested, SDR may precede HDR, but not vice versa, until SDR to HDR tone-mapping is implemented
Some changes to accomplish this include:
1. Inputting the decoded format's color to VideoFrameProcessor.registerInputStream
for each stream.
2. Calculating the estimated decoded format's color for each stream, by estimating
it based on MediaCodec tone-mapping.
PiperOrigin-RevId: 602747837
As per MP4 spec ISO 14496-12: 8.7.5 Chunk Offset Box, Both "stco" and
"co64" can be used to store chunk offsets. While "stco" supports 32-bit
offsets, "co64" supports 64-bit offsets.
In non fragmented MP4, the mdat box can be extremely large, hence muxer
uses "co64" box.
But for fragmented MP4, muxer does not write any data in this chunk offset
box (present in "moov" box) because all sample related info is present in
"moof" box.
Technically, "co64" box should also work in fragmented MP4because
its empty only but QuickTime player fails to play video if "co64"
box is present in fragmented MP4 output file.
Testing: Verified that QuickTime player does not play video when "co64"
box is present but is able to play when "stco" box is present.
#minor-release
PiperOrigin-RevId: 601147046
This fix makes output playable on VLC player.
The output does not play on QuickTime player which is being fixed in
a separate CL.
#minor-release
PiperOrigin-RevId: 601118813
Since mdat box can be huge so there is a provision to use 64 bit size field.
In case of fragmented MP4, individual fragments should not have large mdat box
so a 32 bit size field should be sufficient.
PiperOrigin-RevId: 599219041
Imported from GitHub PR https://github.com/androidx/media/pull/275
Added below mentioned features.
- Support for extracting DTS LBR(DTS Express) and DTS UHD Profile 2(DTS:X) descriptor ID from PSI PMT
- The DTSReader class is updated for extracting a DTS LBR.
- Newly added DtsUhdReader class for extracting DTS UHD frame.
- The DTSUtil class is updated to parse the DTS LBR or DTS UHD frame and report the format information.
Feature request for ExoPlayer: https://github.com/google/ExoPlayer/issues/11075
Merge 21efa0810db31550d6b215639f9ca2af6a32139a into 104cfc322c
COPYBARA_INTEGRATE_REVIEW=https://github.com/androidx/media/pull/275 from rahulnmohan:dts-mpeg2ts-update 21efa0810db31550d6b215639f9ca2af6a32139a
PiperOrigin-RevId: 598854998
MatroskaExtractor will no longer be wrapped in SubtitleTranscodingExtractor, but instead use SubtitleTranscodingExtractorOutput under the hood.
FLAG_EMIT_RAW_SUBTITLE_DATA flag will be used to toggle between subtitle parsing during extraction (before the sample queue) or during decoding (after the sample queue).
The new extractor dump files generated by `MatroskaExtractorTest` now follow the new parsing logic and hence have mimeType as `x-media3-cues`.
PiperOrigin-RevId: 598616231
The seek table in a Xing/Info header is very imprecise (max resolution
of 255 to describe each of 100 byte positions in the file). Seeking
using a constant bitrate assumption is more accurate, especially for
longer files (which exacerbates the imprecision of the Info header).
VBR files should contain an Xing header, while an Info header is
identical but indicates the file is CBR.
Issue: androidx/media#878
PiperOrigin-RevId: 597827891
WebvttExtractor will no longer be wrapped in SubtitleTranscodingExtractor, but instead use SubtitleTranscodingExtractorOutput under the hood.
A new constructor will take a boolean parameter to toggle between subtitle parsing during extraction (before the sample queue) or during decoding (after the sample queue).
PiperOrigin-RevId: 597604942
An audio file can only play sound between two PCM samples (the 'start'
and 'end' of section of a wave form). Therefore when calculating
duration from a count of PCM samples we need to subtract one first (the
'end' sample which has no duration of its own).
This only changes durations by one PCM sample (21us - 22us for 44.1kHz sample
rate).
PiperOrigin-RevId: 596990306
The extractor knows the PCM encoding of the losslessly
encoded data in the samples and should set it in the
Format to allow downstream components to use this information.
PiperOrigin-RevId: 596974863
This file is CBR encoded with LAME, so it has an `Info` header (the CBR
equivalent to `Xing`).
A follow-up change will use this file in `Mp3ExtractorTest`.
Issue: androidx/media#878
PiperOrigin-RevId: 595938327
Changes includes;
1. Public API to enable fMP4 and to pass fragment duration.
2. Added `FragmentedMp4Writer`.
3. Added logic to create fragments based on given fragment duration.
4. Write "moov" box only once in the beginning.
3. Add all the required boxes for current implementation.
4. Unit tests for all the new boxes.
5. E2E test for generating fMP4.
Note: The output file is un seek-able with this first implementation.
PiperOrigin-RevId: 594426486
Mp4Muxer does not support out of order B-frames. Currently it
silently writes out of order B-frames, producing an invalid file (with
negative sample durations).
Although `Mp4Extractor` is somehow able to process this invalid file and
`Exoplayer` is able to play it but that is unexpected.
The `sample.mp4` test file contains B frames. Other test files does not
contain `H264 video + AAC audio` format hence created a new test file by
running `sample.mp4` via `Transformer` after applying some effects.
PiperOrigin-RevId: 594016144
HLS distinguishes between 'subtitles' (WebVTT or TTML distributed in
separate files with their own playlist) and 'captions' (CEA-608 or 708,
distributed muxed into the video file).
The format transformation added in 7b762642db
only applies to subtitles and not captions. This change makes the same
transformation for caption formats.
This resolves an error like:
```
SampleQueueMappingException: Unable to bind a sample queue to TrackGroup with MIME type application/cea-608.
```
Also add two playback tests for HLS CEA-608, one that parses during
decoding (old way) and one during extraction (new way). Adding these
tests is what alerted me to this issue.
PiperOrigin-RevId: 592571284
This was generated by combining the existing `ts/bbb_2500ms.ts` test
asset and a temporary `.srt` file using
https://cloud.google.com/transcoder/docs/how-to/captions-and-subtitles
This doesn't directly reproduce the problem fixed by
7ca26f898d,
because the CEA-608 subs are structured differently to the stream I
discovered the problem with (from Issue: androidx/media#887). However this test
does fail if that fix is reverted after
486230fbd7.
I'm also not able to repro the character duplication reported in
Issue: androidx/media#887 by just changing the manifest in this CL. I'm not yet
sure on the exact differences between the stream provided on GitHub
and this stream.
This stream does provide some regression protection, because it
currently fails with 'new' subtitle parsing
(`DashMediaSource.Factory.experimentalParseSubtitlesDuringExtraction(true)`),
though I'm not sure on the exact reason for that yet.
PiperOrigin-RevId: 592476328
This is because currently
1. Player sets a surfaceView to render to
2. Player intializes the renderer
3. MCVR initializes the VideoSinkProvider, by extension VideoGraph
But when 1 happens, MCVR doesn't set the surfaceView on the VideoGraph because
it's not initialized. Consequently after VideoGraph is initialized, it doesn't
have a surface to render to, and thus dropping the first a few frames.
Also adds a test for first frame to verify the correct first frame is rendered.
PiperOrigin-RevId: 591228174
The MP4 data in JPEG motion photos can contain multiple `video/hevc` tracks, but only the first is at a playable frame rate while the others are low-fps, high-res tracks designed for specific use-cases (not direct video playback).
ExoPlayer currently selects the unplayable track by default, because it
has a higher resolution. This change introduces a flag to
`Mp4Extractor` that results in the first video track being marked as
`ROLE_FLAG_MAIN`, and all subsequent video tracks `ROLE_FLAG_ALTERNATE`
- this then results in the playable lower-res track being selected by
default.
PiperOrigin-RevId: 589832072