C# – Viết chương trình Media Player với MCI (update 26/4/2011)

Trong bài này, tôi sẽ trình bày cách tạo một chương trình để chơi các định dạng âm thanh và video với các chức năng cơ bản như play, pause, stop, seek…thông qua Media Control Interface (MCI). Để biết thêm các thông tin và cách sử dụng MCI bạn có thể tham khảo trên MSDN của Microsoft.

Download sourcecode (14KB)

update: 26/4/2011

– Bổ sung sourcecode, hình ảnh

– Thay đổi một vài phương thức

– Bổ sung phần lấy thông tin lỗi với mci

Do yêu cầu của bạn tien nên tôi cập nhật mã nguồn của chương trình demo này. Chương trình chỉ nhằm mục đích giới thiệu cách dùng thanh trackbar để điều khiển vị trí play của media.

Giao diện mô phỏng của ứng dụng sau khi hoàn thành.

Thiết kế

Trước tiên, để ứng dụng của bạn OOP hơn, hãy tạo một lớp để quản lý chức năng chơi nhạc, ví dụ ở đây tôi đặt tên lớp là MciPlayer. Lớp này sẽ chứa những hàm cơ bản để ta điều khiển quá trình chơi nhạc.

Để sử dụng được MCI bạn cần thêm hàm API mciSendString  nằm trong tập tin winmm.dll  của Windows. Hãy thêm namespace System.Runtime.InteropServices vào trước khi import tập tin winmm.dll. Đây là đoạn mã import thư viện winmm.dll và khai báo hàm API mciSendString để sử dụng trong chương trình.

[DllImport("winmm.dll")]
private static extern int mciSendString(string strCommand, StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);

Trong đó bạn cần chú ý đển tham số strCommand, đây là chuỗi chứa lệnh MCI cần thực hiện. Bạn có thể xem danh sách lệnh tại địa chỉ bên dưới

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_multimedia_command_strings.asp

Sau đây là các phương thức căn bản trong lớp MciPlayer của ta.

Mở và đóng media:

public void Open(string fileName)
{
	_command = "open \"" + fileName + "\" type mpegvideo alias YinYangMedia";
	mciSendString(_command, null, 0, IntPtr.Zero);
}
public void Close()
{
	_command = "close YinYangMedia";
	mciSendString(_command, null, 0, IntPtr.Zero);
}

Trong chuỗi sCommand, chú ý rằng đường dẫn tập tin media của bạn phải chứa trong ngoặc kép nên ta phải dùng chuỗi \” để bao phần filePath lại. Ở đây chuỗi mpegvideo để xác định rằng chúng ta có thể chơi hầu hết các định dạng media hiện nay như mp3, avi, wav, mpeg, mpg and wmv…Ở đây tôi sử dụng tên định danh YinYangMedia cho tập tin Media, bạn có thể thay đổi tên khác tùy thích nhưng phải thống nhất trong những lần gọi hàm khác.

Play, Pause, Stop, Seek media:

public void Play()
{
	Play(false);
}
public void Play(bool loop)
{
	_command = "play YinYangMedia";
	if (loop)
		_command += " repeat";
	mciSendString(_command, null, 0, IntPtr.Zero);
}

public void Pause()
{
	_command = "pause YinYangMedia";
	mciSendString(_command, null, 0, IntPtr.Zero);

}
public void Stop()
{
	mciSendString("stop YinYangMedia", null, 0, IntPtr.Zero);
}
public void Seek(int miliseconds){
	if (this.Status().Equals("playing",StringComparison.OrdinalIgnoreCase))
	{
		_command = "play YinYangMedia from " + miliseconds;
		mciSendString(_command, null, 0, IntPtr.Zero);
	}
	else
	{
		_command = "seek YinYangMedia to " + miliseconds;
		mciSendString(_command, null, 0, IntPtr.Zero);
	}
}

Phương thức Seek sẽ nhảy đến vị trí xác định bằng tham số value truyền vào và play tại vị trí đó, tham số value truyền vào có đơn vị là milisecond.

Trường hợp bạn muốn chơi một file media khác, trước tiên bạn cần đóng (close) media hiện tại. Để tiện hơn bạn có thể thêm phương thức Close() vào đầu phương thức Open() phía trên.

Hiển thị cửa sổ xem video

Chức năng play trên chỉ đơn giản làm nhiệm vụ phát âm thanh. Nếu bạn đang chơi một file video và cần hiển thị hình ảnh, hãy sử dụng phương thức sau:

public void DisplayMediaWindow()
{
	mciSendString("put YinYangMedia", null, 0, IntPtr.Zero);
}

