Содержание
1. Введение
3. Настройка
3.1 Декодер
3.2 RenderScript
5. Применение эффекта RenderScript
7. Заключение
В этой статье мы покажем, как использовать классы Android Media и Android RenderScript для применения эффектов к декодированному видео. Для упрощения в статье описывается только воспроизведение видео; воспроизведение звука не рассматривается. Существует два рабочих потока: поток декодера и поток RenderScript.
Поток декодера делает следующее:
1. Декодирует поток данных и помещает декодированные изображения в декодированный буфер.
2. Копирует только что декодированный кадр в расположение Renderscript.
Поток Renderscript делает следующее:
1. Производит постобработку декодированных кадров видео, чтобы применить преобразование цветового пространства и эффект оттенков серого.
2. Направляет выходные данные в другое расположение, связанное с SurfaceTexture.
Для воспроизведения API Android требуется опыт разработки приложений для Android. Дополнительные сведения о Media Decoder и RenderScript см. в следующих статьях.
1. Android BasicMediaDecoder - The Android Sample BasicMediaDecoder sample -
http://developer.android.com/samples/BasicMediaDecoder/index.html
2. Android BasicRenderScript - The Android Sample BasicRenderScript sample -
http://developer.android.com/samples/BasicRenderScript/index.html
3. Intel INDE Code Sample - Getting Started with RenderScript
https://software.intel.com/en-us/articles/renderscript-basic-sample-for-android-os
Вот фрагмент кода для настройки декодера. Дополнительные сведения см. в разделе класс Android MediaExtractor.
// Get the media file String MediaPath = Environment.getExternalStorageDirectory().getPath() + "/Movies/test4.mp4"; // Create a Uri which parses the given encoded URI string Uri uri = Uri.parse(MediaPath); try { // Set the Data Source for the MediaExtractor mMediaExtractor.setDataSource(this, uri, null); // Get The number of tracks int numTracks = mMediaExtractor.getTrackCount(); for( int i=0; i < numTracks; i++ ) { mMediaExtractor.unselectTrack(i); } for( int i=0; i < numTracks; i++ ) { // Get the type of the track MediaFormat format = mMediaExtractor.getTrackFormat(i); String mimeType = format.getString(MediaFormat.KEY_MIME); if(mimeType.contains("video")) { // For a Video Track, get Media Format // Frame width and height mVideoWidth = format.getInteger(MediaFormat.KEY_WIDTH); mVideoHeight = format.getInteger(MediaFormat.KEY_HEIGHT); // Buffer Width and Height mBufferWidth = mVideoWidth; // make sure that the buffer height is a multiple of 16 mBufferHeight = mVideoHeight + (mVideoHeight%16); // Initialize RenderScript See Section 3 initRenderScript(); // Creade a Decoder mDecoder = MediaCodec.createDecoderByType(mimeType); // Configure the decoder mDecoder.configure(format, // Media Format from the stream null, // set surface to send decoded frames to buffers null, // no Crypto 0); // flags if(mDecoder != null) { // Select this track mMediaExtractor.selectTrack(i); // start the decoder mDecoder.start(); // query input and output buffers mInputBuffers = mDecoder.getInputBuffers(); mOutputBuffers = mDecoder.getOutputBuffers(); // allocate byte buffer to temporarily hold decoded frame, input to renderscript // 3/2 because the decoder outputs NV12 which has 12bits per pixel mLocalOutputBuffers = new byte[mBufferWidth*mBufferHeight*3/2]; break; } } else { // Handle this condition } } } catch (IOException e) { e.printStackTrace(); }
Вот фрагмент кода для настройки RenderScript. Дополнительные сведения см. в разделе Android Renderscript.
// Create RenderScript Context mRS = RenderScript.create(this); // Load RenderScript mScript = new ScriptC_process(mRS); // Create Allocations for reading into and out of Script // Input Allocation // Create a new Pixel Element of type YUV Element elemYUV = Element.createPixel(mRS, Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_YUV); // Create a new (Type).Builder object of type elemYUV Type.Builder TypeYUV = new Type.Builder(mRS, elemYUV); // Set YUV format to NV21. The Decoder Outputs NV21 Surfaces TypeYUV.setYuvFormat(ImageFormat.NV21); // Create an Allocation mAllocationYUV = Allocation.createTyped(mRS,TypeYUV.setX(mVideoWidth).setY(mVideoHeight).create(), // Allocation Type Allocation.MipmapControl.MIPMAP_NONE, // No MIPMAP Allocation.USAGE_SCRIPT);// Allocation will be used by a script // Output Allocation // Create a new Pixel Element of type RGBA Element elemOUT = Element.createPixel(mRS, Element.DataType.UNSIGNED_8, Element.DataKind.PIXEL_RGBA); // Create a new (Type).Builder object of type elemOUT Type.Builder TypeOUT = new Type.Builder(mRS, elemOUT); // create an Allocation mAllocationOUT = Allocation.createTyped(mRS, TypeOUT.setX(mVideoWidth).setY(mVideoHeight).create(), // Allocation Type Allocation.MipmapControl.MIPMAP_NONE, // No MIPMAP Allocation.USAGE_SCRIPT| // will be used by a script Allocation.USAGE_IO_OUTPUT); // will be used as a SurfaceTexture producer // Associate the Surface with output allocation // Get the Surface Texture SurfaceTexture surfaceTexture = mPlaybackView.getSurfaceTexture(); if(surfaceTexture != null) { // Create a new surface Surface surface = new Surface(surfaceTexture); if(surface != null) { // set allocation surface mAllocationOUT.setSurface(surface); } } // Set the Input Allocation mScript.set_gIn(mAllocationYUV);
Вот фрагмент кода для копирования декодированных буферов в расположение RenderScript, входящее в состав потока декодирования. Дополнительные сведения о запуске этого потока см. в разделе класс MediaCodec.
Перед вызовом codec.releaseOutputBuffer(outputBufferIndex, ...) выполните следующие действия:
1. Получите декодированный буфер
2. Скопируйте его в расположение
3. Создайте новый поток для применения эффекта RenderScript
// decoded buffer size int bufsize = mOutputBuffers[index].capacity(); // get the decode buffer mOutputBuffers[index].get(mLocalOutputBuffers, 0, bufsize); // copy to input Allocation mAllocationYUV.copyFrom(mLocalOutputBuffers); // rewind the ByteBuffer so that the pointer can be back to 0 mOutputBuffers[index].rewind(); // start a new thread for applying RenderScript effect new ProcessData().execute(); // release outputbuffer back to the decoder mDecoder.releaseOutputBuffer(index, render);
Применение эффекта RenderScript
Вот фрагмент кода для потока RenderScript. Дополнительные сведения о потоке UI см. в разделе AsyncTask.
private class ProcessData extends AsyncTask<byte[], Void, Boolean> { @Override protected Boolean doInBackground(byte[]... args) { // Apply the Renderscript YUV_to_RGB + greyscale effect mScript.forEach_yuvToRgb_greyscale(mAllocationYUV, mAllocationOUT); // Send the output allocation to surfacetexture mAllocationOUT.ioSend(); return true; } }
rs_allocation gIn; void yuvToRgb_greyscale(const uchar *v_in, uchar4 *v_out, uint32_t x, uint32_t y) { uchar yp = rsGetElementAtYuv_uchar_Y(gIn , x, y) & 0xFF; uchar4 res4; res4.r = yp; res4.g = yp; res4.b = yp; res4.a = 0xFF; *v_out = res4; }
Платформа Android Multimedia Framework предоставляет высокоуровневый API для использования аппаратной поддержки мультимедиа. Обработка видео является весьма ресурсоемкой нагрузкой, и Renderscript предоставляет платформу для распараллеливания этой нагрузки. В приведенной выше статье описывается метод, позволяющий платформе Android Multimedia Framework взаимодействовать с Renderscript для применения эффектов постобработки к декодированному видео.
Для публикаций и участия в обсуждении этой статьи можно использовать форум.