1bf215546Sopenharmony_ci/************************************************************************** 2bf215546Sopenharmony_ci * 3bf215546Sopenharmony_ci * Copyright 2009 Younes Manton. 4bf215546Sopenharmony_ci * All Rights Reserved. 5bf215546Sopenharmony_ci * 6bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 7bf215546Sopenharmony_ci * copy of this software and associated documentation files (the 8bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 9bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 10bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 11bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 12bf215546Sopenharmony_ci * the following conditions: 13bf215546Sopenharmony_ci * 14bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the 15bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 16bf215546Sopenharmony_ci * of the Software. 17bf215546Sopenharmony_ci * 18bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21bf215546Sopenharmony_ci * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 22bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25bf215546Sopenharmony_ci * 26bf215546Sopenharmony_ci **************************************************************************/ 27bf215546Sopenharmony_ci 28bf215546Sopenharmony_ci#include <assert.h> 29bf215546Sopenharmony_ci 30bf215546Sopenharmony_ci#include <X11/Xlibint.h> 31bf215546Sopenharmony_ci#include <X11/extensions/XvMClib.h> 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ci#include "pipe/p_screen.h" 34bf215546Sopenharmony_ci#include "pipe/p_video_codec.h" 35bf215546Sopenharmony_ci#include "pipe/p_video_state.h" 36bf215546Sopenharmony_ci#include "pipe/p_state.h" 37bf215546Sopenharmony_ci 38bf215546Sopenharmony_ci#include "util/u_memory.h" 39bf215546Sopenharmony_ci 40bf215546Sopenharmony_ci#include "vl/vl_csc.h" 41bf215546Sopenharmony_ci#include "vl/vl_winsys.h" 42bf215546Sopenharmony_ci 43bf215546Sopenharmony_ci#include "xvmc_private.h" 44bf215546Sopenharmony_ci 45bf215546Sopenharmony_cistatic Status Validate(Display *dpy, XvPortID port, int surface_type_id, 46bf215546Sopenharmony_ci unsigned int width, unsigned int height, int flags, 47bf215546Sopenharmony_ci bool *found_port, int *screen, int *chroma_format, 48bf215546Sopenharmony_ci int *mc_type, int *surface_flags, 49bf215546Sopenharmony_ci unsigned short *subpic_max_w, 50bf215546Sopenharmony_ci unsigned short *subpic_max_h) 51bf215546Sopenharmony_ci{ 52bf215546Sopenharmony_ci bool found_surface = false; 53bf215546Sopenharmony_ci XvAdaptorInfo *adaptor_info; 54bf215546Sopenharmony_ci unsigned int num_adaptors; 55bf215546Sopenharmony_ci int num_types; 56bf215546Sopenharmony_ci unsigned int max_width = 0, max_height = 0; 57bf215546Sopenharmony_ci Status ret; 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_ci assert(dpy); 60bf215546Sopenharmony_ci assert(found_port); 61bf215546Sopenharmony_ci assert(screen); 62bf215546Sopenharmony_ci assert(chroma_format); 63bf215546Sopenharmony_ci assert(mc_type); 64bf215546Sopenharmony_ci assert(surface_flags); 65bf215546Sopenharmony_ci assert(subpic_max_w); 66bf215546Sopenharmony_ci assert(subpic_max_h); 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ci *found_port = false; 69bf215546Sopenharmony_ci 70bf215546Sopenharmony_ci for (int i = 0; i < XScreenCount(dpy); ++i) { 71bf215546Sopenharmony_ci ret = XvQueryAdaptors(dpy, XRootWindow(dpy, i), &num_adaptors, &adaptor_info); 72bf215546Sopenharmony_ci if (ret != Success) 73bf215546Sopenharmony_ci return ret; 74bf215546Sopenharmony_ci 75bf215546Sopenharmony_ci for (unsigned int j = 0; j < num_adaptors && !*found_port; ++j) { 76bf215546Sopenharmony_ci for (unsigned int k = 0; k < adaptor_info[j].num_ports && !*found_port; ++k) { 77bf215546Sopenharmony_ci XvMCSurfaceInfo *surface_info; 78bf215546Sopenharmony_ci 79bf215546Sopenharmony_ci if (adaptor_info[j].base_id + k != port) 80bf215546Sopenharmony_ci continue; 81bf215546Sopenharmony_ci 82bf215546Sopenharmony_ci *found_port = true; 83bf215546Sopenharmony_ci 84bf215546Sopenharmony_ci surface_info = XvMCListSurfaceTypes(dpy, adaptor_info[j].base_id, &num_types); 85bf215546Sopenharmony_ci if (!surface_info) { 86bf215546Sopenharmony_ci XvFreeAdaptorInfo(adaptor_info); 87bf215546Sopenharmony_ci return BadAlloc; 88bf215546Sopenharmony_ci } 89bf215546Sopenharmony_ci 90bf215546Sopenharmony_ci for (int l = 0; l < num_types && !found_surface; ++l) { 91bf215546Sopenharmony_ci if (surface_info[l].surface_type_id != surface_type_id) 92bf215546Sopenharmony_ci continue; 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_ci found_surface = true; 95bf215546Sopenharmony_ci max_width = surface_info[l].max_width; 96bf215546Sopenharmony_ci max_height = surface_info[l].max_height; 97bf215546Sopenharmony_ci *chroma_format = surface_info[l].chroma_format; 98bf215546Sopenharmony_ci *mc_type = surface_info[l].mc_type; 99bf215546Sopenharmony_ci *surface_flags = surface_info[l].flags; 100bf215546Sopenharmony_ci *subpic_max_w = surface_info[l].subpicture_max_width; 101bf215546Sopenharmony_ci *subpic_max_h = surface_info[l].subpicture_max_height; 102bf215546Sopenharmony_ci *screen = i; 103bf215546Sopenharmony_ci 104bf215546Sopenharmony_ci XVMC_MSG(XVMC_TRACE, "[XvMC] Found requested context surface format.\n" \ 105bf215546Sopenharmony_ci "[XvMC] screen=%u, port=%u\n" \ 106bf215546Sopenharmony_ci "[XvMC] id=0x%08X\n" \ 107bf215546Sopenharmony_ci "[XvMC] max width=%u, max height=%u\n" \ 108bf215546Sopenharmony_ci "[XvMC] chroma format=0x%08X\n" \ 109bf215546Sopenharmony_ci "[XvMC] acceleration level=0x%08X\n" \ 110bf215546Sopenharmony_ci "[XvMC] flags=0x%08X\n" \ 111bf215546Sopenharmony_ci "[XvMC] subpicture max width=%u, max height=%u\n", 112bf215546Sopenharmony_ci i, port, surface_type_id, max_width, max_height, *chroma_format, 113bf215546Sopenharmony_ci *mc_type, *surface_flags, *subpic_max_w, *subpic_max_h); 114bf215546Sopenharmony_ci } 115bf215546Sopenharmony_ci 116bf215546Sopenharmony_ci free(surface_info); 117bf215546Sopenharmony_ci } 118bf215546Sopenharmony_ci } 119bf215546Sopenharmony_ci 120bf215546Sopenharmony_ci XvFreeAdaptorInfo(adaptor_info); 121bf215546Sopenharmony_ci } 122bf215546Sopenharmony_ci 123bf215546Sopenharmony_ci if (!*found_port) { 124bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Could not find a suitable port.\n"); 125bf215546Sopenharmony_ci return XvBadPort; 126bf215546Sopenharmony_ci } 127bf215546Sopenharmony_ci if (!found_surface) { 128bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Could not find a suitable surface.\n"); 129bf215546Sopenharmony_ci return BadMatch; 130bf215546Sopenharmony_ci } 131bf215546Sopenharmony_ci if (width > max_width || height > max_height) { 132bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Requested context dimensions (w=%u,h=%u) too large (max w=%u,h=%u).\n", 133bf215546Sopenharmony_ci width, height, max_width, max_height); 134bf215546Sopenharmony_ci return BadValue; 135bf215546Sopenharmony_ci } 136bf215546Sopenharmony_ci if (flags != XVMC_DIRECT && flags != 0) { 137bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Invalid context flags 0x%08X.\n", flags); 138bf215546Sopenharmony_ci return BadValue; 139bf215546Sopenharmony_ci } 140bf215546Sopenharmony_ci 141bf215546Sopenharmony_ci return Success; 142bf215546Sopenharmony_ci} 143bf215546Sopenharmony_ci 144bf215546Sopenharmony_cistatic enum pipe_video_profile ProfileToPipe(int xvmc_profile) 145bf215546Sopenharmony_ci{ 146bf215546Sopenharmony_ci if (xvmc_profile & XVMC_MPEG_1) 147bf215546Sopenharmony_ci assert(0); 148bf215546Sopenharmony_ci if (xvmc_profile & XVMC_MPEG_2) 149bf215546Sopenharmony_ci return PIPE_VIDEO_PROFILE_MPEG2_MAIN; 150bf215546Sopenharmony_ci if (xvmc_profile & XVMC_H263) 151bf215546Sopenharmony_ci assert(0); 152bf215546Sopenharmony_ci if (xvmc_profile & XVMC_MPEG_4) 153bf215546Sopenharmony_ci assert(0); 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_ci assert(0); 156bf215546Sopenharmony_ci 157bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized profile 0x%08X.\n", xvmc_profile); 158bf215546Sopenharmony_ci 159bf215546Sopenharmony_ci return -1; 160bf215546Sopenharmony_ci} 161bf215546Sopenharmony_ci 162bf215546Sopenharmony_cistatic enum pipe_video_chroma_format FormatToPipe(int xvmc_format) 163bf215546Sopenharmony_ci{ 164bf215546Sopenharmony_ci switch (xvmc_format) { 165bf215546Sopenharmony_ci case XVMC_CHROMA_FORMAT_420: 166bf215546Sopenharmony_ci return PIPE_VIDEO_CHROMA_FORMAT_420; 167bf215546Sopenharmony_ci case XVMC_CHROMA_FORMAT_422: 168bf215546Sopenharmony_ci return PIPE_VIDEO_CHROMA_FORMAT_422; 169bf215546Sopenharmony_ci case XVMC_CHROMA_FORMAT_444: 170bf215546Sopenharmony_ci return PIPE_VIDEO_CHROMA_FORMAT_444; 171bf215546Sopenharmony_ci default: 172bf215546Sopenharmony_ci assert(0); 173bf215546Sopenharmony_ci } 174bf215546Sopenharmony_ci 175bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized format 0x%08X.\n", xvmc_format); 176bf215546Sopenharmony_ci 177bf215546Sopenharmony_ci return -1; 178bf215546Sopenharmony_ci} 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ciPUBLIC 181bf215546Sopenharmony_ciStatus XvMCCreateContext(Display *dpy, XvPortID port, int surface_type_id, 182bf215546Sopenharmony_ci int width, int height, int flags, XvMCContext *context) 183bf215546Sopenharmony_ci{ 184bf215546Sopenharmony_ci bool found_port; 185bf215546Sopenharmony_ci int scrn = 0; 186bf215546Sopenharmony_ci int chroma_format = 0; 187bf215546Sopenharmony_ci int mc_type = 0; 188bf215546Sopenharmony_ci int surface_flags = 0; 189bf215546Sopenharmony_ci unsigned short subpic_max_w = 0; 190bf215546Sopenharmony_ci unsigned short subpic_max_h = 0; 191bf215546Sopenharmony_ci Status ret; 192bf215546Sopenharmony_ci struct vl_screen *vscreen; 193bf215546Sopenharmony_ci struct pipe_context *pipe; 194bf215546Sopenharmony_ci struct pipe_video_codec templat = {0}; 195bf215546Sopenharmony_ci XvMCContextPrivate *context_priv; 196bf215546Sopenharmony_ci vl_csc_matrix csc; 197bf215546Sopenharmony_ci 198bf215546Sopenharmony_ci XVMC_MSG(XVMC_TRACE, "[XvMC] Creating context %p.\n", context); 199bf215546Sopenharmony_ci 200bf215546Sopenharmony_ci assert(dpy); 201bf215546Sopenharmony_ci 202bf215546Sopenharmony_ci if (!context) 203bf215546Sopenharmony_ci return XvMCBadContext; 204bf215546Sopenharmony_ci 205bf215546Sopenharmony_ci ret = Validate(dpy, port, surface_type_id, width, height, flags, 206bf215546Sopenharmony_ci &found_port, &scrn, &chroma_format, &mc_type, &surface_flags, 207bf215546Sopenharmony_ci &subpic_max_w, &subpic_max_h); 208bf215546Sopenharmony_ci 209bf215546Sopenharmony_ci /* Success and XvBadPort have the same value */ 210bf215546Sopenharmony_ci if (ret != Success || !found_port) 211bf215546Sopenharmony_ci return ret; 212bf215546Sopenharmony_ci 213bf215546Sopenharmony_ci /* XXX: Current limits */ 214bf215546Sopenharmony_ci if (chroma_format != XVMC_CHROMA_FORMAT_420) { 215bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Unsupported chroma format.\n"); 216bf215546Sopenharmony_ci return BadImplementation; 217bf215546Sopenharmony_ci } 218bf215546Sopenharmony_ci if ((mc_type & ~XVMC_IDCT) != (XVMC_MOCOMP | XVMC_MPEG_2)) { 219bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Non-MPEG2/Mocomp/iDCT acceleration unsupported.\n"); 220bf215546Sopenharmony_ci return BadImplementation; 221bf215546Sopenharmony_ci } 222bf215546Sopenharmony_ci if (surface_flags & XVMC_INTRA_UNSIGNED) { 223bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Unsigned intra unsupported.\n"); 224bf215546Sopenharmony_ci return BadImplementation; 225bf215546Sopenharmony_ci } 226bf215546Sopenharmony_ci 227bf215546Sopenharmony_ci context_priv = CALLOC(1, sizeof(XvMCContextPrivate)); 228bf215546Sopenharmony_ci if (!context_priv) 229bf215546Sopenharmony_ci return BadAlloc; 230bf215546Sopenharmony_ci 231bf215546Sopenharmony_ci /* TODO: Reuse screen if process creates another context */ 232bf215546Sopenharmony_ci vscreen = vl_dri3_screen_create(dpy, scrn); 233bf215546Sopenharmony_ci if (!vscreen) 234bf215546Sopenharmony_ci vscreen = vl_dri2_screen_create(dpy, scrn); 235bf215546Sopenharmony_ci 236bf215546Sopenharmony_ci if (!vscreen) { 237bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL screen.\n"); 238bf215546Sopenharmony_ci FREE(context_priv); 239bf215546Sopenharmony_ci return BadAlloc; 240bf215546Sopenharmony_ci } 241bf215546Sopenharmony_ci 242bf215546Sopenharmony_ci pipe = vscreen->pscreen->context_create(vscreen->pscreen, NULL, 0); 243bf215546Sopenharmony_ci if (!pipe) { 244bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL context.\n"); 245bf215546Sopenharmony_ci vscreen->destroy(vscreen); 246bf215546Sopenharmony_ci FREE(context_priv); 247bf215546Sopenharmony_ci return BadAlloc; 248bf215546Sopenharmony_ci } 249bf215546Sopenharmony_ci 250bf215546Sopenharmony_ci templat.profile = ProfileToPipe(mc_type); 251bf215546Sopenharmony_ci templat.entrypoint = (mc_type & XVMC_IDCT) ? PIPE_VIDEO_ENTRYPOINT_IDCT : PIPE_VIDEO_ENTRYPOINT_MC; 252bf215546Sopenharmony_ci templat.chroma_format = FormatToPipe(chroma_format); 253bf215546Sopenharmony_ci templat.width = width; 254bf215546Sopenharmony_ci templat.height = height; 255bf215546Sopenharmony_ci templat.max_references = 2; 256bf215546Sopenharmony_ci templat.expect_chunked_decode = true; 257bf215546Sopenharmony_ci 258bf215546Sopenharmony_ci context_priv->decoder = pipe->create_video_codec(pipe, &templat); 259bf215546Sopenharmony_ci 260bf215546Sopenharmony_ci if (!context_priv->decoder) { 261bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL decoder.\n"); 262bf215546Sopenharmony_ci pipe->destroy(pipe); 263bf215546Sopenharmony_ci vscreen->destroy(vscreen); 264bf215546Sopenharmony_ci FREE(context_priv); 265bf215546Sopenharmony_ci return BadAlloc; 266bf215546Sopenharmony_ci } 267bf215546Sopenharmony_ci 268bf215546Sopenharmony_ci if (!vl_compositor_init(&context_priv->compositor, pipe)) { 269bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL compositor.\n"); 270bf215546Sopenharmony_ci context_priv->decoder->destroy(context_priv->decoder); 271bf215546Sopenharmony_ci pipe->destroy(pipe); 272bf215546Sopenharmony_ci vscreen->destroy(vscreen); 273bf215546Sopenharmony_ci FREE(context_priv); 274bf215546Sopenharmony_ci return BadAlloc; 275bf215546Sopenharmony_ci } 276bf215546Sopenharmony_ci 277bf215546Sopenharmony_ci if (!vl_compositor_init_state(&context_priv->cstate, pipe)) { 278bf215546Sopenharmony_ci XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL compositor state.\n"); 279bf215546Sopenharmony_ci vl_compositor_cleanup(&context_priv->compositor); 280bf215546Sopenharmony_ci context_priv->decoder->destroy(context_priv->decoder); 281bf215546Sopenharmony_ci pipe->destroy(pipe); 282bf215546Sopenharmony_ci vscreen->destroy(vscreen); 283bf215546Sopenharmony_ci FREE(context_priv); 284bf215546Sopenharmony_ci return BadAlloc; 285bf215546Sopenharmony_ci } 286bf215546Sopenharmony_ci 287bf215546Sopenharmony_ci 288bf215546Sopenharmony_ci context_priv->color_standard = 289bf215546Sopenharmony_ci debug_get_bool_option("G3DVL_NO_CSC", FALSE) ? 290bf215546Sopenharmony_ci VL_CSC_COLOR_STANDARD_IDENTITY : VL_CSC_COLOR_STANDARD_BT_601; 291bf215546Sopenharmony_ci context_priv->procamp = vl_default_procamp; 292bf215546Sopenharmony_ci 293bf215546Sopenharmony_ci vl_csc_get_matrix 294bf215546Sopenharmony_ci ( 295bf215546Sopenharmony_ci context_priv->color_standard, 296bf215546Sopenharmony_ci &context_priv->procamp, true, &csc 297bf215546Sopenharmony_ci ); 298bf215546Sopenharmony_ci vl_compositor_set_csc_matrix(&context_priv->cstate, (const vl_csc_matrix *)&csc, 1.0f, 0.0f); 299bf215546Sopenharmony_ci 300bf215546Sopenharmony_ci context_priv->vscreen = vscreen; 301bf215546Sopenharmony_ci context_priv->pipe = pipe; 302bf215546Sopenharmony_ci context_priv->subpicture_max_width = subpic_max_w; 303bf215546Sopenharmony_ci context_priv->subpicture_max_height = subpic_max_h; 304bf215546Sopenharmony_ci 305bf215546Sopenharmony_ci context->context_id = XAllocID(dpy); 306bf215546Sopenharmony_ci context->surface_type_id = surface_type_id; 307bf215546Sopenharmony_ci context->width = width; 308bf215546Sopenharmony_ci context->height = height; 309bf215546Sopenharmony_ci context->flags = flags; 310bf215546Sopenharmony_ci context->port = port; 311bf215546Sopenharmony_ci context->privData = context_priv; 312bf215546Sopenharmony_ci 313bf215546Sopenharmony_ci SyncHandle(); 314bf215546Sopenharmony_ci 315bf215546Sopenharmony_ci XVMC_MSG(XVMC_TRACE, "[XvMC] Context %p created.\n", context); 316bf215546Sopenharmony_ci 317bf215546Sopenharmony_ci return Success; 318bf215546Sopenharmony_ci} 319bf215546Sopenharmony_ci 320bf215546Sopenharmony_ciPUBLIC 321bf215546Sopenharmony_ciStatus XvMCDestroyContext(Display *dpy, XvMCContext *context) 322bf215546Sopenharmony_ci{ 323bf215546Sopenharmony_ci XvMCContextPrivate *context_priv; 324bf215546Sopenharmony_ci 325bf215546Sopenharmony_ci XVMC_MSG(XVMC_TRACE, "[XvMC] Destroying context %p.\n", context); 326bf215546Sopenharmony_ci 327bf215546Sopenharmony_ci assert(dpy); 328bf215546Sopenharmony_ci 329bf215546Sopenharmony_ci if (!context || !context->privData) 330bf215546Sopenharmony_ci return XvMCBadContext; 331bf215546Sopenharmony_ci 332bf215546Sopenharmony_ci context_priv = context->privData; 333bf215546Sopenharmony_ci context_priv->decoder->destroy(context_priv->decoder); 334bf215546Sopenharmony_ci vl_compositor_cleanup_state(&context_priv->cstate); 335bf215546Sopenharmony_ci vl_compositor_cleanup(&context_priv->compositor); 336bf215546Sopenharmony_ci context_priv->pipe->destroy(context_priv->pipe); 337bf215546Sopenharmony_ci context_priv->vscreen->destroy(context_priv->vscreen); 338bf215546Sopenharmony_ci FREE(context_priv); 339bf215546Sopenharmony_ci context->privData = NULL; 340bf215546Sopenharmony_ci 341bf215546Sopenharmony_ci XVMC_MSG(XVMC_TRACE, "[XvMC] Context %p destroyed.\n", context); 342bf215546Sopenharmony_ci 343bf215546Sopenharmony_ci return Success; 344bf215546Sopenharmony_ci} 345