-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
On iOS devices, rendering of videotoolbox decoded frames stops after 30000 frames. #388
Comments
PS. XCode Instruments does not show leaks, nothing. |
Interesting can we trust this? Btw, are you saying it might happening due to mem leak somewhere? |
Yes, I think so, I have good experience with xcode Instruments, it is even more helpful than valgrind. What I cannot see is if memory is allocated, still accessible, but should have been released because it's not needed anymore. Meaning memory which is not leaked but just unused waste. I don't see that here. Also I did let the code run on MacOS over night, more than 8 hours, and it's still running and memory usage is stable (according to Activity Monitor in MacOS). If there would be any significant memory leak it should have shown.
Yes, but not RAM, not memory, but resources, something like file descriptors, file handles (everyting on unix/linux is a file). As the tests confirm every time, the freeze happens after almost exactly 30000 frames are sent to display. The correct value seems to be more 29980 or a little less. It happens too when I pause playing in between, multiple pauses, and also when I switch the input source (which means resetting the Codecs in QtAVPlayer). Whatever I do on the frame producing side seems not to matter. When a total of almost 30000 frames, as produced by videotoolbox, without frame format conversion, is sent to display with videoSink->setVideoFrame(), then it freezes the UI. But the freeze is not 100%, there are very short updates, just about one frame, and some UI elements, every minute or so - I guess because some resources got available again, 2 or 3 file descriptors or similar. Such an update from time to time is unlikely if something crashed or got locked. And while this is happening in the display thread, the threads handling download and processing of the data run and run without problems whatsoever. |
Unless there are better ideas, I will try to confirm if this is happening too when using standard QtMM mediaplayer with Qt 6.5.2, on iOS devices, or not.
(This will be time consuming, I don't like to go that route, but is here a choice?) |
https://github.com/valbok/QtAVPlayer/blob/master/src/QtAVPlayer/qavhwdevice_videotoolbox.mm might be possible that some resources there should be cleaned up or reused. F.e. This is very suspicious CVPixelBufferRetain(m_hw->pbuf); |
Yes, exactly, there might be the problem hidden somewhere. I have not much experience with Objective C, but I'll think through the code there. |
As a test, I commented out the line with CVPixelBufferRetain(m_hw->pbuf); It seemed to be not useful anyway, because m_hw->pbuf doesn't go out of scope.
However when testing on Mac, it crashes directly after showing the first frame:
So the CVPixelBufferRelease without prior CVPixelBufferRetain crashes it. So now I test (again first on the Mac) with both commented out. |
pbuf seems ok than, but newTextureWithDescriptor interesting does it require to release this texture, or it creates everytime new. |
if there is a change in memory usage, then it seems to be slightly less, but not more. Other than that, unchanged, it runs on Mac forever, but on iPad the UI freeze still happens. This time after exactly 29800 frames sent to display. The CVPixelBufferRelease() / CVPixelBufferRetain() does not cause it. |
That line above? |
To check, if it might stop the freezing if that textures line would not run, I commented them out:
I expected that the video might play, just no picture showing, but to my surprise. the images show... video plays. The textures returned are as set earlier there:
nevertheless, the pictures are displayed. This code might not be needed, at least not in the test case, Qt 6.5.2 on real iPad device. |
It passed the 32000 frames without freezing !!!!!! If textures are not needed, possibly nothing in that function is really needed, so new test with the complete function changed to this:
Nevertheless the video still plays fine and smooth. |
Exactly means that no textures are used and no HW accel for rendering. Means textures are leaking here. |
You mean that the QtMM code handling the display (which called handle()) sees that there are no textures returned, and switches to a different way of rendering? Very probable, and if needed, verifiable in QtMM source. Rendering seems to be very efficient though, may be because the image to render is already in GPU memory? In any case, it plays perfectly smooth on the devices I tested so far, iPhone XS Max, iPhone 11 PM, 4th gen iPad 11 Pro. On a very old iPad 12 pro it stutters, but that might have other reasons, I still have to dig in there. If this performance is confirmed over all iOS devices then it's good enough for use.
Yes, but that's then in QtMM code, right? |
Yes, this is how it is implemented, it checks for texture first, if nothing, gets back to map() which downloads data from GPU if any. |
No, it is somewhere here, we are responsible to free resources, so I will try to look a bit later, might be possible need to release textures in dtor |
does it mean that MacOS does not reproduce the issue? regardless how long it works? |
Yes, the issue does
|
could you check if you return empty textures but keep calling textures[i] = quint64([m_hw->device newTextureWithDescriptor:desc iosurface:surface plane:i]); ? trying to understand where it should be cleared. |
maybe it needs double CVPixelBufferRelease(m_hw->pbuf); ? |
or there is an issue inside QtMM, can you try just QtMM? |
With this the freeze still happens at the same frame count.
I only added textures2, so that textures can get returned empty and textures2 will get filled. I would have expected that textures would/should get released later, in the code which calls handle(). As far as I can see, here we can only return them, and don't see them again. |
where would you insert that? |
I never had such freezing when using QtMM / QMediaPlayer alone. I have my old stuff running with Qt 5.15.x and also Qt 6.4.3 QtMM, using QMediaplayer in C++ linked to QML Videooutput. The QMediaPlayer part is now replaced with QtAVPlayer, otherwise almost identical, thus comparable. This freezing issue did not show up, never. It was extensively tested on real iOS devices also with Qt 6.4.3. What I experienced there was on some devices almost flawless playing, but on many devices often stop and go. QtMM is calling the IOS internal Player, and somewhere there it got hiccups, often, too often. Also when the input has timecodes which are not always perfect, such as when you load streams over the internet and some parts get lost, or the stream is bad quality, or input resetted, then QtMM and the iOS internal Player always stumble. This is all fixed now with using QtAVPlayer/ffmpeg, it plays for hours without issues. What I hoped for when I stumbled over QtAVPlayer, it got fulfilled, finally a stable Player for Android and iOS. (Disclaimer: I have modified the PTS syncing a little to adapt to input from constant streams with sometimes messed up PTS values. Such an adaption was not possible with QtMM, but with QtAVPlayer all is possible. Thanks for that!). |
I have to correct that. The issue shows very similar on the Mac, but it takes much longer to show. I only noticed it when playing streams which should play for hours. I have not noticed this earlier, because before, at the time we discussed that here, I simply did not test long enough. And then I took over the code from iOS for the Mac version of my App, and that code has the patch for iOS in qavhwdevice_videotoolbox.mm:
If I use instead of this patch the original code from qavhwdevice_videotoolbox.mm, it will freeze after a while, it could be about 1 hour, or less. Tested with Qt 6.6.3, ffmpeg 6.1.1, code compiled for x86_64, on Intel iMac with Big Sur 11.7.10, and on M1 Mac mini with Sonoma 14.5. |
Qt 6.4.3 and Qt 6.5.2, on iOS devices, real devices:
Decoding works fine, decoded frames are AV_PIX_FMT_VIDEOTOOLBOX.
When sending to display using videoSink->setVideoFrame(), they are redeclared (not converted) to NV12 in the code of QtAVPlayer in
QAVVideoFrame::operator QVideoFrame()
:Then they are taken for display by
videoSink->setVideoFrame()
.Works fine, plays smooth on iPad and iPhone. Looks perfect!
But after 20 minutes or apparently exactly 30000 frames fed into
videoSink->setVideoFrame()
the displayed picture and the complete UI is freezing, but not due to something hogging CPU (CPU usage drops significantly).Now there is only about one UI update per minute, or per two minutes, happening. The UI is unresponsive, but the player continues to play, QtAVPlayer delivers audio and video frames, the audio continues to play, only the pictures are not updated.
Different to running on iOS simulator or Mac the memory usage is increasing slowly up until the freezing. But the freezing occurs at the 30000 frames mark, and not when a certain memory threshold is reached. It freezes also no matter if HD or SD video is played.
When sending dummy frames like this:
then there is no freeze, and memory usage increases significantly less.
When converting the AV_PIX_FMT_VIDEOTOOLBOX frames to AV_PIX_FMT_YUV420P or AV_PIX_FMT_NV12 before displaying with
or
which results in displaying these frames as software frames, then there is also no freeze (but the display is jerky, too slow, so that is unfortunately not a workaround).
So this seems to be quite clearly a problem in QtMM videoSink->setVideoFrame(). However when using QtMM alone, directly, the QtMM black box mediaplayer, then there is no freeze, at least not with Qt 5, it plays for hours and smooth.
The question is now, is there something special to consider when sending the AV_PIX_FMT_VIDEOTOOLBOX frames to setVideoFrame()? May be they need not to be relabelled as NV12, but as something different?
Any ideas?
The text was updated successfully, but these errors were encountered: