I searched the Internet, but there is very little information about this.
I have a live broadcast application in which I send H264 encoded frames and AAC audio fragments as a result of the camera and microphone using the Android MediaCodec SDK via the RTMP stack.
My streams in real time are 720p and I aim for excellent quality with 2500 Kbps. This obviously requires a very good network connection, which means 4G if you use a data plan.
The problem, even with the largest connection, will have low peaks and congestions, so there will be times when the network cannot hold such a heavy flow. Since I want to offer high reliability, I want to include automatic adaptive bitrate in my application so that image quality is reduced in favor or reliability.
The point is how to achieve this automatic adaptation to network conditions without losing frames? Is it possible? I used professional encoding devices such as Cerevo and they never lose frames - however with my application I always get terrible drag and drop due to p-frames being lost on the network.
This is what I have:
private long adaptBitrate(long idleNanos, Frame frame) {
int bytes = frame.getSize();
long nowNanos = System.nanoTime();
if (nowNanos - mLastNanos > 1000L * 1000 * 1000) {
double idle = (double) idleNanos / (double) (nowNanos - mLastNanos);
float actualBitrate = newBitrate;
int size = mBuffer.size();
String s = "Bitrate: " + actualBitrate / 1000
+ " kbps In-Flight:" + bytes
+ " idle: " + idle;
if (size > MAX_BUF_SIZE && size > mLastSize) {
Log.i(TAG, "adaptBitrate: Dropping bitrate");
newBitrate = (int) ((double) actualBitrate * BITRATE_DROP_MULTIPLIER);
if (newBitrate < MIN_BITRATE) {
newBitrate = MIN_BITRATE;
}
s += " late => " + newBitrate;
mRtmpHandler.requestBitrate(newBitrate);
} else if (size <= 2 && idle > IDLE_THRESHOLD) {
mIdleFrames++;
if(mIdleFrames >= MIN_IDLE_FRAMES){
Log.i(TAG, "adaptBitrate: Raising bitrate");
newBitrate = (int) ((double) newBitrate * BITRATE_RAISE_MULTIPLIER);
if (newBitrate > MAX_BITRATE) {
newBitrate = MAX_BITRATE;
}
s += " idle => " + newBitrate;
mRtmpHandler.requestBitrate(newBitrate);
mIdleFrames = 0;
}
}
debugThread(Log.VERBOSE, s);
mLastNanos = System.nanoTime();
mLastSize = size;
idleNanos = 0;
}
return idleNanos;
}
, , . , , .
, , , , (2 ). , (, 1500 /), , . .
? , ...