CNA-BUG-139: MediaPlayer::Play(Song*) records Duration and PlayCount on the caller's Song instead of the queued copy, and Stop never resets that PlayCount
Evidence basis: source-verified at the pinned commit; tests exist (not executed for this page). Claims on this page were checked by reading the CNA source at commit 009d40f5; unless a sentence says otherwise, nothing here was built or executed. Nothing on this page was executed unless the Evidence section says so.
Play(Song*) hands the caller's Song to PlaySong, so the mixer duration and a PlayCount increment land on the library or content Song while the queue's active copy keeps zero, contrary to MediaLibrary.cpp's comment.
- Identifier
CNA-BUG-139- Category
- Bug
- Subsystem
- Audio & media
- Status
- Open
- Verified against
- CNA
009d40f5(009d40f5dd085c4e674d3479675fac84b12b3e0a) - Severity
- Low (a triage suggestion, not a project priority)
- Evidence basis
- Source-verified: read at TARGET, not executed
- Tests touching this area
- Yes: see Current tests
- Affected contract
- Song::getDurationProperty() and Song::getPlayCountProperty() of the caller's Song and of MediaQueue::getActiveSongProperty() after MediaPlayer::Play(Song*)
Expected behaviour
CNA copies the Song into the queue on purpose (FNA's comment: XNA “duplicates the Song object and then assigns a bunch of stuff to it at Play time”), and MediaLibrary.cpp (BuildFromRoots) relies on it: “MediaPlayer::Play() operates on a duplicate … not the library's original Song instance”. The active song in the queue should therefore carry what playback learns, the same way for every Play overload, and Stop should reset the counts it maintains.
Actual behaviour at TARGET
In MediaPlayer.cpp, Play(Song*) calls PlaySong(song) with the caller's object, not the copy it just queued. In a build with a mixer PlaySong overwrites that Song's Duration with the mixer-reported length and increments its PlayCount. Play(const SongCollection&, index) and NextSong pass the queue's copy instead. Consequences:
- after
Play(&song)the queue's active Song reports a zero Duration, afterPlay(collection)it reports the mixer value; - a library Song's PlayCount grows by one on every
Play(Song*)and is never reset, becauseStopresets PlayCount only on the queue's copies; - the
MediaLibrary.cppcomment is wrong for this overload.
FNA also passes the caller's Song to PlaySong (so its Duration lands there too) but never increments PlayCount; XNA's queue Song is a new wrapper around the same native media item, whose duration and play count are read from native metadata, so both objects agree.
Source locations
modules/media/src/Xna/MediaPlayer.cpp— MediaPlayer::Play(Song*), PlaySong, Stopmodules/media/src/Xna/MediaLibrary.cpp— MediaLibrary::BuildFromRoots: the songs-block comment on MediaPlayer::Play
Evidence
Checked by reading at 009d40f5; not executed. FNA read at FNA-XNA/FNA b3551247 (MediaPlayer.cs).
Focused reproduction
Illustrative; not compiled or run (SDL3 audio).
Song s("Content/theme.ogg", "theme"); // or a MediaLibrary song
MediaPlayer::Play(&s);
s.getPlayCountProperty(); // 1, grows on every Play(&s)
MediaPlayer::getQueueProperty().getActiveSongProperty()->getDurationProperty(); // zero
MediaPlayer::Stop(); // s keeps its PlayCount
Current tests
PlayEnqueuesADuplicateNotTheOriginalInstance in MediaPlayerTests.cpp checks that setting the original's PlayCount does not change the copy, the opposite direction; nothing checks which object PlaySong updates.
Regression test
With SDL3 audio and the dummy driver: after Play(&song) the queue's active Song has a non-zero Duration and PlayCount 1 while the caller's Song is unchanged (or, if the caller's Song is meant to be updated, document it and reset it in Stop).
Blast radius
Games that display a song's length or play count from the queue's active Song, and MediaLibrary songs whose PlayCount is shown. Playback is unaffected.
Workaround
Read Duration from the Song object the game passed to Play(Song*), or play through a SongCollection.
Related pages
The same subject is explained at several altitudes. These are the neighbouring pages at each one.
- Known issues
- Bug index