VideoFrame VideoDecoderFFmpegHW::copyToFrame(const VideoFormat& fmt, int surface_h, quint8 *src[], int pitch[], bool swapUV) { DPTR_D(VideoDecoderFFmpegHW); Q_ASSERT_X(src[0] && pitch[0] > 0, "VideoDecoderFFmpegHW::copyToFrame", "src[0] and pitch[0] must be set"); const int nb_planes = fmt.planeCount(); const int chroma_pitch = nb_planes > 1 ? fmt.bytesPerLine(pitch[0], 1) : 0; const int chroma_h = fmt.chromaHeight(surface_h); int h[] = { surface_h, 0, 0}; for (int i = 1; i < nb_planes; ++i) { h[i] = chroma_h; // set chroma address and pitch if not set if (pitch[i] <= 0) pitch[i] = chroma_pitch; if (!src[i]) src[i] = src[i-1] + pitch[i-1]*h[i-1]; } if (swapUV) { std::swap(src[1], src[2]); std::swap(pitch[1], pitch[2]); } VideoFrame frame; if (copyMode() == VideoDecoderFFmpegHW::OptimizedCopy && d.gpu_mem.isReady()) { int yuv_size = 0; for (int i = 0; i < nb_planes; ++i) { yuv_size += pitch[i]*h[i]; } // additional 15 bytes to ensure 16 bytes aligned QByteArray buf(15 + yuv_size, 0); const int offset_16 = (16 - ((uintptr_t)buf.data() & 0x0f)) & 0x0f; // plane 1, 2... is aligned? uchar* plane_ptr = (uchar*)buf.data() + offset_16; QVector<uchar*> dst(nb_planes, 0); for (int i = 0; i < nb_planes; ++i) { dst[i] = plane_ptr; // TODO: add VideoFormat::planeWidth/Height() ? // pitch instead of surface_width plane_ptr += pitch[i] * h[i]; d.gpu_mem.copyFrame(src[i], dst[i], pitch[i], h[i], pitch[i]); } frame = VideoFrame(buf, width(), height(), fmt); frame.setBits(dst); frame.setBytesPerLine(pitch); } else { frame = VideoFrame(width(), height(), fmt); frame.setBits(src); frame.setBytesPerLine(pitch); // TODO: why clone is faster()? // TODO: buffer pool and create VideoFrame when needed to avoid copy? also for other va frame = frame.clone(); } frame.setTimestamp(double(d.frame->pkt_pts)/1000.0); frame.setDisplayAspectRatio(d.getDAR(d.frame)); d.updateColorDetails(&frame); return frame; }
void GLWidgetRendererPrivate::uploadPlane(int p, GLint internalFormat, GLenum format, const QRect& roi) { // FIXME: why happens on win? if (video_frame.bytesPerLine(p) <= 0) return; glActiveTexture(GL_TEXTURE0 + p); //xbmc: only for es, not for desktop? glBindTexture(GL_TEXTURE_2D, textures[p]); ////nv12: 2 //glPixelStorei(GL_UNPACK_ALIGNMENT, 1);//GetAlign(video_frame.bytesPerLine(p))); #if defined(GL_UNPACK_ROW_LENGTH) // glPixelStorei(GL_UNPACK_ROW_LENGTH, video_frame.bytesPerLine(p)); #endif setupQuality(); //qDebug("bpl[%d]=%d width=%d", p, video_frame.bytesPerLine(p), video_frame.planeWidth(p)); // This is necessary for non-power-of-two textures glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //uploading part of image eats less gpu memory, but may be more cpu(gles) //FIXME: more cpu usage then qpainter. FBO, VBO? //roi for planes? if (ROI_TEXCOORDS || roi.size() == video_frame.size()) { glTexSubImage2D(GL_TEXTURE_2D , 0 //level , 0 // xoffset , 0 // yoffset , texture_size[p].width() , texture_size[p].height() , format //format, must the same as internal format? , data_type[p] , video_frame.bits(p)); } else { int roi_x = roi.x(); int roi_y = roi.y(); int roi_w = roi.width(); int roi_h = roi.height(); int plane_w = video_frame.planeWidth(p); VideoFormat fmt = video_frame.format(); if (p == 0) { plane0Size = QSize(roi_w, roi_h); } else { roi_x = fmt.chromaWidth(roi_x); roi_y = fmt.chromaHeight(roi_y); roi_w = fmt.chromaWidth(roi_w); roi_h = fmt.chromaHeight(roi_h); } qDebug("roi: %d, %d %dx%d", roi_x, roi_y, roi_w, roi_h); #if 0// defined(GL_UNPACK_ROW_LENGTH) && defined(GL_UNPACK_SKIP_PIXELS) // http://stackoverflow.com/questions/205522/opengl-subtexturing glPixelStorei(GL_UNPACK_ROW_LENGTH, plane_w); //glPixelStorei or compute pointer glPixelStorei(GL_UNPACK_SKIP_PIXELS, roi_x); glPixelStorei(GL_UNPACK_SKIP_ROWS, roi_y); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, roi_w, roi_h, 0, format, GL_UNSIGNED_BYTE, video_frame.bits(p)); //glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, roi_w, roi_h, format, GL_UNSIGNED_BYTE, video_frame.bits(p)); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); #else // GL ES //define it? or any efficient way? //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, roi_w, roi_h, 0, format, GL_UNSIGNED_BYTE, NULL); const char *src = (char*)video_frame.bits(p) + roi_y*plane_w + roi_x*fmt.bytesPerPixel(p); #define UPLOAD_LINE 1 #if UPLOAD_LINE for (int y = 0; y < roi_h; y++) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, roi_w, 1, format, GL_UNSIGNED_BYTE, src); } #else int line_size = roi_w*fmt.bytesPerPixel(p); char *sub = (char*)malloc(roi_h*line_size); char *dst = sub; for (int y = 0; y < roi_h; y++) { memcpy(dst, src, line_size); src += video_frame.bytesPerLine(p); dst += line_size; } // FIXME: crash glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, roi_w, roi_h, format, GL_UNSIGNED_BYTE, sub); free(sub); #endif //UPLOAD_LINE #endif //GL_UNPACK_ROW_LENGTH } #if defined(GL_UNPACK_ROW_LENGTH) // glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); #endif //glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glBindTexture(GL_TEXTURE_2D, 0); }