Phương thức này sẽ mở cửa sổ video với kích thước mặc định, nếu bạn muốn xác định vị trí và kích thước của cửa sổ được mở, hãy overload hoặc sửa lại phương thức này như sau:

public void DisplayMediaWindow(Rectangle rec)
{
	mciSendString(String.Format("put YinYangMedia window at {0} {1} {2} {3}",
	                            rec.Left,rec.Top,rec.Width,rec.Height), null, 0,IntPtr.Zero);
}

Phương thức mới này truyền vào đối tượng Rectangle đại diện cho vị trí và kích thước của cửa sổ sẽ mở với gốc tọa độ là góc trên bên trái màn hình. Thay vì truyền vào một Rectangle, bạn cũng có thể overload thêm một phương thức truyền vào 4 tham số xác định vị trí left, top, width và height của cửa sổ.

Lấy trạng thái của player

Để xác định xem player của chúng ta đang ở trạng thái nào, bạn hãy viết một phương thức hoặc getter như sau:

public string Status
{
	get{
		mciSendString("status YinYangMedia mode", _buffer, BUFFER_LENGTH, IntPtr.Zero);
		return _buffer.ToString();
	}
}

Tham số strBuffer là biến lưu kết quả trả về, length là độ dài của độ dài của bộ đệm chứa kết quả tính bằng kí tự.

Bởi vì kết quả là kiểu string và chỉ thuộc một trong 3 trạng thái “playing”,”paused”, “stopped”. Bạn có thể tạo một enum chứa các trạng thái của player này, bổ sung thêm một trạng thái “Ready” nếu như hàm Open thực thi thành công, mỗi khi thực hiện lệnh nào đó, player của chúng ta sẽ tự động thay đổi trạng thái. Ví dụ như sau:

public enum PlayerStatus { Ready, Playing, Paused, Stopped };

Gợi ý: bạn thêm một thuộc tính kiểu PlayerStatus và danh sách các tập tin Media cần chơi vào player, khi chơi hết một bài hãy tiếp tục bài tiếp theo trong danh sách.

Lấy chiều dài, vị trí hiện tại của media:

Lấy tổng thời gian cần để chơi hết file tính bằng milisecond.

public int Length
{
	get
	{
		mciSendString("status YinYangMedia length", _buffer, BUFFER_LENGTH, IntPtr.Zero);
		try
		{
			return (int)Math.Floor(Convert.ToDouble(_buffer.ToString().Trim()));
		}
		catch { return 0; }
	}
}

Vị trí đang chơi hiện tại của file tính bằng milisecond

public int CurrentMilisecond
{
	get
	{
		mciSendString("status YinYangMedia position", _buffer, BUFFER_LENGTH,IntPtr.Zero);
		try
		{
			return (int)Math.Floor(Convert.ToDouble(_buffer.ToString().Trim()));
		}
		catch  { return 0; }
	}
	set {
		Seek(value);
	}
}

Để hiển thị theo dạng mm:ss bạn cần viết thêm phương thức để tính số phút và số giây, sau đó format theo định dạng thích hợp. Ví dụ bạn tạo ở Form thực thi hương thức sau để format:

private string FormatSeconds(long seconds)

