2013-10-07 37 views
3

我正在使用.NET 4/C#基於Winforms的視頻播放器控件。我們已經收到了客戶的一些報告,但是,幀回放有時是不穩定的 - 每分鐘幾次播放會有明顯的滯後。顯示器是一個圖片盒 - 我們每40毫秒更換一次圖片(對於25fps視頻)。Windows窗體的圖片框不能可靠地刷新

修復了一些可能導致它(並使播放更平滑)的東西后,我們偶爾會看到放緩,特別是當窗口最大化時,我們需要繪製全尺寸的視頻播放器進行屏幕顯示。我注意到,在此場景中更新圖像後,Picturebox最多可能需要7-12ms才能刷新,並且影響非常明顯。

所以我嘗試了不同的方法,我們有兩個pictureboxes(PB1和PB2)。 PB1顯示當前幀,而PB2隱藏。然後,我們在下一幀解碼後立即更新PB2,並在顯示下一幀之前全部更新圖像。一旦它顯示下一幀的時間,我們顯示PB1並隱藏PB2。對於下一幀,我們更新PB1,顯示它並隱藏PB2。沖洗並重復。此操作需要1ms,但即使在測試應用程序中反覆顯示相同的兩個幀,我們仍然會發現全屏偶爾會出現滯後現象(儘管控制檯日誌顯示更新始終間隔40毫秒)。

這是Winforms本身的限制嗎?有沒有辦法獲得毫秒準確的更新控件?下面是我上面提到的代碼,用我們2個預裝的圖像之間交替,其中一個測試應用程序:

namespace WindowsFormsApplication1 
{ 
public partial class Form1 : Form 
{ 

    private Bitmap image1; 
    private Bitmap image2; 
    private int i = 0; 

    public Form1() 
    { 
     InitializeComponent(); 
     image1 = new Bitmap("image1.bmp"); 
     image2 = new Bitmap("image2.bmp"); 
     pictureBox1.Image = ResizeBitmap(image1, pictureBox1.Width, pictureBox1.Height); 
     pictureBox2.Image = ResizeBitmap(image2, pictureBox2.Width, pictureBox2.Height); 

     pictureBox1.Show(); 
     pictureBox2.Hide(); 
    } 

    public delegate void invoke(); 

    private void redraw() 
    { 
     System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); 

     Console.WriteLine(DateTime.UtcNow.Millisecond); 

     stopwatch.Restart(); 

     if (i % 2 == 0) 
     { 
      pictureBox1.BringToFront();     
     } 
     else 
     { 
      pictureBox2.BringToFront();     
     } 


     stopwatch.Stop(); 
     Console.WriteLine("Bring to front: " + stopwatch.ElapsedMilliseconds); 
     Console.WriteLine(DateTime.UtcNow.Millisecond); 

     i++; 
    } 

    private Bitmap ResizeBitmap(Bitmap sourceBMP, int width, int height) 
    { 
     Bitmap result = new Bitmap(width, height); 
     using (Graphics g = Graphics.FromImage(result)) 
      g.DrawImage(sourceBMP, 0, 0, width, height); 
     return result; 
    } 

    private void Form1_SizeChanged(object sender, EventArgs e) 
    { 
     pictureBox1.Show(); 
     pictureBox2.Show(); 
     pictureBox1.Image = ResizeBitmap(image1, pictureBox1.Width, pictureBox1.Height); 
     pictureBox2.Image = ResizeBitmap(image2, pictureBox2.Width, pictureBox2.Height); 
     pictureBox1.Refresh(); 
     pictureBox2.Refresh(); 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     Action<object> action = (object obj) => 
     { 
      while (true) 
      { 
       BeginInvoke(new invoke(redraw));      
       System.Threading.Thread.Sleep(40); 
      } 
     }; 

     System.Threading.Tasks.Task t1 = new System.Threading.Tasks.Task(action, "a"); 
     t1.Start(); 
    } 
} 

}

時機點上,並沒有處理髮生的事情,可能會導致延遲。看來這個圖像盒每40ms就不能可靠地更新一次。

我正在考慮將視頻播放器移植到SFML(嵌入Winforms中),並且早期測試顯示沒有明顯的延遲,因此很有希望。然而,無論如何,要讓Picturebox可靠地更新,還是僅僅有幾毫秒的精度?

+0

我知道這很有趣,試圖重新發明輪子,但你有沒有嘗試過使用優秀的老式嵌入式媒體播放器或VlcDotNet? http://msdn.microsoft.com/en-us/library/bb383953(v=vs.90).aspx http://vlcdotnet.codeplex.com/ – JuStDaN

+0

不幸的是,這些並不適合我們的應用程序。它需要向後和向前的幀步進,這些步驟不提供以及定製覆蓋。該視頻播放器本身實際上已用於生產1 - 2年,除此問題外,其效果還不錯。目前看起來像更改爲SFML的繪圖可以工作。 – awr

回答

3

你沒有得到40毫秒的更新。 Timer和Thread.Sleep()的精度由Windows時鐘中斷率決定。每秒鐘64次,每15.625次。所以當你問40時,你會得到下一個整數倍,3 x 15.625 = 46.875毫秒。

不是真正的問題。該代碼是不完整的,它不顯示更新Image屬性的代碼的任何跟蹤。除了SizeChanged事件處理程序,它有一個經典問題。你沒有處理舊的位圖。它需要看起來像這樣:

private void Form1_SizeChanged(object sender, EventArgs e) 
{ 
    if (pictureBox1.Image != null) pictureBox1.Image.Dispose(); 
    pictureBox1.Image = ResizeBitmap(image1, pictureBox1.Width, pictureBox1.Height); 
    if (pictureBox2.Image != null) pictureBox2.Image.Dispose(); 
    pictureBox2.Image = ResizeBitmap(image2, pictureBox2.Width, pictureBox2.Height); 
} 

無需調用Show()和Refresh()方法。如果在更新其餘代碼中的Image屬性時,如果還忘記調用Dispose(),那麼生澀的回放很容易被很多頁面錯誤所解釋,程序將觸發使用如此多的非託管內存和垃圾收集器所需的辛勤工作做再次回收它。

通過關注所創建圖像的大小和像素格式,可以獲得進一步的改進。調整大小以適合picturebox非常昂貴,所以請確保它已經具有合適的大小,因此不需要重新調整大小。而PixelFormat.Format32bppPArgb的繪製速度比其他所有繪製的繪製速度快倍,總是青睞它。 .NET使得忽略這些細節變得容易,它會採用你所拋棄的東西。但這不是免費的。

+0

感謝您的回覆。正如我上面提到的,這只是一個簡單的測試應用程序來演示問題。我實際上並沒有改變圖片框中的圖像,只是交替顯示我正在顯示的圖片框(所以它將顯示圖像1,等待40ms,顯示圖像2,等待40ms,再次顯示圖像1等)。所以繪畫不是問題。 16毫秒計時器的準確性可以解釋它,但我記錄DateTime.UtcNow.Millisecond,它總是在40或41毫秒的時間間隔。 – awr

+0

這段代碼與用戶實際上抱怨的「基於Winforms的視頻播放器控件」有沒有相似之處? –

+0

表面相似。實際播放器使用FFMPEG轉換和調整幀大小並將其存儲爲位圖,使用2ms睡眠的緊密循環來確定何時顯示圖像,存儲圖像框的前一圖像,將位圖分配給圖像框,以及配置以前的圖像(它只使用一個圖片框)。主要的相似之處在於它們每40ms更新一次(記錄當前時間顯示精確到+/- 2ms),然而兩者顯示相同的偶然滯後。 – awr