-
Notifications
You must be signed in to change notification settings - Fork 468
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
ffmpeg: reallocate output buffer dynamically #746
ffmpeg: reallocate output buffer dynamically #746
Conversation
Thanks for the PR! I added a few comments for improvement ideas. |
Where? 🤔 @tonihei |
LOGE("JNI_OnLoad: GetEnv failed"); | ||
return -1; | ||
} | ||
jclass clazz = env->FindClass("androidx/media3/decoder/ffmpeg/FfmpegAudioDecoder"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you reference classes and methods by name from native code, you'll also need to add them to the proguard-rules.txt
file of the ffmpeg module to prevent them from being obfuscated during the build process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
@@ -49,6 +50,18 @@ public ByteBuffer init(long timeUs, int size) { | |||
return data; | |||
} | |||
|
|||
public ByteBuffer grow(int newSize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add Javadoc to this method in the style of other existing Javadoc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
int outSampleSize = av_get_bytes_per_sample(context->request_sample_fmt); | ||
int outSamples = swr_get_out_samples(resampleContext, sampleCount); | ||
int bufferOutSize = outSampleSize * channelCount * outSamples; | ||
if (outSize + bufferOutSize > outputSize) { | ||
LOGE("Output buffer size (%d) too small for output data (%d).", | ||
LOGE("Output buffer size (%d) too small for output data (%d), reallocating buffer.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't need to be LOGE anymore if the case is handled gracefully, we could change it to LOGD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
outputSize = outSize + bufferOutSize; | ||
outputBuffer = growBuffer(outputSize); | ||
if (outputBuffer) { | ||
LOGE("Reallocated output buffer."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for this log line either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
ByteBuffer outputData = outputBuffer.init(inputBuffer.timeUs, outputBufferSize); | ||
int result = ffmpegDecode(nativeContext, inputData, inputSize, outputData, outputBufferSize); | ||
outputBuffer.init(inputBuffer.timeUs, outputBufferSize); | ||
Assertions.checkNotNull(outputBuffer.data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can leave out this check as this is guaranteed by the API contract of the init
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, but then IDE paints outputBuffer.data
on the next line yellow.
return null; | ||
} | ||
|
||
// Called from native code | ||
@Keep |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use this annotation anywhere else at the moment. I'm not sure, but I think the proguard rules I asked about in another comment should be sufficient to keep the method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Oops, sorry, I forgot to click on "publish review"! |
fe4bd76
to
a66683a
Compare
Thanks! I'll now import this PR internally and you may see some further clean-up commits appear here. |
With FFmpeg we can't determine size of output buffer ahead of time for all codecs, so we need to reallocate it when needed instead of simply failing.
Also amended Javadoc and added assertion to grow method
a66683a
to
8750ed8
Compare
@tonihei Your commit 8750ed8 breaks this. By using a variable to hold the buffer you keep a reference to the old buffer at the end of the function. So that call |
Oh yes, sorry, that was clearly too much "clean-up" :) Will send a fix commit straight-away. |
Should be fixed by ae6f83d |
@tonihei yes the fix works but the keep rules are not working :( |
Do you want to send another PR given you can actually test this out? I was working by looking on the code only without an actual test case. |
@tonihei In this case just compile any app with ffmpeg with R8 and the extension will fail to load in the And I can send you a file that repro this by mail if you want for future reference too. For the PR even after all those years I'm still far from a proguard/R8 expert. So As bonus questions since there's movement in that extension: Is there any chance that #707 be considered? And if yes is there any interest in a PR that adds resampling to ffmpeg extension so that hi res media that Android can't play can properly be downsampled to the max supported sample rate ? |
Yes, if you could send a file that needs this logic to [email protected] with "Issue #746" in the subject, that would be great so that I can just test it myself. |
And just in case I was not clear enough @tonihei ffmpeg extension in main is broken for any file in current state if you build with R8 as it fails to load the extension at all. |
@Tolriq I can't reproduce the R8 failure I'm afraid. I'm testing on the latest The existing proguard rule seems to ensure that the
to get an explanation for why this class is kept in my build and the output directly references the existing rule:
Could you check that the Ffmpeg proguard rules are included in your build? (maybe you need to clean caches/builds and try again?) |
@tonihei The rules are included I'm using R8 with full mode and version "8.2.34" In that mode the SimpleDecoderOutputBuffer is renamed and merged leading to the issue. Maybe it's an R8 issue and the fact that the class is passed as a param should ensure it's kept, but from my understanding of R8/Proguard there's no guarantee about that with the current rule. The rule
Just implies that the class and function is kept nothing about the parameters. And in the code we do reflection with
That explicitly require the type and name to be kept. I can open a question on R8 team about this, and come back here if you want their confirmation. Edit: Opened https://issuetracker.google.com/issues/309068090 they are very efficient so we'll have an answer soon. |
Let me try the full mode too (that wasn't enabled in my test I believe) |
Thanks for following up on the issue with R8. I still can't reproduce it in any way though. I upgraded to R8 8.2.33 (as part of the Android Studio Gradle plugin 8.2.0-rc02), but can't see the issue with or without full mode. Not sure where the difference is to be honest. Could I ask you to test if |
@tonihei yes it works with:
|
Great, will send a fix for this then! Thanks for your help :) |
May I ask again some help on #369 then ? ;) Kills me to no be able to provide the test file to get this merged. |
I think this fell through the cracks a bit because we were waiting to see if there is a way to produce a test file for unit tests. |
With FFmpeg we can't determine size of output buffer ahead of time for all codecs, so we need to reallocate it when needed instead of simply failing.