{

long minus = 0;

if (seconds < 3600)

{

minus = seconds / 60;

seconds = seconds % 60;

return minus.ToString().PadLeft(2, ‘0’)

+ “:” + seconds.ToString().PadLeft(2, ‘0’);

}

// …

Hàm PadLeft sẽ lấp đầy bên trái chuỗi  bằng kí tự ‘0’ cho đến khi đủ số kí tự quy định. Giả sử số phút là 1 và giây là 2, kết quả trả về sẽ là ‘01:02’.

Phần cốt lõi của chương trình đã hoàn thành, bạn hãy tạo giao diện và thêm vào một trackBar để kiếm tra phương thức Seek() và các chức năng khác của lớp MciPlayer.

Lấy các thông tin lỗi trong MciPlayer

Phương thức mciSendString() trả về một kiểu số có giá trị bằng 0 nếu như lời gọi không xảy ra lỗi. Ngược lại phương thức này sẽ trả về một mã lỗi và để biết được đó là lỗi gì ta cần sử dụng thêm phương thức sau cũng trong thư viện winmm.dll. Thông điệp mô tả lỗi sẽ được lưu trong đối tượng StringBuilder errMsg trong tham số thứ 2 của phương thức:

[DllImport("winmm.dll")]
public static extern int mciGetErrorString(int errCode, StringBuilder errMsg, int buflen);

Phần kết

Với các chức năng cơ bản của lớp MciPlayer vừa tạo, bạn có thể tự thiết kế cho mình một ứng dụng MultiMedia đa năng như quản lý nhạc, tự động tắt máy, màn hình, tạo playlist sử dụng các collection,…

https://yinyangit.wordpress.com

9/6/2009

Advertisements

45 thoughts on “C# – Viết chương trình Media Player với MCI (update 26/4/2011)

    • Xin lỗi vì dạo này ít lên mạng nên ko trả lời sớm được.
      Để lấy hoặc thay đổi giá trị volumn bạn có thể sử dụng 2 hàm waveOutGetVolume() và waveOutSetVolume() nằm trong cùng thư viện winmm.dll được sử dụng trong ví dụ trên.
      Bạn khai báo 2 hàm trên như sau:

      [DllImport(“winmm.dll”)]
      public static extern int waveOutGetVolume(IntPtr hwo, out uint dwVolume);

      [DllImport(“winmm.dll”)]
      public static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume);

      Tham số hwo bạn có thể đặt là IntPtr.Zero, còn dwVolume của 2 hàm trên chính là giá trị của volumn khi bạn lấy hoặc gán.

      Trả lời
  1. Để tạo playlist thì bạn chỉ cần tạo một class PlayList chứa tên bài hát, đường dẫn file,…. Sau đó dùng một collection để chứa các PlayList này.
    Để chương trình chơi bài tiếp theo, bạn hãy kiểm tra trạng thái của Player (xem phần 4 trong bài).
    Chúc bạn thành công!

    Trả lời
  2. Sau khi tìm hiểu thì mình nhận thấy hàm lấy chiều dài của mciSendString() có thể không hoạt động chính xác trên các phiên bản Windows mới (có thể là từ win vista). Ngoài ra nó còn phụ thuộc vào một số thuộc tính định dạng của media.
    Bạn có thể sử dụng thư viện WindowsAPICodePack để thay thế. Tham khảo bài sau:
    How to get the length (duration) of a media File in C# on Windows 7

    Trả lời
  3. Cảm ơn bạn rất nhiều về bài viết ! bạn cho mình hỏi là nếu mình muốn xây dựng thêm phần gắn phụ đề cho bài hát giống chương trình karaoke ấy (lời hát tới đâu đổi màu chữ lyrics tới đó ) thì làm thế nào ? mong bạn chia sẻ 🙂
    thanks !

    Trả lời
    • Mình không rành về lĩnh vực này nên cũng không rõ. Tuy nhiên nếu muốn thực hiện thì bạn cần phải có kiến thức sâu và sử dụng những nền tảng như DirectX, hay có thể là flash, silverlight, … hoặc một công nghệ trình chiếu tương tự.

      Trả lời
  4. Cám ơn bạn rất nhiều về bài viết này. Bạn có thể cho mình hỏi thêm một chút là giờ mình đã đọc được file âm thanh, vậy làm thế nào để lấy được tần số âm thanh, và có thể vẽ được dưới dạng xung của âm thanh trong quá trình chơi nhạc. Mình nghĩ là có liên quan đến vấn đề mình đang quan tâm trong bài tập của mình là cho 5 bài hát khác nhau, cắt ngẫu nhiên 1 bài hát, và kiểm tra xem nó là bài hát nào. Vì bình thường để so sánh được đoạn đó giống nhất với âm hưởng của bài hát nào thì mình cần phải lấy được tín hiệu số của nó rồi so sánh. Mình đang bế tắc về vấn đề này. Hy vọng rằng bạn sẽ gợi ý giúp mình. Cám ơn bạn nhiều !

    Trả lời
  5. A cho e hỏi là e làm theo bài của a. dùng SetParent() cửa sổ video vào trong 1 panel.Nhưng khi phóng to hay thu nhỏ form thì xảy ra hiện tượng bị đơ. Làm sao khắc phục tình trạng này, như trong WMP hay KMP thì k bị

    Trả lời
    • Công nghệ mà WMP hay các trình media chuyên nghiệp sử dụng dĩ nhiên ko phải đơn giản như ví dụ. Họ sử dụng các thư viện đồ họa cấp thấp để hiển thị hình ảnh, âm thanh (ví dụ như DirectX). Khi bạn SetParent() thì do ảnh hưởng của cửa sổ cha nên tốc độ của cửa sổ media sẽ không thể ổn định được.
      Nếu cần làm trình media thì bạn có thể dùng Windows Media Player control của Microsoft, đầy đủ chức năng và dễ thực hiện.

      Trả lời

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s