├── LICENSE ├── README.md ├── patches ├── common │ ├── 0001-Enable-share-group-workaround-for-Vivante-GPUs.patch │ └── 0002-Add-VPU-video-decode-accelerator-to-Chromium-GPU-.patch └── wayland │ └── 0001-Modify-eglwayland-versions-for-Vivante-GPUs.patch └── src └── content └── common └── gpu └── media ├── imx_gl_viv_direct_texture.cc ├── imx_gl_viv_direct_texture.h ├── imxvpu_video_decode_accelerator.cc ├── imxvpu_video_decode_accelerator.h ├── imxvpucodec.h ├── imxvpucodec_fslwrapper.c ├── imxvpucodec_platform.h ├── imxvpucodec_platform_chromium.cc └── imxvpucodec_platform_chromium.h /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Carlos Rafael Giani 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Freescale i.MX VPU hardware video decoding support for Chromium's GPU media stack 2 | ================================================================================= 3 | 4 | This repository contains sources which add hardware video decoding support to Chromium 5 | using the Freescale i.MX VPU. They are placed under the BSD 3-clause license. See the 6 | LICENSE file for details. 7 | 8 | These sources are written against Chromium 48.0.2548.0. 9 | 10 | 11 | Prerequisites 12 | ------------- 13 | 14 | The i.MX platform requires the Vivante GPU drivers and userspace libraries to be installed. 15 | The patches make use of Vivante specific OpenGL ES 2.0 extension to ensure zero-copy display 16 | of decoded frames. 17 | 18 | Also, Freescale's libfslvpuwrap library is required. Use at least version 1.0.45. 19 | 20 | Furthermore, Chromium must be ran with its EGL backend enabled. Use the --use-gl=egl option for this. 21 | 22 | 23 | Patching Chromium 24 | ----------------- 25 | 26 | 1. Copy the contents of src/ to the Chromium source tree. 27 | 2. Apply all patches from patches/common/ in the source tree which are not version specific. 28 | 3. If you are using a Chromium build that has already been patched with ozone-wayland, also apply 29 | the patches in patches/wayland/ . Note that it patches ozone-wayland files. 30 | 4. Build Chromium. chromium://gpu should show video decoding as "Accelerated". 31 | -------------------------------------------------------------------------------- /patches/common/0001-Enable-share-group-workaround-for-Vivante-GPUs.patch: -------------------------------------------------------------------------------- 1 | From 4990b4078c06cb0633f5764841259d18cd9fb7cf Mon Sep 17 00:00:00 2001 2 | From: Carlos Rafael Giani 3 | Date: Sun, 12 Oct 2014 19:06:52 +0200 4 | Subject: [PATCH] Enable share group workaround for Vivante GPUs 5 | 6 | This fixes the "blue background" bug with transparent HTML canvas areas 7 | 8 | Upstream-Status: Inappropriate [other] 9 | This fix enables the workarounds for Vivante GPUs on all platforms, even on 10 | ones where it may not be broken; even limiting it to android and linux might 11 | not be enough 12 | 13 | Signed-off-by: Carlos Rafael Giani 14 | --- 15 | gpu/config/gpu_driver_bug_list_json.cc | 3 --- 16 | 1 file changed, 3 deletions(-) 17 | 18 | --- a/gpu/config/gpu_driver_bug_list_json.cc 19 | +++ b/gpu/config/gpu_driver_bug_list_json.cc 20 | @@ -415,9 +415,6 @@ 21 | "id": 34, 22 | "cr_bugs": [179250, 229643, 230896], 23 | "description": "Share groups are not working on (older?) Vivante drivers", 24 | - "os": { 25 | - "type": "android" 26 | - }, 27 | "gl_extensions": ".*GL_VIV_shader_binary.*", 28 | "features": [ 29 | "use_virtualized_gl_contexts" 30 | -------------------------------------------------------------------------------- /patches/common/0002-Add-VPU-video-decode-accelerator-to-Chromium-GPU-.patch: -------------------------------------------------------------------------------- 1 | From b80573b80dd42a5aa00c4d2f463e5fede1edfef0 Mon Sep 17 00:00:00 2001 2 | From: Carlos Rafael Giani 3 | Date: Thu, 25 Sep 2014 22:54:28 +0200 4 | Subject: [PATCH] Add VPU video decode accelerator to Chromium GPU media 5 | stack 6 | 7 | Upstream-Status: Inappropriate [other] 8 | Upstream currently does not accept patches for video acceleration in Linux 9 | 10 | Signed-off-by: Carlos Rafael Giani 11 | --- 12 | build/common.gypi | 3 ++ 13 | .../gpu/media/gpu_video_decode_accelerator.cc | 7 +++++ 14 | content/content_common.gypi | 34 ++++++++++++++++++++++ 15 | gpu/config/software_rendering_list_json.cc | 5 ++-- 16 | ui/gl/gl_implementation.h | 4 +-- 17 | 5 files changed, 49 insertions(+), 4 deletions(-) 18 | 19 | --- a/build/common.gypi 20 | +++ b/build/common.gypi 21 | @@ -1271,6 +1271,9 @@ 22 | # Use system ICU instead of bundled one. 23 | 'use_system_icu%' : 0, 24 | 25 | + # Whether or not the target platform is based on the i.MX SoC 26 | + 'imx_platform%': 1, 27 | + 28 | # Default to enabled PIE; this is important for ASLR but we may need to be 29 | # able to turn it off for various reasons. 30 | 'linux_disable_pie%': 0, 31 | --- a/content/common/gpu/media/gpu_video_decode_accelerator.h 32 | +++ b/content/common/gpu/media/gpu_video_decode_accelerator.h 33 | @@ -77,6 +77,7 @@ 34 | class MessageFilter; 35 | 36 | scoped_ptr CreateDXVAVDA(); 37 | + scoped_ptr CreateImxVpuVDA(); 38 | scoped_ptr CreateV4L2VDA(); 39 | scoped_ptr CreateV4L2SliceVDA(); 40 | scoped_ptr CreateVaapiVDA(); 41 | --- a/content/common/gpu/media/gpu_video_decode_accelerator.cc 42 | +++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc 43 | @@ -31,6 +31,8 @@ 44 | #if defined(OS_WIN) 45 | #include "base/win/windows_version.h" 46 | #include "content/common/gpu/media/dxva_video_decode_accelerator.h" 47 | +#elif defined(IMX_PLATFORM) 48 | +#include "content/common/gpu/media/imxvpu_video_decode_accelerator.h" 49 | #elif defined(OS_MACOSX) 50 | #include "content/common/gpu/media/vt_video_decode_accelerator.h" 51 | #elif defined(OS_CHROMEOS) 52 | @@ -258,6 +260,7 @@ 53 | // same as the order of querying supported profiles of VDAs. 54 | const GpuVideoDecodeAccelerator::CreateVDAFp create_vda_fps[] = { 55 | &GpuVideoDecodeAccelerator::CreateDXVAVDA, 56 | + &GpuVideoDecodeAccelerator::CreateImxVpuVDA, 57 | &GpuVideoDecodeAccelerator::CreateV4L2VDA, 58 | &GpuVideoDecodeAccelerator::CreateV4L2SliceVDA, 59 | &GpuVideoDecodeAccelerator::CreateVaapiVDA, 60 | @@ -298,6 +301,18 @@ 61 | return decoder.Pass(); 62 | } 63 | 64 | +scoped_ptr 65 | +GpuVideoDecodeAccelerator::CreateImxVpuVDA() { 66 | + scoped_ptr decoder; 67 | +#if defined(IMX_PLATFORM) 68 | + DVLOG(0) << "Using the i.MX6 VPU decode accelerator"; 69 | + decoder.reset(new ImxVpuVideoDecodeAccelerator( 70 | + stub_->decoder()->AsWeakPtr(), 71 | + make_context_current_)); 72 | +#endif 73 | + return decoder.Pass(); 74 | +} 75 | + 76 | scoped_ptr 77 | GpuVideoDecodeAccelerator::CreateV4L2VDA() { 78 | scoped_ptr decoder; 79 | @@ -426,6 +426,8 @@ 80 | profiles = VTVideoDecodeAccelerator::GetSupportedProfiles(); 81 | #elif defined(OS_ANDROID) 82 | profiles = AndroidVideoDecodeAccelerator::GetSupportedProfiles(); 83 | +#elif defined(IMX_PLATFORM) 84 | + profiles = ImxVpuVideoDecodeAccelerator::GetSupportedProfiles(); 85 | #endif 86 | return GpuVideoAcceleratorUtil::ConvertMediaToGpuDecodeProfiles(profiles); 87 | } 88 | --- a/content/content_common.gypi 89 | +++ b/content/content_common.gypi 90 | @@ -802,6 +802,40 @@ 91 | 'common/gpu/media/avda_state_provider.h', 92 | ], 93 | }], 94 | + ['target_arch == "arm" and imx_platform == 1', { 95 | + 'dependencies': [ 96 | + '../ui/gl/gl.gyp:gl', 97 | + ], 98 | + 'variables': { 99 | + 'conditions': [ 100 | + ['sysroot!=""', { 101 | + 'pkg-config': '../build/linux/pkg-config-wrapper "<(sysroot)" "<(target_arch)"', 102 | + }, { 103 | + 'pkg-config': 'pkg-config' 104 | + }], 105 | + ], 106 | + }, 107 | + 'sources': [ 108 | + 'common/gpu/media/imx_gl_viv_direct_texture.cc', 109 | + 'common/gpu/media/imx_gl_viv_direct_texture.h', 110 | + 'common/gpu/media/imxvpucodec_fslwrapper.c', 111 | + 'common/gpu/media/imxvpucodec.h', 112 | + 'common/gpu/media/imxvpucodec_platform_chromium.cc', 113 | + 'common/gpu/media/imxvpucodec_platform_chromium.h', 114 | + 'common/gpu/media/imxvpucodec_platform.h', 115 | + 'common/gpu/media/imxvpu_video_decode_accelerator.cc', 116 | + 'common/gpu/media/imxvpu_video_decode_accelerator.h', 117 | + ], 118 | + 'defines': ['IMX_PLATFORM'], 119 | + 'cflags': [ 120 | + ' 3 | Date: Thu, 24 Jul 2014 00:58:07 +0200 4 | Subject: [PATCH] Modify eglwayland versions for Vivante GPUs 5 | 6 | Signed-off-by: Carlos Rafael Giani 7 | --- 8 | ozone/wayland/wayland.gyp | 7 ++++--- 9 | 1 file changed, 4 insertions(+), 3 deletions(-) 10 | 11 | diff --git a/ozone/wayland/wayland.gyp b/ozone/wayland/wayland.gyp 12 | index 062573e..8474546 100644 13 | --- a/ozone/wayland/wayland.gyp 14 | +++ b/ozone/wayland/wayland.gyp 15 | @@ -26,12 +26,13 @@ 16 | 'type': 'static_library', 17 | 'variables': { 18 | 'WAYLAND_VERSION': '1.4.0', 19 | - 'MESA_VERSION': '9.1.3', 20 | + 'WAYLAND_EGL_VERSION': '1.0.0', 21 | + 'VIVEGL_VERSION': '8.0', 22 | 'wayland_packages': [ 23 | - 'egl >= <(MESA_VERSION)', 24 | + 'egl >= <(VIVEGL_VERSION)', 25 | 'wayland-client >= <(WAYLAND_VERSION)', 26 | 'wayland-cursor >= <(WAYLAND_VERSION)', 27 | - 'wayland-egl >= <(MESA_VERSION)', 28 | + 'wayland-egl >= <(WAYLAND_EGL_VERSION)', 29 | 'xkbcommon', 30 | ], 31 | }, 32 | -- 33 | 1.8.3.2 34 | 35 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imx_gl_viv_direct_texture.cc: -------------------------------------------------------------------------------- 1 | #include "ui/gl/gl_implementation.h" 2 | #include "imx_gl_viv_direct_texture.h" 3 | 4 | 5 | bool init_viv_direct_texture(gfx::GLContext &context, GLESVIVDirectTextureProcs &procs) 6 | { 7 | VLOG(1) << "Initializing Vivante direct texture GLES extension"; 8 | 9 | gfx::GLImplementation glimpl = gfx::GetGLImplementation(); 10 | if (glimpl != gfx::kGLImplementationEGLGLES2) 11 | { 12 | LOG(INFO) << "Cannot initialize direct textures - GL implementation is " 13 | << gfx::GetGLImplementationName(glimpl) 14 | << ", expected " << 15 | gfx::GetGLImplementationName(gfx::kGLImplementationEGLGLES2); 16 | return false; 17 | } 18 | 19 | // Newer Vivante drivers call the extension GL_VIV_tex_direct instead of GL_VIV_direct_texture, 20 | // even though it is the same extension 21 | if (context.HasExtension("GL_VIV_direct_texture")) 22 | VLOG(1) << "GL_VIV_direct_texture supported"; 23 | else if (context.HasExtension("GL_VIV_tex_direct")) 24 | VLOG(1) << "GL_VIV_tex_direct supported"; 25 | else 26 | { 27 | VLOG(1) << "Neither GL_VIV_direct_texture nor GL_VIV_tex_direct supported"; 28 | return false; 29 | } 30 | 31 | procs.TexDirectVIV = reinterpret_cast < PFNGLTEXDIRECTVIVPROC > (gfx::GetGLProcAddress("glTexDirectVIV")); 32 | procs.TexDirectVIVMap = reinterpret_cast < PFNGLTEXDIRECTVIVMAPPROC > (gfx::GetGLProcAddress("glTexDirectVIVMap")); 33 | procs.TexDirectTiledMapVIV = reinterpret_cast < PFNGLTEXDIRECTTILEDMAPVIVPROC > (gfx::GetGLProcAddress("glTexDirectTiledMapVIV")); 34 | procs.TexDirectInvalidateVIV = reinterpret_cast < PFNGLTEXDIRECTINVALIDATEVIVPROC > (gfx::GetGLProcAddress("glTexDirectInvalidateVIV")); 35 | 36 | return true; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imx_gl_viv_direct_texture.h: -------------------------------------------------------------------------------- 1 | #ifndef IMX_GL_VIV_DIRECT_TEXTURE_H 2 | #define IMX_GL_VIV_DIRECT_TEXTURE_H 3 | 4 | #include "ui/gl/gl_bindings.h" 5 | #include "ui/gl/gl_context.h" 6 | 7 | 8 | /* GL_VIV_direct_texture */ 9 | #ifndef GL_VIV_direct_texture 10 | #define GL_VIV_YV12 0x8FC0 11 | #define GL_VIV_NV12 0x8FC1 12 | #define GL_VIV_YUY2 0x8FC2 13 | #define GL_VIV_UYVY 0x8FC3 14 | #define GL_VIV_NV21 0x8FC4 15 | #define GL_VIV_I420 0x8FC5 16 | #endif 17 | 18 | 19 | #ifndef GL_APICALL 20 | #define GL_APICALL KHRONOS_APICALL 21 | #endif 22 | 23 | #ifndef GL_APIENTRY 24 | #define GL_APIENTRY KHRONOS_APIENTRY 25 | #endif 26 | 27 | #ifndef GL_APIENTRYP 28 | #define GL_APIENTRYP GL_APIENTRY* 29 | #endif 30 | 31 | 32 | /* GL_VIV_direct_texture */ 33 | #ifndef GL_VIV_direct_texture 34 | #define GL_VIV_direct_texture 1 35 | 36 | typedef void (GL_APIENTRYP PFNGLTEXDIRECTVIVPROC) (GLenum Target, GLsizei Width, GLsizei Height, GLenum Format, GLvoid ** Pixels); 37 | typedef void (GL_APIENTRYP PFNGLTEXDIRECTVIVMAPPROC) (GLenum Target, GLsizei Width, GLsizei Height, GLenum Format, GLvoid ** Logical, const GLuint * Physical); 38 | typedef void (GL_APIENTRYP PFNGLTEXDIRECTTILEDMAPVIVPROC) (GLenum Target, GLsizei Width, GLsizei Height, GLenum Format, GLvoid ** Logical, const GLuint * Physical); 39 | typedef void (GL_APIENTRYP PFNGLTEXDIRECTINVALIDATEVIVPROC) (GLenum Target); 40 | 41 | #endif 42 | 43 | struct GLESVIVDirectTextureProcs 44 | { 45 | PFNGLTEXDIRECTVIVPROC TexDirectVIV; 46 | PFNGLTEXDIRECTVIVMAPPROC TexDirectVIVMap; 47 | PFNGLTEXDIRECTTILEDMAPVIVPROC TexDirectTiledMapVIV; 48 | PFNGLTEXDIRECTINVALIDATEVIVPROC TexDirectInvalidateVIV; 49 | }; 50 | 51 | 52 | bool init_viv_direct_texture(gfx::GLContext &context, GLESVIVDirectTextureProcs &procs); 53 | 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imxvpu_video_decode_accelerator.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "media/base/limits.h" 3 | #include "base/bind.h" 4 | #include "base/memory/singleton.h" 5 | #include "content/common/gpu/media/imxvpu_video_decode_accelerator.h" 6 | #include "ui/gl/gl_bindings.h" 7 | #include "ui/gl/gl_implementation.h" 8 | 9 | 10 | namespace content 11 | { 12 | 13 | 14 | namespace 15 | { 16 | 17 | 18 | class ImxVpuLoadSingleton 19 | { 20 | public: 21 | static ImxVpuLoadSingleton* GetInstance() 22 | { 23 | return base::Singleton < ImxVpuLoadSingleton > ::get(); 24 | } 25 | 26 | bool Load() 27 | { 28 | base::AutoLock auto_lock(lock_); 29 | 30 | ImxVpuDecReturnCodes ret; 31 | 32 | if ((ret = imx_vpu_dec_load()) != IMX_VPU_DEC_RETURN_CODE_OK) 33 | { 34 | LOG(ERROR) << "Could not load VPU: " << imx_vpu_dec_error_string(ret); 35 | return false; 36 | } 37 | else 38 | return true; 39 | } 40 | 41 | bool Unload() 42 | { 43 | base::AutoLock auto_lock(lock_); 44 | 45 | ImxVpuDecReturnCodes ret; 46 | 47 | if ((ret = imx_vpu_dec_unload()) != IMX_VPU_DEC_RETURN_CODE_OK) 48 | { 49 | LOG(ERROR) << "Could not unload VPU: " << imx_vpu_dec_error_string(ret); 50 | return false; 51 | } 52 | else 53 | return true; 54 | } 55 | 56 | private: 57 | ImxVpuLoadSingleton() 58 | { 59 | } 60 | 61 | friend struct base::DefaultSingletonTraits < ImxVpuLoadSingleton >; 62 | 63 | DISALLOW_COPY_AND_ASSIGN(ImxVpuLoadSingleton); 64 | 65 | base::Lock lock_; 66 | }; 67 | 68 | 69 | } // unnamed namespace end 70 | 71 | static const media::VideoCodecProfile kSupportedProfiles[] = { 72 | media::H264PROFILE_BASELINE, 73 | media::H264PROFILE_MAIN, 74 | media::H264PROFILE_HIGH, 75 | media::H264PROFILE_MAX, 76 | media::VP8PROFILE_ANY, 77 | }; 78 | 79 | 80 | ImxVpuVideoDecodeAccelerator::ImxVpuVideoDecodeAccelerator(base::WeakPtr < gpu::gles2::GLES2Decoder > const gles2_decoder, base::Callback < bool(void) > const &make_context_current) 81 | : gles2_decoder_(gles2_decoder) 82 | , make_context_current_(make_context_current) 83 | , vpu_decoder_(NULL) 84 | , initial_info_received_(false) 85 | , message_loop_(base::MessageLoop::current()) 86 | { 87 | vpu_bitstream_buffer_block_.virtual_address = NULL; 88 | } 89 | 90 | 91 | ImxVpuVideoDecodeAccelerator::~ImxVpuVideoDecodeAccelerator() 92 | { 93 | DCHECK_EQ(message_loop_, base::MessageLoop::current()); 94 | } 95 | 96 | 97 | bool ImxVpuVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile, Client *client) 98 | { 99 | gfx::GLContext *context; 100 | 101 | DCHECK_EQ(message_loop_, base::MessageLoop::current()); 102 | 103 | client_ptr_factory_.reset(new base::WeakPtrFactory < Client > (client)); 104 | client_ = client_ptr_factory_->GetWeakPtr(); 105 | 106 | base::AutoLock auto_lock(lock_); 107 | 108 | LOG(INFO) << "Initializing i.MX VPU decoder"; 109 | 110 | if (!make_context_current_.Run()) 111 | { 112 | LOG(ERROR) << "Failed to make this decoder's GL context current."; 113 | return false; 114 | } 115 | 116 | if ((context = gles2_decoder_->GetGLContext()) == NULL) 117 | { 118 | LOG(ERROR) << "GLES2 context is NULL"; 119 | return false; 120 | } 121 | 122 | if (!init_viv_direct_texture(*context, direct_texture_procs_)) 123 | { 124 | LOG(ERROR) << "Initializing the direct texture extension failed"; 125 | return false; 126 | } 127 | 128 | if ((profile >= media::H264PROFILE_MIN) && (profile <= media::H264PROFILE_MAX)) 129 | { 130 | codec_format_ = IMX_VPU_CODEC_FORMAT_H264; 131 | VLOG(1) << "Setting h.264 as codec format"; 132 | } 133 | else if ((profile >= media::VP8PROFILE_MIN) && (profile <= media::VP8PROFILE_MAX)) 134 | { 135 | codec_format_ = IMX_VPU_CODEC_FORMAT_VP8; 136 | VLOG(1) << "Setting VP8 as codec format"; 137 | } 138 | else 139 | { 140 | VLOG(1) << "Unsupported profile"; 141 | return false; 142 | } 143 | 144 | VLOG(1) << "Loading VPU"; 145 | if (!(ImxVpuLoadSingleton::GetInstance()->Load())) 146 | return false; 147 | 148 | if (!AllocateVpuBitstreamBuffer()) 149 | { 150 | ImxVpuLoadSingleton::GetInstance()->Unload(); 151 | return false; 152 | } 153 | 154 | VLOG(1) << "Opening decoder"; 155 | if (!OpenDecoder()) 156 | { 157 | ImxVpuLoadSingleton::GetInstance()->Unload(); 158 | return false; 159 | } 160 | 161 | VLOG(1) << "Initialization done"; 162 | 163 | return true; 164 | } 165 | 166 | 167 | void ImxVpuVideoDecodeAccelerator::Decode(media::BitstreamBuffer const &bitstream_buffer) 168 | { 169 | VLOG(3) << "Decoding bitstream buffer"; 170 | 171 | base::AutoLock auto_lock(lock_); 172 | 173 | input_queue_.push(bitstream_buffer); 174 | ProcessQueuedInput(); 175 | } 176 | 177 | 178 | void ImxVpuVideoDecodeAccelerator::AssignPictureBuffers(std::vector < media::PictureBuffer > const &buffers) 179 | { 180 | DCHECK(output_picture_buffers_.empty()); 181 | DCHECK(buffers.size() == vpu_framebuffers_.size()); 182 | 183 | base::AutoLock auto_lock(lock_); 184 | 185 | VLOG(1) << buffers.size() << " picture buffers are being provided by the client"; 186 | 187 | 188 | // without this call, the GL calls below would not use the correct context 189 | make_context_current_.Run(); 190 | 191 | for (size_t i = 0; i < buffers.size(); ++i) 192 | { 193 | int32 id = buffers[i].id(); 194 | 195 | output_picture_buffers_.insert(std::make_pair(id, buffers[i])); 196 | 197 | ImxVpuFramebuffer &framebuffer = vpu_framebuffers_[i]; 198 | framebuffer.user_data = reinterpret_cast < void* > (id); 199 | 200 | // associate VIV direct textures with VPU framebuffers (one texture per framebuffer) 201 | // by mapping the framebuffer to the direct texture 202 | // only needs to be done once, since this mapping doesn't change 203 | GLuint picture_buffer_texture_id = buffers[i].texture_id(); 204 | glActiveTexture(GL_TEXTURE0); 205 | glBindTexture(GL_TEXTURE_2D, picture_buffer_texture_id); 206 | 207 | GLvoid *virt_addr = framebuffer.virtual_address; 208 | GLuint phys_addr = reinterpret_cast < GLuint > (framebuffer.physical_address); 209 | 210 | direct_texture_procs_.TexDirectVIVMap( 211 | GL_TEXTURE_2D, 212 | aligned_width_, aligned_height_, 213 | GL_VIV_I420, 214 | &virt_addr, &phys_addr 215 | ); 216 | 217 | VLOG(1) 218 | << "Associating picture buffer " << i << "/" << buffers.size() << " ID " << id << " with framebuffer #" << i << std::hex 219 | << " virtual address " << std::setfill('0') << std::setw(8) << reinterpret_cast < void* > (virt_addr) 220 | << " physical address " << std::setfill('0') << std::setw(8) << reinterpret_cast < void* > (phys_addr) 221 | << std::dec; 222 | } 223 | 224 | 225 | ProcessQueuedInput(); 226 | } 227 | 228 | 229 | void ImxVpuVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) 230 | { 231 | base::AutoLock auto_lock(lock_); 232 | 233 | VLOG(3) << "Reusing picture buffer with ID " << picture_buffer_id; 234 | 235 | for (size_t i = 0; i < vpu_framebuffers_.size(); ++i) 236 | { 237 | ImxVpuFramebuffer &framebuffer = vpu_framebuffers_[i]; 238 | int32 id = reinterpret_cast < int32 > (framebuffer.user_data); 239 | if (id == picture_buffer_id) 240 | { 241 | ImxVpuDecReturnCodes ret; 242 | 243 | if ((ret = imx_vpu_dec_mark_framebuffer_as_displayed(vpu_decoder_, &framebuffer)) != IMX_VPU_DEC_RETURN_CODE_OK) 244 | { 245 | LOG(ERROR) << "Marking framebuffer for picture buffer with ID " << picture_buffer_id << "as displayed failed : " << imx_vpu_dec_error_string(ret); 246 | } 247 | else 248 | ProcessQueuedInput(); 249 | 250 | return; 251 | } 252 | } 253 | 254 | LOG(WARNING) << "Picture buffer ID " << picture_buffer_id << " could not be associated with a framebuffer"; 255 | } 256 | 257 | 258 | void ImxVpuVideoDecodeAccelerator::Flush() 259 | { 260 | base::AutoLock auto_lock(lock_); 261 | 262 | VLOG(2) << "Flush: processing all currently queued input bitstream buffers"; 263 | ProcessQueuedInput(); 264 | 265 | VLOG(2) << "Flush: draining VPU decoder"; 266 | imx_vpu_dec_set_drain_mode(vpu_decoder_, 1); 267 | while (true) 268 | { 269 | ProcessRetval pretval = ProcessInput(NULL); 270 | if (pretval != ProcessOK) 271 | break; 272 | // TODO: handle ProcessFail (ProcessEOS should just cause the loop to terminate) 273 | } 274 | imx_vpu_dec_set_drain_mode(vpu_decoder_, 0); 275 | 276 | VLOG(2) << "Flush: done"; 277 | base::MessageLoop::current()->PostTask( 278 | FROM_HERE, 279 | base::Bind( 280 | &Client::NotifyFlushDone, 281 | client_ 282 | ) 283 | ); 284 | 285 | } 286 | 287 | 288 | void ImxVpuVideoDecodeAccelerator::Reset() 289 | { 290 | base::AutoLock auto_lock(lock_); 291 | 292 | VLOG(2) << "Reset: flushing decoder"; 293 | imx_vpu_dec_flush(vpu_decoder_); 294 | 295 | VLOG(2) << "Reset: ending all queued bitstream buffers"; 296 | while (!input_queue_.empty()) 297 | { 298 | int32 bitstream_buffer_id = input_queue_.front().id(); 299 | input_queue_.pop(); 300 | 301 | if (bitstream_buffer_id != -1) 302 | { 303 | base::MessageLoop::current()->PostTask( 304 | FROM_HERE, 305 | base::Bind( 306 | &Client::NotifyEndOfBitstreamBuffer, 307 | client_, bitstream_buffer_id 308 | ) 309 | ); 310 | } 311 | } 312 | 313 | BitstreamBufferQueue empty_queue; 314 | std::swap(input_queue_, empty_queue); 315 | 316 | VLOG(2) << "Reset: done"; 317 | base::MessageLoop::current()->PostTask( 318 | FROM_HERE, 319 | base::Bind( 320 | &Client::NotifyResetDone, 321 | client_ 322 | ) 323 | ); 324 | } 325 | 326 | 327 | void ImxVpuVideoDecodeAccelerator::Destroy() 328 | { 329 | DCHECK_EQ(message_loop_, base::MessageLoop::current()); 330 | Cleanup(); 331 | delete this; 332 | } 333 | 334 | 335 | // static 336 | media::VideoDecodeAccelerator::SupportedProfiles 337 | ImxVpuVideoDecodeAccelerator::GetSupportedProfiles() 338 | { 339 | SupportedProfiles profiles; 340 | for (const auto& supported_profile : kSupportedProfiles) { 341 | SupportedProfile profile; 342 | profile.profile = supported_profile; 343 | profile.min_resolution.SetSize(64, 64); 344 | profile.max_resolution.SetSize(1920, 1088); 345 | profiles.push_back(profile); 346 | } 347 | return profiles; 348 | } 349 | 350 | 351 | bool ImxVpuVideoDecodeAccelerator::CanDecodeOnIOThread() 352 | { 353 | return false; 354 | } 355 | 356 | 357 | void ImxVpuVideoDecodeAccelerator::Cleanup() 358 | { 359 | DCHECK_EQ(message_loop_, base::MessageLoop::current()); 360 | 361 | base::AutoLock auto_lock(lock_); 362 | client_ptr_factory_.reset(); 363 | 364 | CloseDecoder(); 365 | 366 | DeallocateVpuBitstreamBuffer(); 367 | 368 | ImxVpuLoadSingleton::GetInstance()->Unload(); 369 | } 370 | 371 | 372 | bool ImxVpuVideoDecodeAccelerator::OpenDecoder() 373 | { 374 | lock_.AssertAcquired(); 375 | 376 | ImxVpuDecOpenParams open_params; 377 | 378 | open_params.codec_format = codec_format_; 379 | 380 | open_params.enable_frame_reordering = (codec_format_ == IMX_VPU_CODEC_FORMAT_H264) ? 1 : 0; 381 | 382 | // frame width & height are read from the bitstream 383 | open_params.frame_width = 0; 384 | open_params.frame_height = 0; 385 | 386 | if (imx_vpu_dec_open(&(vpu_decoder_), &open_params, vpu_bitstream_buffer_block_.virtual_address, vpu_bitstream_buffer_block_.physical_address) != IMX_VPU_DEC_RETURN_CODE_OK) 387 | return false; 388 | 389 | return true; 390 | } 391 | 392 | 393 | void ImxVpuVideoDecodeAccelerator::CloseDecoder() 394 | { 395 | lock_.AssertAcquired(); 396 | 397 | if (vpu_decoder_ == NULL) 398 | return; 399 | 400 | imx_vpu_dec_close(vpu_decoder_); 401 | DeallocateVpuFramebuffers(); 402 | 403 | vpu_decoder_ = NULL; 404 | } 405 | 406 | 407 | bool ImxVpuVideoDecodeAccelerator::AllocateVpuBitstreamBuffer() 408 | { 409 | lock_.AssertAcquired(); 410 | 411 | ImxVpuDecReturnCodes ret; 412 | 413 | imx_vpu_dec_get_bitstream_buffer_info(&(vpu_bitstream_buffer_block_.alignment), &(vpu_bitstream_buffer_block_.size)); 414 | if ((ret = imx_vpu_dec_allocate_memory(&vpu_bitstream_buffer_block_)) != IMX_VPU_DEC_RETURN_CODE_OK) 415 | { 416 | LOG(ERROR) << "Allocating VPU bitstream buffer failed: " << imx_vpu_dec_error_string(ret); 417 | return false; 418 | } 419 | else 420 | return true; 421 | } 422 | 423 | 424 | bool ImxVpuVideoDecodeAccelerator::DeallocateVpuBitstreamBuffer() 425 | { 426 | lock_.AssertAcquired(); 427 | 428 | ImxVpuDecReturnCodes ret; 429 | 430 | if (vpu_bitstream_buffer_block_.virtual_address == NULL) 431 | return true; 432 | 433 | if ((ret = imx_vpu_dec_deallocate_memory(&vpu_bitstream_buffer_block_)) != IMX_VPU_DEC_RETURN_CODE_OK) 434 | { 435 | LOG(ERROR) << "Deallocating VPU bitstream buffer failed: " << imx_vpu_dec_error_string(ret); 436 | return false; 437 | } 438 | else 439 | return true; 440 | } 441 | 442 | 443 | bool ImxVpuVideoDecodeAccelerator::AllocateAndRegisterVPUFramebuffers() 444 | { 445 | lock_.AssertAcquired(); 446 | 447 | ImxVpuDecReturnCodes ret; 448 | unsigned int y_stride, cbcr_stride, y_size, cbcr_size, mvcol_size, total_size; 449 | 450 | aligned_width_ = vpu_dec_initial_info_.frame_width; 451 | aligned_height_ = vpu_dec_initial_info_.frame_height; 452 | 453 | imx_vpu_dec_calc_framebuffer_sizes( 454 | &vpu_dec_initial_info_, 455 | &aligned_width_, &aligned_height_, 456 | &y_stride, &cbcr_stride, 457 | &y_size, &cbcr_size, 458 | &mvcol_size, 459 | &total_size 460 | ); 461 | 462 | vpu_framebuffers_.resize(vpu_dec_initial_info_.min_num_required_framebuffers + media::limits::kMaxVideoFrames); 463 | vpu_framebuffer_mem_blocks_.resize(vpu_framebuffers_.size()); 464 | memset(&(vpu_framebuffers_[0]), 0, sizeof(ImxVpuFramebuffer) * vpu_framebuffers_.size()); 465 | memset(&(vpu_framebuffer_mem_blocks_[0]), 0, sizeof(ImxVpuMemBlock) * vpu_framebuffers_.size()); 466 | 467 | for (unsigned int i = 0; i < vpu_framebuffers_.size(); ++i) 468 | { 469 | ImxVpuFramebuffer &framebuffer = vpu_framebuffers_[i]; 470 | ImxVpuMemBlock &memblock = vpu_framebuffer_mem_blocks_[i]; 471 | 472 | memblock.size = total_size; 473 | memblock.alignment = vpu_dec_initial_info_.framebuffer_alignment; 474 | if (imx_vpu_dec_allocate_memory(&memblock) != IMX_VPU_DEC_RETURN_CODE_OK) 475 | { 476 | LOG(ERROR) << "Could not allocate framebuffer memory for framebuffer " << i << "/" << vpu_framebuffers_.size(); 477 | memblock.physical_address = 0; 478 | return false; 479 | } 480 | 481 | framebuffer.y_stride = y_stride; 482 | framebuffer.cbcr_stride = cbcr_stride; 483 | framebuffer.virtual_address = memblock.virtual_address; 484 | framebuffer.physical_address = memblock.physical_address; 485 | framebuffer.y_offset = 0; 486 | framebuffer.cb_offset = y_size; 487 | framebuffer.cr_offset = y_size + cbcr_size; 488 | framebuffer.mvcol_offset = y_size + cbcr_size * 2; 489 | } 490 | 491 | if ((ret = imx_vpu_dec_register_framebuffers(vpu_decoder_, &(vpu_framebuffers_[0]), vpu_framebuffers_.size())) != IMX_VPU_DEC_RETURN_CODE_OK) 492 | { 493 | LOG(ERROR) << "Registering framebuffers failed: " << imx_vpu_dec_error_string(ret); 494 | return false; 495 | } 496 | else 497 | return true; 498 | } 499 | 500 | 501 | bool ImxVpuVideoDecodeAccelerator::DeallocateVpuFramebuffers() 502 | { 503 | lock_.AssertAcquired(); 504 | 505 | for (unsigned int i = 0; i < vpu_framebuffer_mem_blocks_.size(); ++i) 506 | { 507 | ImxVpuMemBlock &memblock = vpu_framebuffer_mem_blocks_[i]; 508 | if (memblock.physical_address != 0) 509 | { 510 | if (imx_vpu_dec_deallocate_memory(&memblock) != IMX_VPU_DEC_RETURN_CODE_OK) 511 | { 512 | LOG(ERROR) << "Deallocating memory block of framebuffer " << i << "/" << vpu_framebuffer_mem_blocks_.size() << " failed"; 513 | } 514 | } 515 | } 516 | vpu_framebuffer_mem_blocks_.clear(); 517 | 518 | return true; 519 | } 520 | 521 | 522 | void ImxVpuVideoDecodeAccelerator::ProcessQueuedInput() 523 | { 524 | lock_.AssertAcquired(); 525 | 526 | VLOG(1) << "Input queue size: " << input_queue_.size(); 527 | 528 | { 529 | if (input_queue_.empty()) 530 | { 531 | VLOG(1) << "Input queue empty - nothing to process"; 532 | return; 533 | } 534 | 535 | if (initial_info_received_ && output_picture_buffers_.empty()) 536 | { 537 | VLOG(1) << "No picture buffers have been provided yet - will try again later"; 538 | return; 539 | } 540 | 541 | if (initial_info_received_ && (imx_vpu_dec_get_num_free_framebuffers(vpu_decoder_) < imx_vpu_dec_get_min_num_free_required(vpu_decoder_))) 542 | { 543 | VLOG(1) << "Not enough free framebuffers available - will try again later"; 544 | return; 545 | } 546 | 547 | media::BitstreamBuffer const &queued_bitstream_buffer = input_queue_.front(); 548 | ProcessRetval ret = ProcessInput(&queued_bitstream_buffer); 549 | input_queue_.pop(); 550 | 551 | if (ret != ProcessOK) 552 | return; 553 | } 554 | } 555 | 556 | 557 | ImxVpuVideoDecodeAccelerator::ProcessRetval ImxVpuVideoDecodeAccelerator::ProcessInput(media::BitstreamBuffer const *input_bitstream_buffer) 558 | { 559 | ImxVpuDecReturnCodes ret; 560 | 561 | lock_.AssertAcquired(); 562 | 563 | 564 | ImxVpuEncodedFrame encoded_frame; 565 | scoped_ptr < base::SharedMemory > shm; 566 | 567 | // bitstream_buffer is NULL while draining 568 | if (input_bitstream_buffer != NULL) 569 | { 570 | VLOG(3) << "Processing input bitstream buffer with ID " << input_bitstream_buffer->id(); 571 | 572 | shm.reset(new base::SharedMemory(input_bitstream_buffer->handle(), true)); 573 | // The shared memory block is automatically unmapped by the destructor 574 | shm->Map(input_bitstream_buffer->size()); 575 | 576 | // The stored bitstream IDs are incremented to make debug output clearer 577 | // (otherwise, the imxvpucodec debug printout will print (nil) for the ID 0, 578 | // 0x1 for the ID 1 etc.) 579 | encoded_frame.virtual_address = shm->memory(); 580 | encoded_frame.data_size = input_bitstream_buffer->size(); 581 | encoded_frame.user_data = reinterpret_cast < void* > (input_bitstream_buffer->id() + 1); 582 | 583 | VLOG(3) << "Creating encoded frame (data address " << encoded_frame.virtual_address << " size " << encoded_frame.data_size << " id " << input_bitstream_buffer->id(); 584 | } 585 | else 586 | { 587 | VLOG(2) << "Setting input data to NULL"; 588 | 589 | encoded_frame.virtual_address = NULL; 590 | encoded_frame.data_size = 0; 591 | encoded_frame.user_data = NULL; 592 | } 593 | 594 | encoded_frame.codec_data = NULL; 595 | encoded_frame.codec_data_size = 0; 596 | 597 | 598 | unsigned int output_code; 599 | if ((ret = imx_vpu_dec_decode_frame(vpu_decoder_, &encoded_frame, &output_code)) != IMX_VPU_DEC_RETURN_CODE_OK) 600 | { 601 | LOG(ERROR) << "Decoding frame failed: " << imx_vpu_dec_error_string(ret); 602 | return ProcessFailed; 603 | } 604 | 605 | VLOG(1) << "Output code of decoded frame: 0x" << std::hex << output_code; 606 | 607 | if (input_bitstream_buffer != NULL) 608 | { 609 | base::MessageLoop::current()->PostTask( 610 | FROM_HERE, 611 | base::Bind( 612 | &Client::NotifyEndOfBitstreamBuffer, 613 | client_, 614 | input_bitstream_buffer->id() 615 | ) 616 | ); 617 | } 618 | 619 | if (output_code & (IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE | IMX_VPU_DEC_OUTPUT_CODE_RESOLUTION_CHANGED)) 620 | { 621 | VLOG(1) << "Initial information is available - retrieving"; 622 | 623 | if ((ret = imx_vpu_dec_get_initial_info(vpu_decoder_, &vpu_dec_initial_info_)) != IMX_VPU_DEC_RETURN_CODE_OK) 624 | { 625 | LOG(ERROR) << "Retrieving initial info failed: " << imx_vpu_dec_error_string(ret); 626 | return ProcessFailed; 627 | } 628 | 629 | VLOG(1) << "Initial info: frame size " << vpu_dec_initial_info_.frame_width << "x" << vpu_dec_initial_info_.frame_height << " min num required framebuffers: " << vpu_dec_initial_info_.min_num_required_framebuffers; 630 | 631 | if ( 632 | !DeallocateVpuFramebuffers() || 633 | !AllocateAndRegisterVPUFramebuffers() 634 | ) 635 | return ProcessFailed; 636 | 637 | initial_info_received_ = true; 638 | 639 | base::MessageLoop::current()->PostTask( 640 | FROM_HERE, 641 | base::Bind( 642 | &Client::ProvidePictureBuffers, 643 | client_, 644 | vpu_framebuffers_.size(), 645 | gfx::Size(aligned_width_, aligned_height_), 646 | GL_TEXTURE_2D 647 | ) 648 | ); 649 | } 650 | 651 | ProcessRetval pretval = (output_code & IMX_VPU_DEC_OUTPUT_CODE_EOS) ? ProcessEOS : ProcessOK; 652 | 653 | int32 bitstream_buffer_id = 0; 654 | if (output_code & IMX_VPU_DEC_OUTPUT_CODE_FRAME_OUTPUT) 655 | { 656 | ImxVpuDecodedFrame decoded_frame; 657 | if ((ret = imx_vpu_dec_get_decoded_frame(vpu_decoder_, &decoded_frame)) != IMX_VPU_DEC_RETURN_CODE_OK) 658 | { 659 | LOG(ERROR) << "Retrieving decoded frame failed: " << imx_vpu_dec_error_string(ret); 660 | return ProcessFailed; 661 | } 662 | 663 | bitstream_buffer_id = reinterpret_cast < int32 > (decoded_frame.user_data) - 1; 664 | 665 | VLOG(3) << "Decoded frame was retrieved, bitstream buffer id " << bitstream_buffer_id; 666 | 667 | if (decoded_frame.framebuffer == NULL) 668 | { 669 | LOG(ERROR) << "Framebuffer of decoded frame is NULL"; 670 | pretval = ProcessFailed; 671 | } 672 | else 673 | { 674 | if (!ProcessOutput(*(decoded_frame.framebuffer), bitstream_buffer_id)) 675 | { 676 | // if ProcessOutput returns false, then no picture buffer has 677 | // been sent to the client, so the decoded frame must be returned 678 | // to the VPU pool here 679 | VLOG(1) << "ProcessOutput failed -> returning decoded frame to internal VPU pool"; 680 | imx_vpu_dec_mark_framebuffer_as_displayed(vpu_decoder_, decoded_frame.framebuffer); 681 | pretval = ProcessFailed; 682 | } 683 | 684 | // if processing the output was successful, the framebuffer is 685 | // _not_ marked as displayed here; this is done in ReusePictureBuffer(), 686 | // because only then it is certain that the client is done with that frame 687 | } 688 | } 689 | else if (output_code & IMX_VPU_DEC_OUTPUT_CODE_DROPPED) 690 | { 691 | void *user_data = imx_vpu_dec_get_dropped_frame_user_data(vpu_decoder_); 692 | bitstream_buffer_id = reinterpret_cast < int32 > (user_data) - 1; 693 | VLOG(2) << "Frame was dropped, bitstream buffer id " << bitstream_buffer_id; 694 | } 695 | 696 | return pretval; 697 | } 698 | 699 | 700 | bool ImxVpuVideoDecodeAccelerator::ProcessOutput(ImxVpuFramebuffer const &output_framebuffer, int32 input_bitstream_buffer_id) 701 | { 702 | lock_.AssertAcquired(); 703 | 704 | int32 picture_buffer_id = reinterpret_cast < int32 > (output_framebuffer.user_data); 705 | OutputBufferMap::const_iterator iter = output_picture_buffers_.find(picture_buffer_id); 706 | if (iter == output_picture_buffers_.end()) 707 | { 708 | LOG(ERROR) << "No picture buffer with ID " << picture_buffer_id << " found"; 709 | return false; 710 | } 711 | GLuint picture_buffer_texture_id = iter->second.texture_id(); 712 | 713 | make_context_current_.Run(); 714 | 715 | glActiveTexture(GL_TEXTURE0); 716 | glBindTexture(GL_TEXTURE_2D, picture_buffer_texture_id); 717 | 718 | direct_texture_procs_.TexDirectInvalidateVIV(GL_TEXTURE_2D); 719 | 720 | gles2_decoder_->RestoreTextureUnitBindings(0); 721 | gles2_decoder_->RestoreActiveTexture(); 722 | 723 | base::MessageLoop::current()->PostTask( 724 | FROM_HERE, 725 | base::Bind( 726 | &Client::PictureReady, 727 | client_, 728 | media::Picture( 729 | picture_buffer_id, 730 | input_bitstream_buffer_id, 731 | gfx::Rect( 732 | 0, 733 | 0, 734 | vpu_dec_initial_info_.frame_width, 735 | vpu_dec_initial_info_.frame_height 736 | ), 737 | false 738 | ) 739 | ) 740 | ); 741 | 742 | return true; 743 | } 744 | 745 | 746 | } // namespace content 747 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imxvpu_video_decode_accelerator.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTENT_COMMON_GPU_MEDIA_IMXVPU_VIDEO_DECODE_ACCELERATOR_H_ 2 | #define CONTENT_COMMON_GPU_MEDIA_IMXVPU_VIDEO_DECODE_ACCELERATOR_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "base/compiler_specific.h" 9 | #include "base/memory/linked_ptr.h" 10 | #include "base/memory/weak_ptr.h" 11 | #include "base/message_loop/message_loop.h" 12 | #include "base/compiler_specific.h" 13 | #include "base/synchronization/lock.h" 14 | #include "content/common/content_export.h" 15 | #include "gpu/command_buffer/service/gles2_cmd_decoder.h" 16 | #include "media/base/bitstream_buffer.h" 17 | #include "media/video/picture.h" 18 | #include "media/video/video_decode_accelerator.h" 19 | 20 | #include "imxvpucodec.h" 21 | #include "imx_gl_viv_direct_texture.h" 22 | 23 | 24 | namespace content 25 | { 26 | 27 | 28 | class CONTENT_EXPORT ImxVpuVideoDecodeAccelerator 29 | : public media::VideoDecodeAccelerator 30 | { 31 | public: 32 | explicit ImxVpuVideoDecodeAccelerator(base::WeakPtr < gpu::gles2::GLES2Decoder > const gles2_decoder, base::Callback < bool(void) > const &make_context_current); 33 | virtual ~ImxVpuVideoDecodeAccelerator(); 34 | 35 | virtual bool Initialize(media::VideoCodecProfile profile, Client *client) override; 36 | virtual void Decode(media::BitstreamBuffer const &bitstream_buffer) override; 37 | virtual void AssignPictureBuffers(std::vector < media::PictureBuffer > const &buffers) override; 38 | virtual void ReusePictureBuffer(int32 picture_buffer_id) override; 39 | virtual void Flush() override; 40 | virtual void Reset() override; 41 | virtual void Destroy() override; 42 | virtual bool CanDecodeOnIOThread() override; 43 | 44 | static media::VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(); 45 | 46 | private: 47 | enum ProcessRetval 48 | { 49 | ProcessOK, 50 | ProcessEOS, 51 | ProcessFailed 52 | }; 53 | 54 | void Cleanup(); 55 | 56 | // VPU specifics 57 | bool OpenDecoder(); 58 | void CloseDecoder(); 59 | bool AllocateVpuBitstreamBuffer(); 60 | bool DeallocateVpuBitstreamBuffer(); 61 | bool AllocateAndRegisterVPUFramebuffers(); 62 | bool DeallocateVpuFramebuffers(); 63 | 64 | // Bitstream buffer and framebuffer processing 65 | void ProcessQueuedInput(); 66 | ProcessRetval ProcessInput(media::BitstreamBuffer const *input_bitstream_buffer); 67 | bool ProcessOutput(ImxVpuFramebuffer const &output_framebuffer, int32 input_bitstream_buffer_id); 68 | 69 | 70 | scoped_ptr < base::WeakPtrFactory < Client > > client_ptr_factory_; 71 | base::WeakPtr < Client > client_; 72 | 73 | base::WeakPtr < gpu::gles2::GLES2Decoder > const gles2_decoder_; 74 | base::Callback < bool(void) > make_context_current_; 75 | 76 | typedef std::vector < ImxVpuFramebuffer > ImxVpuFramebuffers; 77 | typedef std::vector < ImxVpuMemBlock > ImxVpuMemBlocks; 78 | ImxVpuDecoder *vpu_decoder_; 79 | ImxVpuCodecFormats codec_format_; 80 | ImxVpuMemBlock vpu_bitstream_buffer_block_; 81 | ImxVpuDecInitialInfo vpu_dec_initial_info_; 82 | ImxVpuFramebuffers vpu_framebuffers_; 83 | ImxVpuMemBlocks vpu_framebuffer_mem_blocks_; 84 | unsigned int aligned_width_, aligned_height_; 85 | bool initial_info_received_; 86 | 87 | GLESVIVDirectTextureProcs direct_texture_procs_; 88 | 89 | base::Lock lock_; 90 | 91 | typedef std::queue < media::BitstreamBuffer > BitstreamBufferQueue; 92 | BitstreamBufferQueue input_queue_; 93 | 94 | typedef std::map < int32, media::PictureBuffer > OutputBufferMap; 95 | OutputBufferMap output_picture_buffers_; 96 | 97 | // ChildThread's message loop 98 | base::MessageLoop* message_loop_; 99 | 100 | DISALLOW_COPY_AND_ASSIGN(ImxVpuVideoDecodeAccelerator); 101 | }; 102 | 103 | 104 | } // namespace content end 105 | 106 | 107 | #endif // CONTENT_COMMON_GPU_MEDIA_IMXVPU_VIDEO_DECODE_ACCELERATOR_H_ 108 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imxvpucodec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imxvpucodec - i.MX6 VPU hardware codec engine API library 3 | * Copyright (c) 2014 Carlos Rafael Giani 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any 7 | * damages arising from the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute 11 | * it freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must 14 | * not claim that you wrote the original software. If you use this 15 | * software in a product, an acknowledgment in the product 16 | * documentation would be appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must 19 | * not be misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | 25 | #ifndef IMXVPUCODEC_H 26 | #define IMXVPUCODEC_H 27 | 28 | #include 29 | #include 30 | 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | 37 | 38 | /* This library provides a high-level interface for controlling the Freescale 39 | * i.MX VPU en/decoder. 40 | * Other libraries do not provide a way of associating frames with user defined 41 | * information, and lack calls to check the number of currently free framebuffers 42 | * (when decoding). The former is required by many media frameworks such as 43 | * GStreamer, FFmpeg/libav, the Chromium media codebase etc. The latter is 44 | * necessary when framebuffer display and decoding can happen in different 45 | * threads (the counter makes it possible to use synchronization primitives 46 | * like thread condition variables to wait until enough frames are free). 47 | * 48 | * Note that the functions are _not_ thread safe. If they may be called from 49 | * different threads, you must make sure they are surrounded by a mutex lock. 50 | * It is recommended to use one global mutex for the imx_vpu_*_load()/unload() 51 | * functions, and another de/encoder instance specific mutex for all of the other 52 | * calls. 53 | * 54 | * How to use the decoder (error handling omitted for clarity): 55 | * 1. Call imx_vpu_dec_load() 56 | * 2. Call imx_vpu_dec_get_bitstream_buffer_info(), and allocate a DMA buffer 57 | * with the given size and alignment. 58 | * 3. Fill an instance of ImxVpuDecOpenParams with the values specific to the 59 | * input data. In most cases, one wants to set enable_frame_reordering to 1 60 | * with h.264 data here. 61 | * 4. Call imx_vpu_dec_open(), passing in a pointer to the filled ImxVpuDecOpenParams 62 | * instance, and the virtual and physical addresses of the bitstream DMA buffer 63 | * which was allocated in step 2. 64 | * 5. Call imx_vpu_dec_decode_frame() with the first encoded frame. 65 | * If the output_code bitmask contains IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE, 66 | * proceed, otherwise continue feeding in data. 67 | * 6. Once IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE has been set in the output code, 68 | * call imx_vpu_dec_get_initial_info() with a pointer to an ImxVpuDecInitialInfo 69 | * instance. 70 | * 7. (Optional) Perform the necessary size and alignment calculations by calling 71 | * imx_vpu_dec_calc_framebuffer_sizes(). 72 | * 8. Create an array of at least as many ImxVpuFramebuffer instances as specified in 73 | * min_num_required_framebuffers. Each instance must point to a DMA buffer that is big 74 | * enough to hold a frame. If step 7 was performed, allocating as many bytes as indicated 75 | * by total_size is enough. Make sure the Y,Cb,Cr,MvCol offsets in each ImxVpuFramebuffer 76 | * instance are valid. 77 | * 9. Call imx_vpu_dec_register_framebuffers() and pass in the ImxVpuFramebuffer array 78 | * and the number of ImxVpuFramebuffer instances. 79 | * 10. Continue calling imx_vpu_dec_decode_frame(). The virtual address in encoded_frame 80 | * must not be NULL. 81 | * If the IMX_VPU_DEC_OUTPUT_CODE_FRAME_OUTPUT flag is set in the output code, 82 | * call imx_vpu_dec_get_decoded_frame() with a pointer to an ImxVpuDecodedFrame instance 83 | * which gets filled with information about the decoded frame. Once the decoded frame 84 | * has been processed by the user, imx_vpu_dec_mark_framebuffer_as_displayed() must be 85 | * called to let the decoder know that the framebuffer is available for storing new 86 | * decoded frames again. 87 | * If IMX_VPU_DEC_OUTPUT_CODE_DROPPED is set, you can call 88 | * imx_vpu_dec_get_dropped_frame_user_data() to retrieve the user_data field 89 | * of the dropped frame. If IMX_VPU_DEC_OUTPUT_CODE_EOS is set, stop playback and close 90 | * the decoder. 91 | * 11. In case a flush/reset is desired (typically after seeking), call imx_vpu_dec_flush(). 92 | * Note that any internal user_data pointers from the en/decoded frames will be 93 | * set to NULL after this call (this is the only exception where the library modifies 94 | * the user_data fields). 95 | * 12. When there is no more incoming data, and pending decoded frames need to be retrieved 96 | * from the decoder, call imx_vpu_dec_set_drain_mode(). This is typically necessary when 97 | * the data source reached its end, playback is finishing, and there is a delay 98 | * of N frames at the beginning. 99 | * After this call, continue calling imx_vpu_dec_decode_frame() to retrieve the pending 100 | * decoded frames, but the virtual address of encoded_frame must be NULL. 101 | * As in step 10, if IMX_VPU_DEC_OUTPUT_CODE_EOS is set, stop playback, close the decoder. 102 | * 13. After playback is finished, close the decoder with imx_vpu_dec_close(). 103 | * 14. Deallocate framebuffer memory blocks and the bitstream buffer memory block. 104 | * 15. Call imx_vpu_dec_unload(). 105 | * 106 | * Step 15 should only be called if no more playback sessions will occur. 107 | * 108 | * As mentioned before, in situations where decoding and display of decoded frames happen in 109 | * different thread, it is necessary to let the decoder wait until enough framebuffers 110 | * are free (= available for the VPU to decode into). This is typically done by such a check 111 | * (in pseudo code): 112 | * 113 | * mutex_lock(&mutex); 114 | * 115 | * while (imx_vpu_dec_get_num_free_framebuffers(decoder) < imx_vpu_dec_get_min_num_free_required(decoder)) 116 | * condition_wait(&condition_variable, &mutex); 117 | * 118 | * imx_vpu_dec_decode_frame(decoder, encoded_frame, &output_code); 119 | * ... 120 | * 121 | * mutex_unlock(&mutex); 122 | */ 123 | 124 | 125 | 126 | /***********************************************/ 127 | /******* COMMON STRUCTURES AND FUNCTIONS *******/ 128 | /***********************************************/ 129 | 130 | 131 | #define IMX_VPU_ALIGN_VAL_TO(LENGTH, ALIGN_SIZE) ( ((uintptr_t)(((uint8_t*)(LENGTH)) + (ALIGN_SIZE) - 1) / (ALIGN_SIZE)) * (ALIGN_SIZE) ) 132 | 133 | 134 | typedef uint32_t imx_vpu_phys_addr_t; 135 | typedef uint32_t imx_vpu_cpu_addr_t; /* used only in allocators so far */ 136 | 137 | 138 | typedef enum 139 | { 140 | IMX_VPU_PIC_TYPE_UNKNOWN = 0, 141 | IMX_VPU_PIC_TYPE_I, 142 | IMX_VPU_PIC_TYPE_P, 143 | IMX_VPU_PIC_TYPE_B, 144 | IMX_VPU_PIC_TYPE_IDR, 145 | IMX_VPU_PIC_TYPE_BI, 146 | IMX_VPU_PIC_TYPE_SKIP 147 | } 148 | ImxVpuPicType; 149 | 150 | 151 | typedef enum 152 | { 153 | IMX_VPU_CODEC_FORMAT_MPEG2 = 0, /* includes MPEG1 */ 154 | IMX_VPU_CODEC_FORMAT_MPEG4, 155 | IMX_VPU_CODEC_FORMAT_H263, 156 | IMX_VPU_CODEC_FORMAT_H264, 157 | IMX_VPU_CODEC_FORMAT_H264_MVC, 158 | IMX_VPU_CODEC_FORMAT_WMV3, 159 | IMX_VPU_CODEC_FORMAT_WVC1, 160 | IMX_VPU_CODEC_FORMAT_MJPEG, 161 | IMX_VPU_CODEC_FORMAT_VP8 162 | /* XXX others will be added when the firmware supports them */ 163 | } 164 | ImxVpuCodecFormats; 165 | 166 | 167 | typedef enum 168 | { 169 | IMX_VPU_MJPEG_FORMAT_YUV420 = 0, /* also known as I420 */ 170 | IMX_VPU_MJPEG_FORMAT_YUV422_HORIZONTAL = 1, 171 | IMX_VPU_MJPEG_FORMAT_YUV422_VERTICAL = 2, /* 4:2:2 vertical, actually 2:2:4 (according to the VPU docs) */ 172 | IMX_VPU_MJPEG_FORMAT_YUV444 = 3, 173 | IMX_VPU_MJPEG_FORMAT_YUV400 = 4 /* 8-bit grayscale */ 174 | } 175 | ImxVpuMJpegFormat; 176 | 177 | 178 | typedef struct 179 | { 180 | /* Stride of the Y and of the Cb&Cr components. 181 | * Specified in bytes. */ 182 | unsigned int y_stride, cbcr_stride; 183 | 184 | /* The virtual address of is actually not used by the VPU, 185 | * and only of interest to the user. It can be NULL for cases 186 | * where only a physical address exists or where a virtual 187 | * address is not necessary. */ 188 | void *virtual_address; 189 | /* The physical address must always be valid. */ 190 | imx_vpu_phys_addr_t physical_address; 191 | 192 | /* These define the starting offsets of each component 193 | * relative to the start of the buffer. Specified in bytes. 194 | * 195 | * mvcol is the "co-located motion vector" data. */ 196 | size_t 197 | y_offset, 198 | cb_offset, 199 | cr_offset, 200 | mvcol_offset; 201 | 202 | /* User-defined pointer. The library does not touch this value. 203 | * This can be used to identify framebuffers for example. 204 | * Not to be confused with the user_data fields of ImxVpuEncodedFrame 205 | * and ImxVpuDecodedFrame. */ 206 | void *user_data; 207 | 208 | /* Internal, implementation-defined data. Do not modify. */ 209 | void *internal; 210 | } 211 | ImxVpuFramebuffer; 212 | 213 | 214 | typedef struct 215 | { 216 | /* Virtual and physical addresses pointing to the encoded data. 217 | * The virtual address must always be valid. The physical address 218 | * is only required for encoding. */ 219 | void *virtual_address; 220 | imx_vpu_phys_addr_t physical_address; 221 | 222 | /* Size of the encoded data, in bytes. */ 223 | unsigned int data_size; 224 | 225 | /* Pointer to out-of-band codec/header data. If such data exists, 226 | * specify the pointer to the memory block containing the data, 227 | * as well as the size of the memory block (in bytes). 228 | * Set pointer and size for every encoded frame when decoding. 229 | * If no such data exists or is required, set pointer to NULL and 230 | * size to 0. */ 231 | void *codec_data; 232 | unsigned int codec_data_size; 233 | 234 | /* User-defined pointer. The library does not touch this value. 235 | * This pointer and the one from the corresponding 236 | * decoded frame will have the same value. The library will 237 | * pass then through. 238 | * It can be used to identify frames and associated corresponding 239 | * en- and decoded frames for example. */ 240 | void *user_data; 241 | } 242 | ImxVpuEncodedFrame; 243 | 244 | 245 | typedef struct 246 | { 247 | /* When decoding: pointer to the framebuffer containing the decoded frame. 248 | * When encoding: pointer to the framebuffer containing the frame to be encoded. 249 | * Must always be valid. */ 250 | ImxVpuFramebuffer *framebuffer; 251 | 252 | /* picture type (I, P, B, ..) */ 253 | ImxVpuPicType pic_type; 254 | 255 | /* User-defined pointer. The library does not touch this value. 256 | * This pointer and the one from the corresponding 257 | * encoded frame will have the same value. The library will 258 | * pass then through. 259 | * It can be used to identify frames and associated corresponding 260 | * en- and decoded frames for example. */ 261 | void *user_data; 262 | } 263 | ImxVpuDecodedFrame; 264 | 265 | 266 | /* This structure is only used by the allocate/deallocate calls below, 267 | * which in turn are convenience calls that wrap VPU-provided DMA buffer 268 | * allocators. If a different DMA buffer allocator is used (like ION), 269 | * this structure does not have to be used. */ 270 | typedef struct 271 | { 272 | size_t size; 273 | unsigned int alignment; 274 | 275 | void* virtual_address; 276 | imx_vpu_phys_addr_t physical_address; 277 | imx_vpu_cpu_addr_t cpu_address; 278 | 279 | void* virtual_address_unaligned; 280 | imx_vpu_phys_addr_t physical_address_unaligned; 281 | } 282 | ImxVpuMemBlock; 283 | 284 | 285 | 286 | 287 | /************************************************/ 288 | /******* DECODER STRUCTURES AND FUNCTIONS *******/ 289 | /************************************************/ 290 | 291 | 292 | typedef struct _ImxVpuDecoder ImxVpuDecoder; 293 | 294 | 295 | typedef enum 296 | { 297 | IMX_VPU_DEC_RETURN_CODE_OK = 0, 298 | IMX_VPU_DEC_RETURN_CODE_ERROR, 299 | IMX_VPU_DEC_RETURN_CODE_INVALID_PARAMS, 300 | IMX_VPU_DEC_RETURN_CODE_INVALID_HANDLE, 301 | IMX_VPU_DEC_RETURN_CODE_INVALID_FRAMEBUFFER, 302 | IMX_VPU_DEC_RETURN_CODE_INSUFFICIENT_FRAMEBUFFERS, 303 | IMX_VPU_DEC_RETURN_CODE_INVALID_STRIDE, 304 | IMX_VPU_DEC_RETURN_CODE_WRONG_CALL_SEQUENCE, 305 | IMX_VPU_DEC_RETURN_CODE_TIMEOUT 306 | } 307 | ImxVpuDecReturnCodes; 308 | 309 | 310 | typedef enum 311 | { 312 | IMX_VPU_DEC_OUTPUT_CODE_INPUT_USED = (1UL << 0), 313 | IMX_VPU_DEC_OUTPUT_CODE_EOS = (1UL << 1), 314 | IMX_VPU_DEC_OUTPUT_CODE_FRAME_OUTPUT = (1UL << 2), 315 | IMX_VPU_DEC_OUTPUT_CODE_NO_FRAME_OUTPUT = (1UL << 3), 316 | IMX_VPU_DEC_OUTPUT_CODE_DROPPED = (1UL << 4), 317 | IMX_VPU_DEC_OUTPUT_CODE_NOT_ENOUGH_OUTPUT_FRAMES = (1UL << 5), 318 | IMX_VPU_DEC_OUTPUT_CODE_NOT_ENOUGH_INPUT_DATA = (1UL << 6), 319 | IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE = (1UL << 7), 320 | IMX_VPU_DEC_OUTPUT_CODE_RESOLUTION_CHANGED = (1UL << 8), 321 | IMX_VPU_DEC_OUTPUT_CODE_DECODE_ONLY = (1UL << 9), 322 | IMX_VPU_DEC_OUTPUT_CODE_INTERNAL_RESET = (1UL << 10) 323 | } 324 | ImxVpuDecOutputCodes; 325 | 326 | 327 | typedef struct 328 | { 329 | ImxVpuCodecFormats codec_format; 330 | 331 | int enable_frame_reordering; 332 | unsigned int frame_width, frame_height; 333 | } 334 | ImxVpuDecOpenParams; 335 | 336 | 337 | typedef struct 338 | { 339 | /* Width of height of frames, in pixels. */ 340 | unsigned int frame_width, frame_height; 341 | /* Frame rate ratio. */ 342 | unsigned int frame_rate_numerator, frame_rate_denominator; 343 | 344 | /* Caller must register at least this many framebuffers 345 | * with the decoder. */ 346 | unsigned int min_num_required_framebuffers; 347 | 348 | /* Pixel format of the decoded frames. For codec formats 349 | * other than motion JPEG, this value will always be 350 | * IMX_VPU_MJPEG_FORMAT_YUV420. */ 351 | ImxVpuMJpegFormat mjpeg_source_format; 352 | 353 | /* 0 = no interlacing, 1 = interlacing. */ 354 | int interlacing; 355 | 356 | /* Fixed point, shifted by 16. 357 | * Example: 1.0 -> floor(1.0*(1<<16)) = 0x10000 358 | * 0.5 -> floor(0.5*(1<<16)) = 0x8000 */ 359 | unsigned int width_height_ratio; 360 | 361 | /* Physical framebuffer addresses must be aligned to this value. */ 362 | unsigned int framebuffer_alignment; 363 | } 364 | ImxVpuDecInitialInfo; 365 | 366 | 367 | /* Returns a human-readable description of the error code. 368 | * Useful for logging. */ 369 | char const * imx_vpu_dec_error_string(ImxVpuDecReturnCodes code); 370 | 371 | /* These two functions load/unload the decoder. Thanks to an internal reference 372 | * counter, it is safe to call these functions more than once. However, the 373 | * number of unload() calls must match the number of load() calls. 374 | * 375 | * The decoder must be loaded before doing anything else with the decoder. 376 | * Similarly, the decoder must not be unloaded before all decoder activities 377 | * have been finished. This includes opening/decoding decoder instances. */ 378 | ImxVpuDecReturnCodes imx_vpu_dec_load(void); 379 | ImxVpuDecReturnCodes imx_vpu_dec_unload(void); 380 | 381 | /* Convenience allocator for allocating DMA buffers. */ 382 | ImxVpuDecReturnCodes imx_vpu_dec_allocate_memory(ImxVpuMemBlock *mem_block); 383 | ImxVpuDecReturnCodes imx_vpu_dec_deallocate_memory(ImxVpuMemBlock *mem_block); 384 | 385 | /* Called before imx_vpu_dec_open(), it returns the alignment and size for the 386 | * physical memory block necessary for the decoder's bitstream buffer. The user 387 | * must allocate a DMA buffer of at least this size, and its physical address 388 | * must be aligned according to the alignment value. */ 389 | void imx_vpu_dec_get_bitstream_buffer_info(unsigned int *alignment, size_t *size); 390 | 391 | ImxVpuDecReturnCodes imx_vpu_dec_open(ImxVpuDecoder **decoder, ImxVpuDecOpenParams const *open_params, void *bitstream_buffer_virtual_address, imx_vpu_phys_addr_t bitstream_buffer_physical_address); 392 | ImxVpuDecReturnCodes imx_vpu_dec_close(ImxVpuDecoder *decoder); 393 | 394 | ImxVpuDecReturnCodes imx_vpu_dec_set_drain_mode(ImxVpuDecoder *decoder, int enabled); 395 | ImxVpuDecReturnCodes imx_vpu_dec_flush(ImxVpuDecoder *decoder); 396 | 397 | ImxVpuDecReturnCodes imx_vpu_dec_register_framebuffers(ImxVpuDecoder *decoder, ImxVpuFramebuffer *framebuffers, unsigned int num_framebuffers); 398 | void imx_vpu_dec_calc_framebuffer_sizes(ImxVpuDecInitialInfo const *initial_info, unsigned int *frame_width, unsigned int *frame_height, unsigned int *y_stride, unsigned int *cbcr_stride, unsigned int *y_size, unsigned int *cbcr_size, unsigned int *mvcol_size, unsigned int *total_size); 399 | 400 | ImxVpuDecReturnCodes imx_vpu_dec_get_initial_info(ImxVpuDecoder *decoder, ImxVpuDecInitialInfo *info); 401 | 402 | ImxVpuDecReturnCodes imx_vpu_dec_decode_frame(ImxVpuDecoder *decoder, ImxVpuEncodedFrame const *encoded_frame, unsigned int *output_code); 403 | ImxVpuDecReturnCodes imx_vpu_dec_get_decoded_frame(ImxVpuDecoder *decoder, ImxVpuDecodedFrame *decoded_frame); 404 | void* imx_vpu_dec_get_dropped_frame_user_data(ImxVpuDecoder *decoder); 405 | int imx_vpu_dec_get_num_free_framebuffers(ImxVpuDecoder *decoder); 406 | int imx_vpu_dec_get_min_num_free_required(ImxVpuDecoder *decoder); 407 | ImxVpuDecReturnCodes imx_vpu_dec_mark_framebuffer_as_displayed(ImxVpuDecoder *decoder, ImxVpuFramebuffer const *framebuffer); 408 | 409 | 410 | 411 | 412 | #ifdef __cplusplus 413 | } 414 | #endif 415 | 416 | 417 | #endif 418 | 419 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imxvpucodec_fslwrapper.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imxvpucodec - i.MX6 VPU hardware codec engine API library 3 | * Copyright (c) 2014 Carlos Rafael Giani 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any 7 | * damages arising from the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute 11 | * it freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must 14 | * not claim that you wrote the original software. If you use this 15 | * software in a product, an acknowledgment in the product 16 | * documentation would be appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must 19 | * not be misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "imxvpucodec.h" 30 | #include "imxvpucodec_platform.h" 31 | 32 | 33 | 34 | 35 | /***********************************************/ 36 | /******* COMMON STRUCTURES AND FUNCTIONS *******/ 37 | /***********************************************/ 38 | 39 | 40 | #ifndef NULL 41 | #define NULL ((void*)0) 42 | #endif 43 | 44 | 45 | #ifndef TRUE 46 | #define TRUE (1) 47 | #endif 48 | 49 | 50 | #ifndef FALSE 51 | #define FALSE (0) 52 | #endif 53 | 54 | 55 | #ifndef BOOL 56 | #define BOOL int 57 | #endif 58 | 59 | 60 | static ImxVpuMJpegFormat convert_from_wrapper_mjpg_format(int format) 61 | { 62 | return (ImxVpuMJpegFormat)format; 63 | } 64 | 65 | 66 | static ImxVpuPicType convert_from_wrapper_pic_type(VpuPicType type) 67 | { 68 | switch (type) 69 | { 70 | case VPU_I_PIC: return IMX_VPU_PIC_TYPE_I; 71 | case VPU_P_PIC: return IMX_VPU_PIC_TYPE_P; 72 | case VPU_B_PIC: return IMX_VPU_PIC_TYPE_B; 73 | case VPU_IDR_PIC: return IMX_VPU_PIC_TYPE_IDR; 74 | case VPU_BI_PIC: return IMX_VPU_PIC_TYPE_BI; 75 | case VPU_SKIP_PIC: return IMX_VPU_PIC_TYPE_SKIP; 76 | default: return IMX_VPU_PIC_TYPE_UNKNOWN; 77 | } 78 | } 79 | 80 | 81 | static VpuCodStd convert_to_wrapper_codec_std(ImxVpuCodecFormats format) 82 | { 83 | switch (format) 84 | { 85 | case IMX_VPU_CODEC_FORMAT_MPEG4: return VPU_V_MPEG4; 86 | case IMX_VPU_CODEC_FORMAT_H263: return VPU_V_H263; 87 | case IMX_VPU_CODEC_FORMAT_H264: return VPU_V_AVC; 88 | case IMX_VPU_CODEC_FORMAT_H264_MVC: return VPU_V_AVC_MVC; 89 | case IMX_VPU_CODEC_FORMAT_WMV3: return VPU_V_VC1; 90 | case IMX_VPU_CODEC_FORMAT_WVC1: return VPU_V_VC1_AP; 91 | case IMX_VPU_CODEC_FORMAT_MPEG2: return VPU_V_MPEG2; 92 | case IMX_VPU_CODEC_FORMAT_MJPEG: return VPU_V_MJPG; 93 | case IMX_VPU_CODEC_FORMAT_VP8: return VPU_V_VP8; 94 | default: assert(FALSE); 95 | } 96 | 97 | return VPU_V_MPEG2; /* should never be reached */ 98 | } 99 | 100 | 101 | static void convert_from_wrapper_mem_desc(VpuMemDesc *mem_desc, ImxVpuMemBlock *mem_block) 102 | { 103 | mem_block->size = mem_desc->nSize; 104 | mem_block->virtual_address_unaligned = (void*)(mem_desc->nVirtAddr); 105 | mem_block->physical_address_unaligned = mem_desc->nPhyAddr; 106 | mem_block->cpu_address = mem_desc->nCpuAddr; 107 | } 108 | 109 | 110 | static void convert_to_wrapper_mem_desc(ImxVpuMemBlock *mem_block, VpuMemDesc *mem_desc) 111 | { 112 | mem_desc->nSize = mem_block->size; 113 | mem_desc->nVirtAddr = (unsigned long)(mem_block->virtual_address_unaligned); 114 | mem_desc->nPhyAddr = mem_block->physical_address_unaligned; 115 | mem_desc->nCpuAddr = mem_block->cpu_address; 116 | } 117 | 118 | 119 | static void convert_to_wrapper_framebuffer(ImxVpuFramebuffer *fb, VpuFrameBuffer *wrapper_fb) 120 | { 121 | memset(wrapper_fb, 0, sizeof(VpuFrameBuffer)); 122 | 123 | wrapper_fb->nStrideY = fb->y_stride; 124 | wrapper_fb->nStrideC = fb->cbcr_stride; 125 | 126 | wrapper_fb->pbufY = (unsigned char*)(fb->physical_address + fb->y_offset); 127 | wrapper_fb->pbufCb = (unsigned char*)(fb->physical_address + fb->cb_offset); 128 | wrapper_fb->pbufCr = (unsigned char*)(fb->physical_address + fb->cr_offset); 129 | wrapper_fb->pbufMvCol = (unsigned char*)(fb->physical_address + fb->mvcol_offset); 130 | } 131 | 132 | 133 | 134 | 135 | /************************************************/ 136 | /******* DECODER STRUCTURES AND FUNCTIONS *******/ 137 | /************************************************/ 138 | 139 | 140 | #define MIN_NUM_FREE_FB_REQUIRED 6 141 | #define FRAME_ALIGN 16 142 | 143 | 144 | struct _ImxVpuDecoder 145 | { 146 | VpuDecHandle handle; 147 | 148 | void *virt_mem_sub_block; 149 | size_t virt_mem_sub_block_size; 150 | 151 | ImxVpuCodecFormats codec_format; 152 | 153 | unsigned int num_framebuffers; 154 | VpuFrameBuffer **wrapper_framebuffers; 155 | ImxVpuFramebuffer *framebuffers; 156 | void **user_data_for_frames; 157 | void *pending_user_data; 158 | void *dropped_frame_user_data; 159 | int num_user_data; 160 | BOOL delay_pending_user_data; 161 | void *last_pending_user_data; 162 | 163 | BOOL consumption_info_available; 164 | BOOL flush_vpu_upon_reset; 165 | 166 | BOOL recalculate_num_avail_framebuffers; 167 | int num_available_framebuffers; 168 | int num_times_counter_decremented; 169 | int num_framebuffers_in_use; 170 | }; 171 | 172 | 173 | static ImxVpuDecReturnCodes dec_convert_retcode(VpuDecRetCode code) 174 | { 175 | switch (code) 176 | { 177 | case VPU_DEC_RET_SUCCESS: return IMX_VPU_DEC_RETURN_CODE_OK; 178 | case VPU_DEC_RET_FAILURE: return IMX_VPU_DEC_RETURN_CODE_ERROR; 179 | case VPU_DEC_RET_INVALID_PARAM: return IMX_VPU_DEC_RETURN_CODE_INVALID_PARAMS; 180 | case VPU_DEC_RET_INVALID_HANDLE: return IMX_VPU_DEC_RETURN_CODE_INVALID_HANDLE; 181 | case VPU_DEC_RET_INVALID_FRAME_BUFFER: return IMX_VPU_DEC_RETURN_CODE_INVALID_FRAMEBUFFER; 182 | case VPU_DEC_RET_INSUFFICIENT_FRAME_BUFFERS: return IMX_VPU_DEC_RETURN_CODE_INSUFFICIENT_FRAMEBUFFERS; 183 | case VPU_DEC_RET_INVALID_STRIDE: return IMX_VPU_DEC_RETURN_CODE_INVALID_STRIDE; 184 | case VPU_DEC_RET_WRONG_CALL_SEQUENCE: return IMX_VPU_DEC_RETURN_CODE_WRONG_CALL_SEQUENCE; 185 | case VPU_DEC_RET_FAILURE_TIMEOUT: return IMX_VPU_DEC_RETURN_CODE_TIMEOUT; 186 | 187 | default: return IMX_VPU_DEC_RETURN_CODE_ERROR; 188 | } 189 | } 190 | 191 | 192 | static unsigned int dec_convert_outcode(VpuDecBufRetCode code) 193 | { 194 | /* TODO: REPEAT? SKIP? */ 195 | unsigned int out = 0; 196 | if (code & VPU_DEC_INPUT_USED) out |= IMX_VPU_DEC_OUTPUT_CODE_INPUT_USED; 197 | if (code & VPU_DEC_OUTPUT_EOS) out |= IMX_VPU_DEC_OUTPUT_CODE_EOS; 198 | if (code & VPU_DEC_OUTPUT_DIS) out |= IMX_VPU_DEC_OUTPUT_CODE_FRAME_OUTPUT; 199 | if (code & VPU_DEC_OUTPUT_NODIS) out |= IMX_VPU_DEC_OUTPUT_CODE_NO_FRAME_OUTPUT; 200 | if (code & VPU_DEC_OUTPUT_DROPPED) out |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; 201 | if (code & VPU_DEC_OUTPUT_MOSAIC_DIS) out |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; /* mosaic frames are dropped */ 202 | if (code & VPU_DEC_NO_ENOUGH_BUF) out |= IMX_VPU_DEC_OUTPUT_CODE_NOT_ENOUGH_OUTPUT_FRAMES; 203 | if (code & VPU_DEC_NO_ENOUGH_INBUF) out |= IMX_VPU_DEC_OUTPUT_CODE_NOT_ENOUGH_INPUT_DATA; 204 | if (code & VPU_DEC_INIT_OK) out |= IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE; 205 | if (code & VPU_DEC_RESOLUTION_CHANGED) out |= IMX_VPU_DEC_OUTPUT_CODE_RESOLUTION_CHANGED; 206 | return out; 207 | } 208 | 209 | 210 | static void dec_convert_to_wrapper_open_param(ImxVpuDecOpenParams const *open_params, VpuDecOpenParam *wrapper_open_param) 211 | { 212 | memset(wrapper_open_param, 0, sizeof(VpuDecOpenParam)); 213 | 214 | wrapper_open_param->CodecFormat = convert_to_wrapper_codec_std(open_params->codec_format); 215 | wrapper_open_param->nReorderEnable = open_params->enable_frame_reordering; 216 | wrapper_open_param->nPicWidth = open_params->frame_width; 217 | wrapper_open_param->nPicHeight = open_params->frame_height; 218 | } 219 | 220 | 221 | static void dec_convert_from_wrapper_initial_info(VpuDecInitInfo *wrapper_info, ImxVpuDecInitialInfo *info) 222 | { 223 | info->frame_width = wrapper_info->nPicWidth; 224 | info->frame_height = wrapper_info->nPicHeight; 225 | info->frame_rate_numerator = wrapper_info->nFrameRateRes; 226 | info->frame_rate_denominator = wrapper_info->nFrameRateDiv; 227 | 228 | info->min_num_required_framebuffers = wrapper_info->nMinFrameBufferCount + MIN_NUM_FREE_FB_REQUIRED; 229 | info->mjpeg_source_format = convert_from_wrapper_mjpg_format(wrapper_info->nMjpgSourceFormat); 230 | 231 | info->interlacing = wrapper_info->nInterlace; 232 | 233 | info->width_height_ratio = wrapper_info->nQ16ShiftWidthDivHeightRatio; 234 | 235 | info->framebuffer_alignment = wrapper_info->nAddressAlignment; 236 | } 237 | 238 | 239 | static int dec_get_wrapper_framebuffer_index(ImxVpuDecoder *decoder, VpuFrameBuffer *wrapper_fb) 240 | { 241 | unsigned int i; 242 | 243 | // TODO: do something faster, like a hash table 244 | for (i = 0; i < decoder->num_framebuffers; ++i) 245 | { 246 | if (wrapper_fb == decoder->wrapper_framebuffers[i]) 247 | return (int)i; 248 | } 249 | return -1; 250 | } 251 | 252 | 253 | char const * imx_vpu_dec_error_string(ImxVpuDecReturnCodes code) 254 | { 255 | switch (code) 256 | { 257 | case IMX_VPU_DEC_RETURN_CODE_OK: return "ok"; 258 | case IMX_VPU_DEC_RETURN_CODE_ERROR: return "unspecified error"; 259 | case IMX_VPU_DEC_RETURN_CODE_INVALID_PARAMS: return "invalid params"; 260 | case IMX_VPU_DEC_RETURN_CODE_INVALID_HANDLE: return "invalid handle"; 261 | case IMX_VPU_DEC_RETURN_CODE_INVALID_FRAMEBUFFER: return "invalid framebuffer"; 262 | case IMX_VPU_DEC_RETURN_CODE_INSUFFICIENT_FRAMEBUFFERS: return "insufficient_framebuffers"; 263 | case IMX_VPU_DEC_RETURN_CODE_INVALID_STRIDE: return "invalid stride"; 264 | case IMX_VPU_DEC_RETURN_CODE_WRONG_CALL_SEQUENCE: return "wrong call sequence"; 265 | case IMX_VPU_DEC_RETURN_CODE_TIMEOUT: return "timeout"; 266 | default: return ""; 267 | } 268 | } 269 | 270 | 271 | static unsigned long vpu_load_inst_counter = 0; 272 | 273 | 274 | ImxVpuDecReturnCodes imx_vpu_dec_load(void) 275 | { 276 | IMX_VPU_TRACE("VPU load instance counter: %lu", vpu_load_inst_counter); 277 | if (vpu_load_inst_counter == 0) 278 | { 279 | ImxVpuDecReturnCodes ret = dec_convert_retcode(VPU_DecLoad()); 280 | if (ret != IMX_VPU_DEC_RETURN_CODE_OK) 281 | { 282 | IMX_VPU_ERROR("loading decoder failed: %s", imx_vpu_dec_error_string(ret)); 283 | return ret; 284 | } 285 | else 286 | IMX_VPU_TRACE("loaded decoder"); 287 | } 288 | 289 | ++vpu_load_inst_counter; 290 | 291 | return IMX_VPU_DEC_RETURN_CODE_OK; 292 | } 293 | 294 | 295 | ImxVpuDecReturnCodes imx_vpu_dec_unload(void) 296 | { 297 | IMX_VPU_TRACE("VPU load instance counter: %lu", vpu_load_inst_counter); 298 | if (vpu_load_inst_counter == 1) 299 | { 300 | ImxVpuDecReturnCodes ret = dec_convert_retcode(VPU_DecUnLoad()); 301 | if (ret != IMX_VPU_DEC_RETURN_CODE_OK) 302 | { 303 | IMX_VPU_ERROR("unloading decoder failed: %s", imx_vpu_dec_error_string(ret)); 304 | return ret; 305 | } 306 | else 307 | IMX_VPU_TRACE("unloaded decoder"); 308 | } 309 | 310 | if (vpu_load_inst_counter > 0) 311 | --vpu_load_inst_counter; 312 | 313 | return IMX_VPU_DEC_RETURN_CODE_OK; 314 | } 315 | 316 | 317 | ImxVpuDecReturnCodes imx_vpu_dec_allocate_memory(ImxVpuMemBlock *mem_block) 318 | { 319 | VpuDecRetCode ret; 320 | VpuMemDesc mem_desc; 321 | 322 | if (mem_block->alignment == 0) 323 | mem_block->alignment = 1; 324 | 325 | mem_desc.nSize = mem_block->size + mem_block->alignment; 326 | 327 | if ((ret = VPU_DecGetMem(&mem_desc)) != VPU_DEC_RET_SUCCESS) 328 | { 329 | IMX_VPU_ERROR("allocating %d bytes of physical memory failed: %s", mem_block->size, imx_vpu_dec_error_string(dec_convert_retcode(ret))); 330 | return dec_convert_retcode(ret); 331 | } 332 | else 333 | IMX_VPU_TRACE("allocated %d bytes of physical memory", mem_block->size); 334 | 335 | convert_from_wrapper_mem_desc(&mem_desc, mem_block); 336 | 337 | mem_block->virtual_address = (void *)IMX_VPU_ALIGN_VAL_TO(mem_block->virtual_address_unaligned, mem_block->alignment); 338 | mem_block->physical_address = (imx_vpu_phys_addr_t)IMX_VPU_ALIGN_VAL_TO(mem_block->physical_address_unaligned, mem_block->alignment); 339 | 340 | return IMX_VPU_DEC_RETURN_CODE_OK; 341 | } 342 | 343 | 344 | ImxVpuDecReturnCodes imx_vpu_dec_deallocate_memory(ImxVpuMemBlock *mem_block) 345 | { 346 | ImxVpuDecReturnCodes ret; 347 | VpuMemDesc mem_desc; 348 | 349 | convert_to_wrapper_mem_desc(mem_block, &mem_desc); 350 | 351 | ret = dec_convert_retcode(VPU_DecFreeMem(&mem_desc)); 352 | if (ret != IMX_VPU_DEC_RETURN_CODE_OK) 353 | IMX_VPU_ERROR("deallocating %d bytes of physical memory failed: %s", mem_block->size, imx_vpu_dec_error_string(ret)); 354 | else 355 | IMX_VPU_TRACE("deallocated %d bytes of physical memory", mem_block->size); 356 | 357 | return ret; 358 | } 359 | 360 | 361 | void imx_vpu_dec_get_bitstream_buffer_info(unsigned int *alignment, size_t *size) 362 | { 363 | int i; 364 | VpuMemInfo mem_info; 365 | 366 | VPU_DecQueryMem(&mem_info); 367 | 368 | /* only two sub blocks are ever present - get the VPU_MEM_PHY one */ 369 | 370 | for (i = 0; i < mem_info.nSubBlockNum; ++i) 371 | { 372 | if (mem_info.MemSubBlock[i].MemType == VPU_MEM_PHY) 373 | { 374 | *alignment = mem_info.MemSubBlock[i].nAlignment; 375 | *size = mem_info.MemSubBlock[i].nSize; 376 | IMX_VPU_TRACE("determined alignment %d and size %d for the physical memory for the bitstream buffer", *alignment, *size); 377 | break; 378 | } 379 | } 380 | 381 | /* virtual memory block is allocated internally inside imx_vpu_dec_open() */ 382 | } 383 | 384 | 385 | ImxVpuDecReturnCodes imx_vpu_dec_open(ImxVpuDecoder **decoder, ImxVpuDecOpenParams const *open_params, void *bitstream_buffer_virtual_address, imx_vpu_phys_addr_t bitstream_buffer_physical_addres) 386 | { 387 | int config_param; 388 | VpuDecRetCode ret; 389 | VpuMemInfo mem_info; 390 | VpuDecOpenParam open_param; 391 | 392 | *decoder = IMX_VPU_ALLOC(sizeof(ImxVpuDecoder)); 393 | if ((*decoder) == NULL) 394 | { 395 | IMX_VPU_ERROR("allocating memory for decoder object failed"); 396 | return IMX_VPU_DEC_RETURN_CODE_ERROR; 397 | } 398 | 399 | memset(*decoder, 0, sizeof(ImxVpuDecoder)); 400 | 401 | { 402 | int i; 403 | 404 | VPU_DecQueryMem(&mem_info); 405 | 406 | IMX_VPU_INFO("about to allocate %d memory sub blocks", mem_info.nSubBlockNum); 407 | for (i = 0; i < mem_info.nSubBlockNum; ++i) 408 | { 409 | char const *type_str = ""; 410 | VpuMemSubBlockInfo *sub_block = &(mem_info.MemSubBlock[i]); 411 | 412 | switch (sub_block->MemType) 413 | { 414 | case VPU_MEM_VIRT: 415 | type_str = "virtual"; 416 | 417 | (*decoder)->virt_mem_sub_block_size = sub_block->nSize + sub_block->nAlignment; 418 | (*decoder)->virt_mem_sub_block = IMX_VPU_ALLOC((*decoder)->virt_mem_sub_block_size); 419 | if ((*decoder)->virt_mem_sub_block == NULL) 420 | { 421 | IMX_VPU_ERROR("allocating memory for sub block failed"); 422 | return IMX_VPU_DEC_RETURN_CODE_ERROR; 423 | } 424 | 425 | sub_block->pVirtAddr = (unsigned char *)IMX_VPU_ALIGN_VAL_TO((*decoder)->virt_mem_sub_block, sub_block->nAlignment); 426 | sub_block->pPhyAddr = 0; 427 | break; 428 | 429 | case VPU_MEM_PHY: 430 | type_str = "physical"; 431 | 432 | sub_block->pVirtAddr = (unsigned char *)(bitstream_buffer_virtual_address); 433 | sub_block->pPhyAddr = (unsigned char *)(bitstream_buffer_physical_addres); 434 | break; 435 | default: 436 | break; 437 | } 438 | 439 | IMX_VPU_INFO("allocated memory sub block #%d: type: %s size: %d alignment: %d virtual address: %p physical address: %p", i, type_str, sub_block->nSize, sub_block->nAlignment, sub_block->pVirtAddr, sub_block->pPhyAddr); 440 | } 441 | } 442 | 443 | dec_convert_to_wrapper_open_param(open_params, &open_param); 444 | 445 | IMX_VPU_TRACE("opening decoder"); 446 | 447 | switch (open_params->codec_format) 448 | { 449 | case IMX_VPU_CODEC_FORMAT_H264: 450 | case IMX_VPU_CODEC_FORMAT_H264_MVC: 451 | case IMX_VPU_CODEC_FORMAT_MPEG2: 452 | case IMX_VPU_CODEC_FORMAT_MPEG4: 453 | (*decoder)->consumption_info_available = TRUE; 454 | (*decoder)->flush_vpu_upon_reset = TRUE; 455 | break; 456 | case IMX_VPU_CODEC_FORMAT_H263: 457 | case IMX_VPU_CODEC_FORMAT_WMV3: 458 | case IMX_VPU_CODEC_FORMAT_WVC1: 459 | (*decoder)->consumption_info_available = FALSE; 460 | (*decoder)->flush_vpu_upon_reset = FALSE; 461 | break; 462 | case IMX_VPU_CODEC_FORMAT_MJPEG: 463 | case IMX_VPU_CODEC_FORMAT_VP8: 464 | (*decoder)->consumption_info_available = FALSE; 465 | (*decoder)->flush_vpu_upon_reset = TRUE; 466 | break; 467 | default: 468 | break; 469 | } 470 | 471 | ret = VPU_DecOpen(&((*decoder)->handle), &open_param, &mem_info); 472 | if (ret != VPU_DEC_RET_SUCCESS) 473 | { 474 | IMX_VPU_ERROR("opening decoder failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 475 | goto cleanup; 476 | } 477 | 478 | IMX_VPU_TRACE("setting configuration"); 479 | 480 | config_param = VPU_DEC_SKIPNONE; 481 | ret = VPU_DecConfig((*decoder)->handle, VPU_DEC_CONF_SKIPMODE, &config_param); 482 | if (ret != VPU_DEC_RET_SUCCESS) 483 | { 484 | IMX_VPU_ERROR("setting skipmode to NONE failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 485 | goto cleanup; 486 | } 487 | 488 | config_param = 0; 489 | ret = VPU_DecConfig((*decoder)->handle, VPU_DEC_CONF_BUFDELAY, &config_param); 490 | if (ret != VPU_DEC_RET_SUCCESS) 491 | { 492 | IMX_VPU_ERROR("setting bufdelay to 0 failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 493 | goto cleanup; 494 | } 495 | 496 | config_param = VPU_DEC_IN_NORMAL; 497 | ret = VPU_DecConfig((*decoder)->handle, VPU_DEC_CONF_INPUTTYPE, &config_param); 498 | if (ret != VPU_DEC_RET_SUCCESS) 499 | { 500 | IMX_VPU_ERROR("setting input type to \"normal\" failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 501 | goto cleanup; 502 | } 503 | 504 | (*decoder)->codec_format = open_params->codec_format; 505 | 506 | finish: 507 | if (ret == VPU_DEC_RET_SUCCESS) 508 | IMX_VPU_TRACE("successfully opened decoder"); 509 | 510 | return dec_convert_retcode(ret); 511 | 512 | cleanup: 513 | if ((*decoder)->virt_mem_sub_block != NULL) 514 | IMX_VPU_FREE((*decoder)->virt_mem_sub_block, (*decoder)->virt_mem_sub_block_size); 515 | IMX_VPU_FREE(*decoder, sizeof(ImxVpuDecoder)); 516 | *decoder = NULL; 517 | 518 | goto finish; 519 | } 520 | 521 | 522 | ImxVpuDecReturnCodes imx_vpu_dec_close(ImxVpuDecoder *decoder) 523 | { 524 | VpuDecRetCode ret; 525 | 526 | IMX_VPU_TRACE("closing decoder"); 527 | 528 | ret = VPU_DecFlushAll(decoder->handle); 529 | if (ret == VPU_DEC_RET_FAILURE_TIMEOUT) 530 | { 531 | IMX_VPU_WARNING("resetting decoder after a timeout occurred"); 532 | ret = VPU_DecReset(decoder->handle); 533 | if (ret != VPU_DEC_RET_SUCCESS) 534 | IMX_VPU_ERROR("resetting decoder failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 535 | } 536 | else if (ret != VPU_DEC_RET_SUCCESS) 537 | IMX_VPU_ERROR("flushing decoder failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 538 | 539 | ret = VPU_DecClose(decoder->handle); 540 | if (ret != VPU_DEC_RET_SUCCESS) 541 | IMX_VPU_ERROR("closing decoder failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 542 | 543 | if (decoder->user_data_for_frames != NULL) 544 | IMX_VPU_FREE(decoder->user_data_for_frames, sizeof(void*) * decoder->num_framebuffers); 545 | if (decoder->wrapper_framebuffers != NULL) 546 | IMX_VPU_FREE(decoder->wrapper_framebuffers, sizeof(VpuFrameBuffer*) * decoder->num_framebuffers); 547 | if (decoder->virt_mem_sub_block != NULL) 548 | IMX_VPU_FREE(decoder->virt_mem_sub_block, decoder->virt_mem_sub_block_size); 549 | IMX_VPU_FREE(decoder, sizeof(ImxVpuDecoder)); 550 | 551 | IMX_VPU_TRACE("closed decoder"); 552 | 553 | return dec_convert_retcode(ret); 554 | } 555 | 556 | 557 | ImxVpuDecReturnCodes imx_vpu_dec_set_drain_mode(ImxVpuDecoder *decoder, int enabled) 558 | { 559 | int config_param; 560 | VpuDecRetCode ret; 561 | 562 | config_param = enabled ? VPU_DEC_IN_DRAIN : VPU_DEC_IN_NORMAL; 563 | ret = VPU_DecConfig(decoder->handle, VPU_DEC_CONF_INPUTTYPE, &config_param); 564 | 565 | if (ret != VPU_DEC_RET_SUCCESS) 566 | IMX_VPU_ERROR("setting decoder drain mode failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 567 | else 568 | IMX_VPU_INFO("set decoder drain mode to %d", enabled); 569 | 570 | return dec_convert_retcode(ret); 571 | } 572 | 573 | 574 | ImxVpuDecReturnCodes imx_vpu_dec_flush(ImxVpuDecoder *decoder) 575 | { 576 | VpuDecRetCode ret = VPU_DEC_RET_SUCCESS; 577 | 578 | decoder->delay_pending_user_data = FALSE; 579 | 580 | if (decoder->flush_vpu_upon_reset) 581 | { 582 | ret = VPU_DecFlushAll(decoder->handle); 583 | if (ret == VPU_DEC_RET_FAILURE_TIMEOUT) 584 | { 585 | IMX_VPU_WARNING("resetting decoder after a timeout occurred"); 586 | ret = VPU_DecReset(decoder->handle); 587 | if (ret != VPU_DEC_RET_SUCCESS) 588 | IMX_VPU_ERROR("resetting decoder failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 589 | } 590 | else if (ret != VPU_DEC_RET_SUCCESS) 591 | IMX_VPU_ERROR("flushing decoder failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 592 | else 593 | IMX_VPU_INFO("flushed decoder"); 594 | 595 | decoder->recalculate_num_avail_framebuffers = TRUE; 596 | } 597 | else 598 | IMX_VPU_INFO("decoder not flushed, because it is unnecessary for this codec format"); 599 | 600 | if (decoder->user_data_for_frames != NULL) 601 | memset(decoder->user_data_for_frames, 0, sizeof(void*) * decoder->num_framebuffers); 602 | decoder->num_user_data = 0; 603 | 604 | return dec_convert_retcode(ret); 605 | } 606 | 607 | 608 | ImxVpuDecReturnCodes imx_vpu_dec_register_framebuffers(ImxVpuDecoder *decoder, ImxVpuFramebuffer *framebuffers, unsigned int num_framebuffers) 609 | { 610 | unsigned int i; 611 | VpuDecRetCode ret; 612 | VpuFrameBuffer *temp_fbs; 613 | 614 | IMX_VPU_TRACE("attempting to register %u framebuffers", num_framebuffers); 615 | 616 | decoder->wrapper_framebuffers = NULL; 617 | 618 | temp_fbs = IMX_VPU_ALLOC(sizeof(VpuFrameBuffer) * num_framebuffers); 619 | if (temp_fbs == NULL) 620 | { 621 | IMX_VPU_ERROR("allocating memory for framebuffers failed"); 622 | return IMX_VPU_DEC_RETURN_CODE_ERROR; 623 | } 624 | 625 | for (i = 0; i < num_framebuffers; ++i) 626 | convert_to_wrapper_framebuffer(&framebuffers[i], &(temp_fbs[i])); 627 | 628 | ret = VPU_DecRegisterFrameBuffer(decoder->handle, temp_fbs, num_framebuffers); 629 | 630 | IMX_VPU_FREE(temp_fbs, sizeof(VpuFrameBuffer) * num_framebuffers); 631 | 632 | if (ret != VPU_DEC_RET_SUCCESS) 633 | { 634 | ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); 635 | IMX_VPU_ERROR("registering framebuffers failed: %s", imx_vpu_dec_error_string(imxret)); 636 | return ret; 637 | } 638 | 639 | decoder->wrapper_framebuffers = IMX_VPU_ALLOC(sizeof(VpuFrameBuffer*) * num_framebuffers); 640 | { 641 | int out_num; 642 | VPU_DecAllRegFrameInfo(decoder->handle, decoder->wrapper_framebuffers, &out_num); 643 | IMX_VPU_LOG("out_num: %d num_framebuffers: %u", out_num, num_framebuffers); 644 | } 645 | 646 | decoder->framebuffers = framebuffers; 647 | decoder->num_framebuffers = num_framebuffers; 648 | decoder->num_available_framebuffers = num_framebuffers; 649 | 650 | decoder->user_data_for_frames = IMX_VPU_ALLOC(sizeof(void*) * num_framebuffers); 651 | if (decoder->user_data_for_frames == NULL) 652 | { 653 | IMX_VPU_ERROR("allocating memory for user data failed"); 654 | IMX_VPU_FREE(decoder->wrapper_framebuffers, sizeof(VpuFrameBuffer*) * num_framebuffers); 655 | decoder->wrapper_framebuffers = NULL; 656 | return IMX_VPU_DEC_RETURN_CODE_ERROR; 657 | } 658 | 659 | memset(decoder->user_data_for_frames, 0, sizeof(void*) * num_framebuffers); 660 | decoder->num_user_data = 0; 661 | 662 | return IMX_VPU_DEC_RETURN_CODE_OK; 663 | } 664 | 665 | 666 | void imx_vpu_dec_calc_framebuffer_sizes(ImxVpuDecInitialInfo const *initial_info, unsigned int *frame_width, unsigned int *frame_height, unsigned int *y_stride, unsigned int *uv_stride, unsigned int *y_size, unsigned int *uv_size, unsigned int *mvcol_size, unsigned int *total_size) 667 | { 668 | int alignment; 669 | 670 | *frame_width = IMX_VPU_ALIGN_VAL_TO(*frame_width, FRAME_ALIGN); 671 | if (initial_info->interlacing) 672 | *frame_height = IMX_VPU_ALIGN_VAL_TO(*frame_height, (2 * FRAME_ALIGN)); 673 | else 674 | *frame_height = IMX_VPU_ALIGN_VAL_TO(*frame_height, FRAME_ALIGN); 675 | 676 | *y_stride = *frame_width; 677 | *y_size = (*y_stride) * (*frame_height); 678 | 679 | switch (initial_info->mjpeg_source_format) 680 | { 681 | case IMX_VPU_MJPEG_FORMAT_YUV420: 682 | *uv_stride = *y_stride / 2; 683 | *uv_size = *mvcol_size = *y_size / 4; 684 | break; 685 | case IMX_VPU_MJPEG_FORMAT_YUV422_HORIZONTAL: 686 | *uv_stride = *y_stride / 2; 687 | *uv_size = *mvcol_size = *y_size / 2; 688 | break; 689 | case IMX_VPU_MJPEG_FORMAT_YUV444: 690 | *uv_stride = *y_stride; 691 | *uv_size = *mvcol_size = *y_size; 692 | break; 693 | case IMX_VPU_MJPEG_FORMAT_YUV400: 694 | /* TODO: check if this is OK */ 695 | *uv_stride = 0; 696 | *uv_size = *mvcol_size = 0; 697 | break; 698 | default: 699 | assert(FALSE); 700 | } 701 | 702 | alignment = initial_info->framebuffer_alignment; 703 | if (alignment > 1) 704 | { 705 | *y_size = IMX_VPU_ALIGN_VAL_TO(*y_size, alignment); 706 | *uv_size = IMX_VPU_ALIGN_VAL_TO(*uv_size, alignment); 707 | *mvcol_size = IMX_VPU_ALIGN_VAL_TO(*mvcol_size, alignment); 708 | } 709 | 710 | *total_size = *y_size + *uv_size + *uv_size + *mvcol_size + alignment; 711 | } 712 | 713 | 714 | ImxVpuDecReturnCodes imx_vpu_dec_get_initial_info(ImxVpuDecoder *decoder, ImxVpuDecInitialInfo *info) 715 | { 716 | VpuDecRetCode ret; 717 | VpuDecInitInfo init_info; 718 | 719 | ret = VPU_DecGetInitialInfo(decoder->handle, &init_info); 720 | IMX_VPU_LOG("VPU_DecGetInitialInfo: min num framebuffers required: %d", init_info.nMinFrameBufferCount); 721 | dec_convert_from_wrapper_initial_info(&init_info, info); 722 | return dec_convert_retcode(ret); 723 | } 724 | 725 | 726 | ImxVpuDecReturnCodes imx_vpu_dec_decode_frame(ImxVpuDecoder *decoder, ImxVpuEncodedFrame const *encoded_frame, unsigned int *output_code) 727 | { 728 | VpuDecRetCode ret; 729 | VpuBufferNode node; 730 | int buf_ret_code; 731 | 732 | node.pVirAddr = encoded_frame->virtual_address; 733 | node.pPhyAddr = 0; /* encoded data is always read from a regular memory block, not a DMA buffer */ 734 | node.nSize = encoded_frame->data_size; 735 | 736 | node.sCodecData.pData = encoded_frame->codec_data; 737 | node.sCodecData.nSize = encoded_frame->codec_data_size; 738 | 739 | decoder->pending_user_data = encoded_frame->user_data; 740 | 741 | ret = VPU_DecDecodeBuf(decoder->handle, &node, &buf_ret_code); 742 | IMX_VPU_LOG("VPU_DecDecodeBuf buf ret code: 0x%x", buf_ret_code); 743 | 744 | *output_code = dec_convert_outcode(buf_ret_code); 745 | 746 | if (ret != VPU_DEC_RET_SUCCESS) 747 | { 748 | IMX_VPU_ERROR("decoding frame failed: %s", imx_vpu_dec_error_string(dec_convert_retcode(ret))); 749 | return dec_convert_retcode(ret); 750 | } 751 | 752 | if (decoder->recalculate_num_avail_framebuffers) 753 | { 754 | decoder->num_available_framebuffers = decoder->num_framebuffers - decoder->num_framebuffers_in_use; 755 | IMX_VPU_LOG("recalculated number of available framebuffers to %d", decoder->num_available_framebuffers); 756 | decoder->recalculate_num_avail_framebuffers = FALSE; 757 | } 758 | 759 | if (buf_ret_code & VPU_DEC_INIT_OK) 760 | { 761 | decoder->delay_pending_user_data = TRUE; 762 | decoder->last_pending_user_data = decoder->pending_user_data; 763 | } 764 | 765 | if (buf_ret_code & VPU_DEC_FLUSH) 766 | { 767 | IMX_VPU_INFO("VPU requested a decoder flush"); 768 | ret = VPU_DecFlushAll(decoder->handle); 769 | if (ret == VPU_DEC_RET_FAILURE_TIMEOUT) 770 | { 771 | IMX_VPU_WARNING("timeout detected, resetting decoder"); 772 | 773 | ret = VPU_DecReset(decoder->handle); 774 | if (ret != VPU_DEC_RET_SUCCESS) 775 | { 776 | ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); 777 | IMX_VPU_ERROR("resetting decoder failed: %s", imx_vpu_dec_error_string(imxret)); 778 | return imxret; 779 | } 780 | else 781 | *output_code |= IMX_VPU_DEC_OUTPUT_CODE_INTERNAL_RESET; 782 | } 783 | else if (ret != VPU_DEC_RET_SUCCESS) 784 | { 785 | ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); 786 | IMX_VPU_ERROR("flushing decoder failed: %s", imx_vpu_dec_error_string(imxret)); 787 | return imxret; 788 | } 789 | else 790 | IMX_VPU_INFO("flushed decoder"); 791 | } 792 | 793 | if (buf_ret_code & VPU_DEC_RESOLUTION_CHANGED) 794 | { 795 | IMX_VPU_INFO("resolution changed - resetting internal states"); 796 | 797 | *output_code |= IMX_VPU_DEC_OUTPUT_CODE_INITIAL_INFO_AVAILABLE; 798 | 799 | decoder->delay_pending_user_data = TRUE; 800 | decoder->recalculate_num_avail_framebuffers = FALSE; 801 | 802 | decoder->num_user_data = 0; 803 | 804 | if (decoder->user_data_for_frames != NULL) 805 | IMX_VPU_FREE(decoder->user_data_for_frames, sizeof(void*) * decoder->num_framebuffers); 806 | if (decoder->wrapper_framebuffers != NULL) 807 | IMX_VPU_FREE(decoder->wrapper_framebuffers, sizeof(VpuFrameBuffer*) * decoder->num_framebuffers); 808 | 809 | decoder->user_data_for_frames = NULL; 810 | decoder->wrapper_framebuffers = NULL; 811 | } 812 | 813 | if (buf_ret_code & VPU_DEC_NO_ENOUGH_INBUF) 814 | { 815 | /* Not dropping frame here on purpose; the next input frame may 816 | * complete the input */ 817 | } 818 | 819 | { 820 | void *user_data = decoder->delay_pending_user_data ? decoder->last_pending_user_data : decoder->pending_user_data; 821 | 822 | /* The first time this location is reached, VPU_DEC_INIT_OK will be set in the output_code. 823 | * This implies that the framebuffers have not been allocated and registered yet, 824 | * so no user data can be stored yet. 825 | * With codec formats that produce consumption info, this is not a problem, because 826 | * VPU_DEC_ONE_FRM_CONSUMED will be returned only when framebuffers are present. 827 | * But with other formats, an explicit decoder->framebuffers != NULL check is necessary 828 | * (see below). The user_data pointer does not get lost; it is stored in last_pending_user_data. */ 829 | if ((buf_ret_code & VPU_DEC_ONE_FRM_CONSUMED) && !(buf_ret_code & VPU_DEC_OUTPUT_DROPPED)) 830 | { 831 | int fb_index; 832 | 833 | VpuDecFrameLengthInfo consumed_frame_info; 834 | ret = VPU_DecGetConsumedFrameInfo(decoder->handle, &consumed_frame_info); 835 | if (ret != VPU_DEC_RET_SUCCESS) 836 | { 837 | ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); 838 | IMX_VPU_ERROR("getting consumed frame info failed: %s", imx_vpu_dec_error_string(imxret)); 839 | return imxret; 840 | } 841 | 842 | fb_index = dec_get_wrapper_framebuffer_index(decoder, consumed_frame_info.pFrame); 843 | 844 | if (consumed_frame_info.pFrame != NULL) 845 | { 846 | if ((fb_index >= 0) && (fb_index < (int)(decoder->num_framebuffers))) 847 | { 848 | IMX_VPU_LOG("framebuffer index %d for framebuffer %p user data %p", fb_index, (void *)(consumed_frame_info.pFrame), user_data); 849 | decoder->user_data_for_frames[fb_index] = user_data; 850 | } 851 | else 852 | IMX_VPU_ERROR("framebuffer index %d for framebuffer %p user data %p out of bounds", fb_index, (void *)(consumed_frame_info.pFrame), user_data); 853 | } 854 | else 855 | IMX_VPU_WARNING("consumed frame info contains a NULL frame"); 856 | } 857 | else if (!(decoder->consumption_info_available) && (decoder->framebuffers != NULL)) 858 | { 859 | if (decoder->num_user_data < (int)(decoder->num_framebuffers)) 860 | { 861 | decoder->user_data_for_frames[decoder->num_user_data] = user_data; 862 | decoder->num_user_data++; 863 | 864 | IMX_VPU_LOG("user data %p stored as newest", user_data); 865 | 866 | IMX_VPU_TRACE("incremented number of userdata pointers to %d", decoder->num_user_data); 867 | } 868 | else 869 | IMX_VPU_WARNING("too many user data pointers in memory - cannot store current one"); 870 | } 871 | 872 | decoder->last_pending_user_data = decoder->pending_user_data; 873 | decoder->pending_user_data = NULL; 874 | } 875 | 876 | if ((buf_ret_code & VPU_DEC_ONE_FRM_CONSUMED) && !(buf_ret_code & VPU_DEC_OUTPUT_DROPPED)) 877 | { 878 | decoder->num_available_framebuffers--; 879 | decoder->num_times_counter_decremented++; 880 | IMX_VPU_LOG("decremented number of available framebuffers to %d (with consumed frame info); number of times decremented is now %d", decoder->num_available_framebuffers, decoder->num_times_counter_decremented); 881 | } 882 | 883 | if (buf_ret_code & VPU_DEC_OUTPUT_NODIS) 884 | { 885 | if ((encoded_frame->virtual_address != NULL) && (decoder->codec_format == IMX_VPU_CODEC_FORMAT_VP8)) 886 | *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DECODE_ONLY; 887 | } 888 | 889 | /* VPU_DEC_NO_ENOUGH_BUF handled by caller - should be treated as an error */ 890 | 891 | if ((buf_ret_code & VPU_DEC_OUTPUT_DIS) && !(decoder->consumption_info_available)) 892 | { 893 | decoder->num_available_framebuffers--; 894 | decoder->num_times_counter_decremented++; 895 | IMX_VPU_LOG("decremented number of available framebuffers to %d (no consumed frame info); number of times decremented is now %d", decoder->num_available_framebuffers, decoder->num_times_counter_decremented); 896 | } 897 | else if (buf_ret_code & VPU_DEC_OUTPUT_MOSAIC_DIS) 898 | { 899 | IMX_VPU_TRACE("dropping mosaic frame"); 900 | 901 | /* mosaic frames do not seem to be useful for anything, so they are just dropped here */ 902 | 903 | ImxVpuDecReturnCodes imxret; 904 | ImxVpuDecodedFrame decoded_frame; 905 | 906 | if ((imxret = imx_vpu_dec_get_decoded_frame(decoder, &decoded_frame)) != IMX_VPU_DEC_RETURN_CODE_OK) 907 | { 908 | IMX_VPU_ERROR("error getting output mosaic frame: %s", imx_vpu_dec_error_string(imxret)); 909 | return imxret; 910 | } 911 | 912 | if ((imxret = imx_vpu_dec_mark_framebuffer_as_displayed(decoder, decoded_frame.framebuffer)) != IMX_VPU_DEC_RETURN_CODE_OK) 913 | { 914 | IMX_VPU_ERROR("error marking mosaic frame as displayed: %s", imx_vpu_dec_error_string(imxret)); 915 | return imxret; 916 | } 917 | 918 | decoder->dropped_frame_user_data = decoded_frame.user_data; 919 | 920 | *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; 921 | } 922 | else if (buf_ret_code & VPU_DEC_OUTPUT_DROPPED) 923 | { 924 | // TODO make this work for formats with consumption info 925 | if (decoder->num_user_data > 0) 926 | { 927 | decoder->dropped_frame_user_data = decoder->user_data_for_frames[0]; 928 | decoder->user_data_for_frames[0] = NULL; 929 | memmove(decoder->user_data_for_frames, decoder->user_data_for_frames + 1, sizeof(void*) * (decoder->num_user_data - 1)); 930 | decoder->num_user_data--; 931 | } 932 | else 933 | decoder->dropped_frame_user_data = NULL; 934 | } 935 | 936 | /* In case the VPU didn't use the input and no consumed frame info is available, 937 | * drop the input frame to make sure timestamps are okay 938 | * (If consumed frame info is present it is still possible it might be used for input-output frame 939 | * associations; unlikely to occur thought) */ 940 | if ((encoded_frame->virtual_address != NULL) && !(buf_ret_code & (VPU_DEC_ONE_FRM_CONSUMED | VPU_DEC_INPUT_USED | VPU_DEC_RESOLUTION_CHANGED))) 941 | { 942 | decoder->dropped_frame_user_data = encoded_frame->user_data; 943 | *output_code |= IMX_VPU_DEC_OUTPUT_CODE_DROPPED; 944 | } 945 | 946 | return IMX_VPU_DEC_RETURN_CODE_OK; 947 | } 948 | 949 | 950 | ImxVpuDecReturnCodes imx_vpu_dec_get_decoded_frame(ImxVpuDecoder *decoder, ImxVpuDecodedFrame *decoded_frame) 951 | { 952 | VpuDecRetCode ret; 953 | VpuDecOutFrameInfo out_frame_info; 954 | int fb_index; 955 | void *user_data; 956 | 957 | ret = VPU_DecGetOutputFrame(decoder->handle, &out_frame_info); 958 | if (ret != VPU_DEC_RET_SUCCESS) 959 | { 960 | ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); 961 | IMX_VPU_ERROR("error getting decoded output frame: %s", imx_vpu_dec_error_string(imxret)); 962 | return imxret; 963 | } 964 | 965 | fb_index = dec_get_wrapper_framebuffer_index(decoder, out_frame_info.pDisplayFrameBuf); 966 | 967 | user_data = NULL; 968 | if (decoder->consumption_info_available) 969 | { 970 | if ((fb_index >= 0) && (fb_index < (int)(decoder->num_framebuffers))) 971 | { 972 | user_data = decoder->user_data_for_frames[fb_index]; 973 | IMX_VPU_LOG("framebuffer index %d for framebuffer %p and user data %p", fb_index, (void *)(out_frame_info.pDisplayFrameBuf), user_data); 974 | decoder->user_data_for_frames[fb_index] = NULL; 975 | } 976 | else 977 | IMX_VPU_ERROR("framebuffer index %d for framebuffer %p and user data %p out of bounds", fb_index, (void *)(out_frame_info.pDisplayFrameBuf), user_data); 978 | } 979 | else 980 | { 981 | if (decoder->num_user_data > 0) 982 | { 983 | user_data = decoder->user_data_for_frames[0]; 984 | decoder->user_data_for_frames[0] = NULL; 985 | IMX_VPU_LOG("framebuffer index %d user data %p retrieved as oldest", fb_index, user_data); 986 | memmove(decoder->user_data_for_frames, decoder->user_data_for_frames + 1, sizeof(void*) * (decoder->num_user_data - 1)); 987 | decoder->num_user_data--; 988 | } 989 | } 990 | 991 | decoded_frame->pic_type = convert_from_wrapper_pic_type(out_frame_info.ePicType); 992 | decoded_frame->user_data = user_data; 993 | 994 | /* XXX 995 | * This association assumes that the order of internal framebuffer entries 996 | * inside the VPU wrapper is the same as the order of the framebuffers here. 997 | * So, decoder->framebuffers[1] equals internal framebuffer entry with index 1 etc. 998 | */ 999 | decoded_frame->framebuffer = &(decoder->framebuffers[fb_index]); 1000 | /* This is used in imx_vpu_dec_mark_framebuffer_as_displayed() to be able 1001 | * to mark the vpuwrapper framebuffer as displayed */ 1002 | decoded_frame->framebuffer->internal = out_frame_info.pDisplayFrameBuf; 1003 | 1004 | decoder->num_framebuffers_in_use++; 1005 | 1006 | return IMX_VPU_DEC_RETURN_CODE_OK; 1007 | } 1008 | 1009 | 1010 | void* imx_vpu_dec_get_dropped_frame_user_data(ImxVpuDecoder *decoder) 1011 | { 1012 | return decoder->dropped_frame_user_data; 1013 | } 1014 | 1015 | 1016 | int imx_vpu_dec_get_num_free_framebuffers(ImxVpuDecoder *decoder) 1017 | { 1018 | return decoder->num_available_framebuffers; 1019 | } 1020 | 1021 | 1022 | int imx_vpu_dec_get_min_num_free_required(ImxVpuDecoder *decoder) 1023 | { 1024 | IMXVPUCODEC_UNUSED_PARAM(decoder); 1025 | return MIN_NUM_FREE_FB_REQUIRED; 1026 | } 1027 | 1028 | 1029 | ImxVpuDecReturnCodes imx_vpu_dec_mark_framebuffer_as_displayed(ImxVpuDecoder *decoder, ImxVpuFramebuffer const *framebuffer) 1030 | { 1031 | VpuDecRetCode ret; 1032 | VpuFrameBuffer *wrapper_fb = (VpuFrameBuffer *)(framebuffer->internal); 1033 | 1034 | ret = VPU_DecOutFrameDisplayed(decoder->handle, wrapper_fb); 1035 | if (ret != VPU_DEC_RET_SUCCESS) 1036 | { 1037 | ImxVpuDecReturnCodes imxret = dec_convert_retcode(ret); 1038 | IMX_VPU_ERROR("error marking output frame as displayed: %s", imx_vpu_dec_error_string(imxret)); 1039 | return imxret; 1040 | } 1041 | 1042 | IMX_VPU_LOG("marked framebuffer %p with physical address 0x%x as displayed", (void *)framebuffer, framebuffer->physical_address); 1043 | 1044 | if (decoder->num_times_counter_decremented > 0) 1045 | { 1046 | decoder->num_available_framebuffers++; 1047 | decoder->num_times_counter_decremented--; 1048 | decoder->num_framebuffers_in_use--; 1049 | 1050 | IMX_VPU_LOG("num_available_framebuffers %d num_times_counter_decremented %d num_framebuffers_in_use %d", decoder->num_available_framebuffers, decoder->num_times_counter_decremented, decoder->num_framebuffers_in_use); 1051 | } 1052 | 1053 | return IMX_VPU_DEC_RETURN_CODE_OK; 1054 | } 1055 | 1056 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imxvpucodec_platform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imxvpucodec - i.MX6 VPU hardware codec engine API library 3 | * Copyright (c) 2014 Carlos Rafael Giani 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any 7 | * damages arising from the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute 11 | * it freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must 14 | * not claim that you wrote the original software. If you use this 15 | * software in a product, an acknowledgment in the product 16 | * documentation would be appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must 19 | * not be misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | 25 | #ifndef IMXVPUCODEC_PLATFORM_H 26 | #define IMXVPUCODEC_PLATFORM_H 27 | 28 | 29 | #define IMXVPUCODEC_UNUSED_PARAM(x) ((void)(x)) 30 | 31 | 32 | #include "imxvpucodec_platform_chromium.h" 33 | 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imxvpucodec_platform_chromium.cc: -------------------------------------------------------------------------------- 1 | #include "imxvpucodec_platform_chromium.h" 2 | #include "base/logging.h" 3 | 4 | #include 5 | #include 6 | 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | 13 | void imx_vpu_log(ImxVpuLogLevel level, char const *file, int const line, char const *fn, const char * format, ...) 14 | { 15 | va_list args; 16 | char buf[500]; 17 | 18 | va_start(args, format); 19 | vsnprintf(buf, sizeof(buf), format, args); 20 | va_end(args); 21 | 22 | #define DO_LOG(severity) do { LOG(severity) << file << ":" << line << " (" << fn << ") " << buf; } while(0) 23 | #define DO_VLOG(severity) do { VLOG(severity) << file << ":" << line << " (" << fn << ") " << buf; } while(0) 24 | 25 | switch (level) 26 | { 27 | case IMX_VPU_LOG_LEVEL_ERROR: DO_LOG(ERROR); break; 28 | case IMX_VPU_LOG_LEVEL_WARNING: DO_LOG(WARNING); break; 29 | case IMX_VPU_LOG_LEVEL_INFO: DO_LOG(INFO); break; 30 | case IMX_VPU_LOG_LEVEL_DEBUG: DO_VLOG(0); break; 31 | case IMX_VPU_LOG_LEVEL_LOG: DO_VLOG(1); break; 32 | case IMX_VPU_LOG_LEVEL_TRACE: DO_VLOG(2); break; 33 | default: break; 34 | } 35 | } 36 | 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /src/content/common/gpu/media/imxvpucodec_platform_chromium.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imxvpucodec - i.MX6 VPU hardware codec engine API library 3 | * Copyright (c) 2014 Carlos Rafael Giani 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any 7 | * damages arising from the use of this software. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose, 10 | * including commercial applications, and to alter it and redistribute 11 | * it freely, subject to the following restrictions: 12 | * 13 | * 1. The origin of this software must not be misrepresented; you must 14 | * not claim that you wrote the original software. If you use this 15 | * software in a product, an acknowledgment in the product 16 | * documentation would be appreciated but is not required. 17 | * 18 | * 2. Altered source versions must be plainly marked as such, and must 19 | * not be misrepresented as being the original software. 20 | * 21 | * 3. This notice may not be removed or altered from any source distribution. 22 | */ 23 | 24 | 25 | #ifndef IMXVPUCODEC_PLATFORM_CHROMIUM_H 26 | #define IMXVPUCODEC_PLATFORM_CHROMIUM_H 27 | 28 | 29 | #include 30 | 31 | 32 | #ifdef __cplusplus 33 | extern "C" { 34 | #endif 35 | 36 | 37 | #define IMX_VPU_ALLOC(SIZE) malloc(SIZE) 38 | #define IMX_VPU_FREE(PTR, SIZE) free(PTR) 39 | 40 | 41 | #define IMX_VPU_ERROR(...) imx_vpu_log(IMX_VPU_LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 42 | #define IMX_VPU_WARNING(...) imx_vpu_log(IMX_VPU_LOG_LEVEL_WARNING, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 43 | #define IMX_VPU_INFO(...) imx_vpu_log(IMX_VPU_LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 44 | #define IMX_VPU_DEBUG(...) imx_vpu_log(IMX_VPU_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 45 | #define IMX_VPU_LOG(...) imx_vpu_log(IMX_VPU_LOG_LEVEL_LOG, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 46 | #define IMX_VPU_TRACE(...) imx_vpu_log(IMX_VPU_LOG_LEVEL_TRACE, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) 47 | 48 | 49 | typedef enum 50 | { 51 | IMX_VPU_LOG_LEVEL_ERROR = 0, 52 | IMX_VPU_LOG_LEVEL_WARNING, 53 | IMX_VPU_LOG_LEVEL_INFO, 54 | IMX_VPU_LOG_LEVEL_DEBUG, 55 | IMX_VPU_LOG_LEVEL_LOG, 56 | IMX_VPU_LOG_LEVEL_TRACE 57 | } 58 | ImxVpuLogLevel; 59 | 60 | 61 | void imx_vpu_log(ImxVpuLogLevel level, char const *file, int const line, char const *fn, const char * format, ...); 62 | 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | 69 | #endif 70 | 71 | 72 | --------------------------------------------------------------------------------