Zamierzam użyć Android MediaCodec do dekodowania strumienia wideo, a następnie użyć obrazów wyjściowych do dalszego przetwarzania obrazu w natywnym kodzie.Naruszenie zasad dostępu w natywnym kodzie za pomocą przyspieszanego sprzętowo dekodera Android MediaCodec:
Platforma: ASUS tf700t android 4.1.1. Strumień testowy: H.264 full HD @ 24 frm/s
Wewnątrz SoC Tegra-3, liczę na sprzętową obsługę dekodowania wideo. Funkcjonalnie moja aplikacja zachowuje się zgodnie z oczekiwaniami: rzeczywiście mogę uzyskać dostęp do obrazów dekodera i przetwarzać je poprawnie. Jednak mam bardzo duże obciążenie procesora dekodera.
W następujących eksperymentach obciążenie procesu/nici jest mierzone przez "wierzchołek -m 32-t" w powłoce adb. Aby uzyskać wiarygodne dane wyjściowe z "góry", wszystkie 4 rdzenie procesora są wymuszone aktywacją, uruchamiając kilka wątków w pętli na zawsze z najniższym priorytetem. Potwierdza to powtarzające się wykonywanie "cat/sys/devices/system/cpu/cpu [0-3]/online". Aby wszystko było proste, jest tylko dekodowanie wideo, brak dźwięku; i nie ma kontroli czasu, więc dekoder działa tak szybko, jak tylko może.
Pierwszy eksperyment: uruchom aplikację, wywołując funkcję przetwarzania JNI, ale wszystkie dalsze wywołania przetwarzania są komentowane. Wyniki:
- wydajność: 25 FRM/S
- 1% obciążenie gwintu VideoDecoder stosowania
- 24% obciążenia gwintu Binder_3 procesowej/system/bin/Serwer mediów
ramę wydaje się, że prędkość dekodowania jest ograniczona do CPU (25% czterordzeniowego procesora) ... Po włączeniu przetwarzania wyjściowego zdekodowane obrazy są poprawne, a aplikacja działa. Jedyny problem: zbyt wysokie obciążenie procesora do dekodowania.
Po wielu eksperymentach zastanawiałem się nad wypuszczeniem MediaCodec na powierzchnię. We wszystkich innych aspektach kod jest identyczny. Wyniki:
- przepustowości 55 FRM/s (mili !!)
- 2% obciążenie gwintu VideoDecoder stosowania
- 1% obciążenie gwintu MediaServer procesowej/system/bin/MediaServer
Rzeczywiście wideo jest wyświetlane na podanej powierzchni. Ponieważ nie ma prawie żadnego obciążenia procesora, to musi być sprzętowo przyspieszany ...
Wygląda na to, że de MediaCodec używa jedynie akceleracji sprzętowej, jeśli dostarczona jest powierzchnia?
Jak dotąd, tak dobrze. Byłem już skłonny używać Surface jako obejścia (nie jest to wymagane, ale w niektórych przypadkach nawet przyjemne). Ale w przypadku, gdy powierzchnia jest dostarczona, nie mam dostępu do wyjściowych obrazów! Wynik jest naruszeniem dostępu w kodzie rodzimym.
To naprawdę mnie zastanawia! Nie widziałem żadnego pojęcia o ograniczeniach dostępu ani o tym, co jest w dokumentacji: http://developer.android.com/reference/android/media/MediaCodec.html. Również nic w tym kierunku nie było wspomniane na prezentacji go/go google http://www.youtube.com/watch?v=RQws6vsoav8.
A więc: jak używać sprzętowo akcelerowanego dekodera Android MediaCodec i uzyskiwać dostęp do obrazów w natywnym kodzie?Jak uniknąć naruszenia zasad dostępu? Każda pomoc jest doceniana! Również wszelkie wyjaśnienia lub podpowiedź.
Jestem prawie pewny, że MediaExtractor i MediaCodec są używane poprawnie, ponieważ aplikacja działa poprawnie (o ile nie zapewniam powierzchni). Jest jeszcze dość eksperymentalny, a dobry projekt API jest na liście rzeczy do zrobienia ;-)
Należy pamiętać, że jedyną różnicą pomiędzy tymi dwoma eksperymentów jest zmienna mSurface: null lub rzeczywistej powierzchni w „mDecoder.configure (mediaFormat , mSurface, null, 0); "
kod inicjalizacji:
mExtractor = new MediaExtractor();
mExtractor.setDataSource(mPath);
// Locate first video stream
for (int i = 0; i < mExtractor.getTrackCount(); i++) {
mediaFormat = mExtractor.getTrackFormat(i);
String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
Log.i(TAG, String.format("Stream %d/%d %s", i, mExtractor.getTrackCount(), mime));
if (streamId == -1 && mime.startsWith("video/")) {
streamId = i;
}
}
if (streamId == -1) {
Log.e(TAG, "Can't find video info in " + mPath);
return;
}
mExtractor.selectTrack(streamId);
mediaFormat = mExtractor.getTrackFormat(streamId);
mDecoder = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));
mDecoder.configure(mediaFormat, mSurface, null, 0);
width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
Log.i(TAG, String.format("Image size: %dx%d format: %s", width, height, mediaFormat.toString()));
JniGlue.decoutStart(width, height);
pętla Decoder (działa w osobnym wątku):
ByteBuffer[] inputBuffers = mDecoder.getInputBuffers();
ByteBuffer[] outputBuffers = mDecoder.getOutputBuffers();
while (!isEOS && !Thread.interrupted()) {
int inIndex = mDecoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
// Valid buffer returned
int sampleSize = mExtractor.readSampleData(inputBuffers[inIndex], 0);
if (sampleSize < 0) {
Log.i(TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM");
mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mExtractor.getSampleTime(), 0);
mExtractor.advance();
}
}
int outIndex = mDecoder.dequeueOutputBuffer(info, 10000);
if (outIndex >= 0) {
// Valid buffer returned
ByteBuffer buffer = outputBuffers[outIndex];
JniGlue.decoutFrame(buffer, info.offset, info.size);
mDecoder.releaseOutputBuffer(outIndex, true);
} else {
// Some INFO_* value returned
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.i(TAG, "RunDecoder: INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = mDecoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.i(TAG, "RunDecoder: New format " + mDecoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
// Timeout - simply ignore
break;
default:
// Some other value, simply ignore
break;
}
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "RunDecoder: OutputBuffer BUFFER_FLAG_END_OF_STREAM");
isEOS = true;
}
}
Nadal nie ma rozwiązania. Wszelkie sugestie są nadal mile widziane. Również sugestie dotyczące eksperymentów w celu zwiększenia zrozumienia. Czy ktoś ma dekodowanie sprzętowe działające z wykorzystaniem MediaCodec? Może na innej platformie? – Bram
Bram, próbuję rozwiązać dokładnie ten sam problem. Wygląda na to, że to spowolnienie nie dotyczy wielu kopii zdekodowanego bufora.Kiedy zdekodowane dane są przeznaczone do prezentacji na natywnej powierzchni, wygląda na to, że istnieje pewna bezpośrednia ścieżka danych i wykorzystuje ona TILER (rendering kaflowy). Gdy potrzebujesz dostępu do pełnej ramki YUV (np. Chcesz uzyskać dostęp do zdekodowanego bufora), dekoder musi wykonać dodatkowe zadania, takie jak renderowanie wszystkich danych do bufora pamięci i kopiowanie go, co czyni go tak wolnym. Dosłownie zmarnowałem tydzień mojego życia, próbując naprawić problem, ale wydaje się, że nie ma nic do naprawienia. – Pavel
Co więcej, w moim przypadku miałem 720p przy 30fps, którego nie byłem w stanie odcyfrować w czasie rzeczywistym, podczas gdy odtwarzacz natywny nie miał problemów z opłaceniem go. – Pavel