diff --git a/aegisub/gl_wrap.cpp b/aegisub/gl_wrap.cpp index 9369ebadb..d230acc25 100644 --- a/aegisub/gl_wrap.cpp +++ b/aegisub/gl_wrap.cpp @@ -408,7 +408,7 @@ GLuint OpenGLWrapper::CreateYV12PixelShader() { ///////////////////////////////////// // Create YV12->RGB32 Shader Program -GLuint OpenGLWrapper::CreateYV12Shader(float tw,float th) { +GLuint OpenGLWrapper::CreateYV12Shader(float tw,float th,float tws) { // Create vertex shader GLuint ver = OpenGLWrapper::CreateStandardVertexShader(); if (glGetError() != 0) throw _T("Error creating generic vertex shader"); @@ -431,7 +431,7 @@ GLuint OpenGLWrapper::CreateYV12Shader(float tw,float th) { address = glGetUniformLocationARB(program,"off1"); glUniform2fARB(address, 0.0f, th); address = glGetUniformLocationARB(program,"off2"); - glUniform2fARB(address, tw*0.5f, th); + glUniform2fARB(address, tws, th); // Return shader return program; diff --git a/aegisub/gl_wrap.h b/aegisub/gl_wrap.h index 8daa998de..121fc8b40 100644 --- a/aegisub/gl_wrap.h +++ b/aegisub/gl_wrap.h @@ -66,5 +66,5 @@ public: static bool ShadersAvailable(); static void SetShader(GLuint i); static void DestroyShaderProgram(GLuint i); - static GLuint CreateYV12Shader(float tw,float th); + static GLuint CreateYV12Shader(float tw,float th,float tws); }; diff --git a/aegisub/video_context.cpp b/aegisub/video_context.cpp index a014cef0d..8dc224b6f 100644 --- a/aegisub/video_context.cpp +++ b/aegisub/video_context.cpp @@ -452,7 +452,7 @@ GLuint VideoContext::GetFrameAsTexture(int n) { // Load image data into texture int height = frame.h; if (frame.format == FORMAT_YV12) height = height * 3 / 2; - int tw = SmallestPowerOf2(frame.w); + int tw = SmallestPowerOf2(MAX(frame.pitch[0],frame.pitch[1]+frame.pitch[2])); int th = SmallestPowerOf2(height); texW = float(frame.w)/float(tw); texH = float(frame.h)/float(th); @@ -469,12 +469,12 @@ GLuint VideoContext::GetFrameAsTexture(int n) { // Create shader if necessary if (frame.format == FORMAT_YV12 && yv12shader == 0) { - yv12shader = OpenGLWrapper::CreateYV12Shader(texW,texH); + yv12shader = OpenGLWrapper::CreateYV12Shader(texW,texH,float(frame.pitch[1])/float(tw)); } } // Load texture data - glTexSubImage2D(GL_TEXTURE_2D,0,0,0,frame.w,frame.h,format,GL_UNSIGNED_BYTE,frame.data[0]); + glTexSubImage2D(GL_TEXTURE_2D,0,0,0,frame.pitch[0],frame.h,format,GL_UNSIGNED_BYTE,frame.data[0]); if (glGetError() != 0) throw _T("Error uploading primary plane"); // UV planes for YV12 @@ -485,9 +485,9 @@ GLuint VideoContext::GetFrameAsTexture(int n) { u = 2; v = 1; } - glTexSubImage2D(GL_TEXTURE_2D,0,0,frame.h,frame.w/2,frame.h/2,format,GL_UNSIGNED_BYTE,frame.data[u]); + glTexSubImage2D(GL_TEXTURE_2D,0,0,frame.h,frame.pitch[1],frame.h/2,format,GL_UNSIGNED_BYTE,frame.data[u]); if (glGetError() != 0) throw _T("Error uploading U plane."); - glTexSubImage2D(GL_TEXTURE_2D,0,frame.w/2,frame.h,frame.w/2,frame.h/2,format,GL_UNSIGNED_BYTE,frame.data[v]); + glTexSubImage2D(GL_TEXTURE_2D,0,frame.pitch[1],frame.h,frame.pitch[2],frame.h/2,format,GL_UNSIGNED_BYTE,frame.data[v]); if (glGetError() != 0) throw _T("Error uploadinv V plane."); } diff --git a/aegisub/video_frame.cpp b/aegisub/video_frame.cpp index 282558c57..95161511a 100644 --- a/aegisub/video_frame.cpp +++ b/aegisub/video_frame.cpp @@ -49,7 +49,7 @@ void AegiVideoFrame::Reset() { memSize = 0; w = 0; h = 0; - format = FORMAT_RGB24; + format = FORMAT_NONE; flipped = false; cppAlloc = true; invertChannels = true; @@ -85,10 +85,20 @@ AegiVideoFrame::AegiVideoFrame(int width,int height,VideoFrameFormat fmt) { //////////// // Allocate void AegiVideoFrame::Allocate() { + // Check consistency + wxASSERT(pitch[0] > 0 && pitch[0] < 10000); + wxASSERT(w > 0 && w < 10000); + wxASSERT(h > 0 && h < 10000); + wxASSERT(format != FORMAT_NONE); + // Get size int height = h; unsigned int size; - if (format == FORMAT_YV12) size = pitch[0] * height * 3 / 2; + if (format == FORMAT_YV12) { + wxASSERT(pitch[1] > 0 && pitch[1] < 10000); + wxASSERT(pitch[2] > 0 && pitch[2] < 10000); + size = pitch[0]*height + (pitch[1]+pitch[2])*height/2; + } else size = pitch[0] * height; // Reallocate, if necessary @@ -104,11 +114,12 @@ void AegiVideoFrame::Allocate() { // Planar if (format == FORMAT_YV12) { data[1] = data[0] + (pitch[0]*height); - data[2] = data[0] + (pitch[0]*height*5/4); + data[2] = data[0] + (pitch[0]*height+pitch[1]*height/2); } - } - cppAlloc = true; + // Flag as allocated by C++ + cppAlloc = true; + } } diff --git a/aegisub/video_frame.h b/aegisub/video_frame.h index a33603b69..e0c3dea84 100644 --- a/aegisub/video_frame.h +++ b/aegisub/video_frame.h @@ -40,6 +40,7 @@ ////////////////////// // Video Frame format enum VideoFrameFormat { + FORMAT_NONE, FORMAT_RGB24, FORMAT_RGB32, FORMAT_YUY2, diff --git a/aegisub/video_provider_dshow.cpp b/aegisub/video_provider_dshow.cpp index 7b4107ae3..1fc7a7edc 100644 --- a/aegisub/video_provider_dshow.cpp +++ b/aegisub/video_provider_dshow.cpp @@ -419,34 +419,25 @@ void DirectShowVideoProvider::ReadFrame(long long timestamp, unsigned format, un df->frame.w = width; df->frame.h = height; df->frame.pitch[0] = stride; + if (format == IVS_YV12) { + df->frame.pitch[1] = stride/2; + df->frame.pitch[2] = stride/2; + } df->frame.cppAlloc = false; df->frame.invertChannels = true; - // Planar - if (format == IVS_YUY2) { - df->frame.format = FORMAT_YUY2; + // Set format + if (format == IVS_RGB24) df->frame.format = FORMAT_RGB24; + else if (format == IVS_RGB32) df->frame.format = FORMAT_RGB32; + else if (format == IVS_YV12) { + df->frame.format = FORMAT_YV12; + df->frame.invertChannels = true; } + else if (format == IVS_YUY2) df->frame.format = FORMAT_YUY2; - // Interleaved - else { - // Set format - if (format == IVS_RGB24) df->frame.format = FORMAT_RGB24; - else if (format == IVS_RGB32) df->frame.format = FORMAT_RGB32; - else if (format == IVS_YV12) df->frame.format = FORMAT_YV12; - - // Allocate - unsigned int datalen = stride*height; - df->frame.Allocate(); - - // Prepare data for YV12 - if (format == IVS_YV12) { - datalen = datalen * 3 / 2; - df->frame.invertChannels = true; - } - - // Copy - memcpy(df->frame.data[0],src,datalen); - } + // Allocate and copy data + df->frame.Allocate(); + memcpy(df->frame.data[0],src,df->frame.pitch[0]*height + (df->frame.pitch[1]+df->frame.pitch[2])*height/2); } diff --git a/aegisub/video_provider_lavc.cpp b/aegisub/video_provider_lavc.cpp index eea6ffcc9..c9b42a611 100644 --- a/aegisub/video_provider_lavc.cpp +++ b/aegisub/video_provider_lavc.cpp @@ -85,6 +85,7 @@ LAVCVideoProvider::LAVCVideoProvider(wxString filename,double fps) { validFrame = false; // Load + SetCacheMax(8); LoadVideo(filename,fps); } @@ -389,21 +390,24 @@ const AegiVideoFrame LAVCVideoProvider::DoGetFrame(int n) { switch (format) { case PIX_FMT_BGR24: final.invertChannels = true; case PIX_FMT_RGB24: final.format = FORMAT_RGB24; break; + #ifdef __WINDOWS__ case PIX_FMT_BGR32: final.invertChannels = true; + #endif case PIX_FMT_RGB32: final.format = FORMAT_RGB32; break; case PIX_FMT_YUYV422: final.format = FORMAT_YUY2; break; case PIX_FMT_YUV420P: final.format = FORMAT_YV12; break; + default: throw _T("ffmpeg returned an unknown frame format."); } // Allocate - final.pitch[0] = final.w * final.GetBpp(); + for (int i=0;i<4;i++) final.pitch[i] = frame->linesize[i]; final.Allocate(); // Copy data if (final.format == FORMAT_YV12) { - memcpy(final.data[0],frame->data[0],final.w * final.h); - memcpy(final.data[1],frame->data[1],final.w * final.h / 4); - memcpy(final.data[2],frame->data[2],final.w * final.h / 4); + memcpy(final.data[0],frame->data[0],frame->linesize[0] * final.h); + memcpy(final.data[1],frame->data[1],frame->linesize[1] * final.h / 2); + memcpy(final.data[2],frame->data[2],frame->linesize[2] * final.h / 2); } else memcpy(final.data[0],frame->data[0],size); }