Link gốc: http://www.arm.vn/index.php?option=com_content&task=view&id=46&Itemid=1
Đối với những người hay chat, có lẽ không ít thì nhiều cũng nghe qua phần mềm Fakewebcam , đây là phần mềm khá độc đáo, cho phép đưa dữ liệu từ một file video hay file hình bất kỳ vào chương trình để chia sẻ với bạn bè. Cách đây khá lâu, một người bạn có nhờ tôi làm hộ một chương trình tương tự như Fakewebcam, dữ liệu lấy từ webcam thật, sau quá trình xử lý ảnh (bằng một chương trình_application) sẽ được đưa cho Yahoo. Vấn đề ở đây là làm thế nào có thể đưa dữ liệu hình ảnh sau xử lý cho Yahoo. Sau khi tìm kiếm trên mạng, tham khảo thêm thông tin từ DirectX, và nhất là tìm kiếm trong bộ source code mẫu của WDF, tôi nghĩ có một giải pháp là làm giả thiết bị webcam (Virtual webcam) ở mức độ hệ thống.
Trong hình trên, dấu mũi tên là đường đi dữ liệu: dữ liệu từ webcam thật đến chương trình xử lý ảnh, hình ảnh xử lý xong sẽ được đưa cho webcam ảo, Yahoo (hay các chương trình ứng dụng khác) truy cập vào webcam ảo để lấy dữ liệu. Ở đây chúng ta có vấn đề nảy sinh tiếp theo là dữ liệu từ driver webcam thật có thể đọc được từ chương trình ứng dụng (sử dụng DirectShow, hay VFW), nhưng làm thế nào để đưa dữ liệu từ ứng dụng xuống cho driver.
Chia sẻ dữ liệu giữa các tiến trình
Giải pháp là sử dụng: kỹ thuật chia sẻ vùng nhớ giữa các process với nhau. Ở đây là chia sẻ giữa driver và ứng dụng.
1. Phía ứng dụng: khởi tạo vùng nhớ và đưa dữ liệu vào.
+CreateFileMapping: hàm này sẽ khởi tạo vùng nhớ.
+MapViewOfFile: hàm này sẽ đưa cho ta địa chỉ trỏ tới vùng nhớ đã được khởi tạo bởi CreateFileMapping ở trên.
Đoạn mã thực hiện việc tạo vùng nhớ và địa chỉ trỏ tới vùng nhớ:
#define szName L"MyFakeWebcamMappingObject"
void COpenFakeWebcamDlg::CreateMappFile()
{
MapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // max. object size
BUF_SIZE, // buffer size
szName);
if(MapFile == NULL)
{
MessageBox(L"Create MapFile Failed !",L"Error");
this->CloseWindow();
}
MapBuffer = (unsigned char*)MapViewOfFile(
MapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if(MapBuffer == NULL)
{
MessageBox(L"Create MapBuffer Failed !",L"Error");
this->CloseWindow();
}
}
void COpenFakeWebcamDlg::CreateMappFile()
{
MapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // use paging file
NULL, // default security
PAGE_READWRITE, // read/write access
0, // max. object size
BUF_SIZE, // buffer size
szName);
if(MapFile == NULL)
{
MessageBox(L"Create MapFile Failed !",L"Error");
this->CloseWindow();
}
MapBuffer = (unsigned char*)MapViewOfFile(
MapFile, // handle to map object
FILE_MAP_ALL_ACCESS, // read/write permission
0,
0,
BUF_SIZE);
if(MapBuffer == NULL)
{
MessageBox(L"Create MapBuffer Failed !",L"Error");
this->CloseWindow();
}
}
2. Phía driver: mở vùng nhớ (nếu đã được tạo bởi ứng dụng), đọc dữ liệu và đưa ra giao diện xuất dữ liệu.
+ZwOpenSection: hàm này mở 1 section đã có sẵn thông qua tên gọi của nó. Section này là vùng dữ liệu chúng ta cần chia sẻ. Lưu ý là dùng xong thì chúng ta phải đóng section này lại bằng hàm ZwClose để giải phóng tài nguyên.
+ZwMapViewOfSection: sau khi mở section xong, hàm này sẽ ánh xạ vùng dữ liệu của section vào vùng nhớ ảo để có thể truy xuất. Sau khi truy xuất xong thì dùng hàm ZwUnmapViewOfSection để giải phóng tài nguyên.
RtlInitUnicodeString(
&fileNameUnicodeString,
L"\\BaseNamedObjects\\MyFakeWebcamMappingObject");
InitializeObjectAttributes(
&objectAttributes,
&fileNameUnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
(HANDLE)NULL,
(PSECURITY_DESCRIPTOR)NULL );
rc = ZwOpenSection(
&hFileHandle,
SECTION_MAP_READ,
&objectAttributes);
if (NT_SUCCESS(rc))
{
rc = ZwMapViewOfSection(
hFileHandle, //section handle
ZwCurrentProcess(), // current process
&uBaseAddress, //virtual based address
0L, //Zerobits
size,
0, // optional
(PSIZE_T)&ViewSize, // How much to map
ViewShare, // Inherit disposition
0, //ALlocation Type
PAGE_READWRITE //protection
);
if (NT_SUCCESS(rc))
{
m_Device-> m_HardwareSimulation -> bFileOk = TRUE;
RtlCopyMemory(
m_Device-> m_HardwareSimulation -> FileBuffer,
uBaseAddress,ViewSize );
//release memory
ZwUnmapViewOfSection(
ZwCurrentProcess(), // current process
uBaseAddress);
}
} Nền tảng phát triển
&fileNameUnicodeString,
L"\\BaseNamedObjects\\MyFakeWebcamMappingObject");
InitializeObjectAttributes(
&objectAttributes,
&fileNameUnicodeString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
(HANDLE)NULL,
(PSECURITY_DESCRIPTOR)NULL );
rc = ZwOpenSection(
&hFileHandle,
SECTION_MAP_READ,
&objectAttributes);
if (NT_SUCCESS(rc))
{
rc = ZwMapViewOfSection(
hFileHandle, //section handle
ZwCurrentProcess(), // current process
&uBaseAddress, //virtual based address
0L, //Zerobits
size,
0, // optional
(PSIZE_T)&ViewSize, // How much to map
ViewShare, // Inherit disposition
0, //ALlocation Type
PAGE_READWRITE //protection
);
if (NT_SUCCESS(rc))
{
m_Device-> m_HardwareSimulation -> bFileOk = TRUE;
RtlCopyMemory(
m_Device-> m_HardwareSimulation -> FileBuffer,
uBaseAddress,ViewSize );
//release memory
ZwUnmapViewOfSection(
ZwCurrentProcess(), // current process
uBaseAddress);
}
} Nền tảng phát triển
Driver: sử dụng driver của chương trình mẫu có sẵn trong WDF: AVStream Simulated Hardware Sample Driver (\src\AVStream\avshws), phát triển thêm các phần chia sẻ dữ liệu với chương trình ứng dụng.
Ứng dụng: sử dụng DirectX để đọc dữ liệu từ các file video.
Thao tác dữ liệu
Sau khi đã tạo vùng chia sẽ dữ liệu chung, công việc còn lại bao gồm:
+Ứng dụng đọc dữ liệu và ghi vào vùng nhớ chia sẻ đã được khởi tạo
for(long y=0 ; y < height && y< HEIGHT ; y++)
{
bufAddress = (long)(y*width*4);
for(long x=0; x< width && x< WIDTH ; x++)
{
MapBuffer[index] = bufData[bufAddress];
MapBuffer[index+1] = bufData[bufAddress+1];
MapBuffer[index+2] = bufData[bufAddress+2];
index+=3;
bufAddress += 4;
}
} +Phía driver chép dữ liệu vào vùng đệm
{
bufAddress = (long)(y*width*4);
for(long x=0; x< width && x< WIDTH ; x++)
{
MapBuffer[index] = bufData[bufAddress];
MapBuffer[index+1] = bufData[bufAddress+1];
MapBuffer[index+2] = bufData[bufAddress+2];
index+=3;
bufAddress += 4;
}
} +Phía driver chép dữ liệu vào vùng đệm
int w = m_ImageSynth -> m_Width;
int h = m_ImageSynth -> m_Height;
int size = w*h*3;
RtlCopyMemory (
m_ImageSynth -> m_SynthesisBuffer,
FileBuffer,size);
// Fill scatter gather buffers
if (!NT_SUCCESS (FillScatterGatherBuffers ())) {
InterlockedIncrement (PLONG (&m_NumFramesSkipped));
} Biên dịch, cài đặt và sử dụng chương trình
int h = m_ImageSynth -> m_Height;
int size = w*h*3;
RtlCopyMemory (
m_ImageSynth -> m_SynthesisBuffer,
FileBuffer,size);
// Fill scatter gather buffers
if (!NT_SUCCESS (FillScatterGatherBuffers ())) {
InterlockedIncrement (PLONG (&m_NumFramesSkipped));
} Biên dịch, cài đặt và sử dụng chương trình
Về cách biên dịch và cài đặt driver, có thể tham khảo tại ở bài viết "Lập trình driver trên Windows - Cài đặt bộ công cụ lập trình".
Sau khi cài đặt Webcam ảo thành công, vào Device Manager->Sound,Video and game controllers để kiểm tra xem thiết bị Webcam ảo có xuất hiện hay không.
Hướng dẫn sử dụng:
Hạn chế của chương trình
Có thể nhận thấy là chương trình này chiếm khá nhiều tài nguyên và bộ nhớ hệ thống, hiệu suất chưa cao, ngoài ra chỉ mới hỗ trợ cho Windows XP.
Kết luận
Vậy là chúng ta đã có thể tự tạo cho mình một webcam ảo. Quan trọng hơn là chúng ta đã tiếp cận tới hệ thống driver của Microsoft, làm một ứng dụng cụ thể, có khả năng áp dụng thực tế mà lại không quá khó. Hi vọng ví dụ nhỏ này sẽ giúp các bạn có thêm hứng thú với lập trình hệ thống. Chúc các bạn thành công.
Hồ Nghĩa Đức - Nguyễn Chí Kiên
Hồ Nghĩa Đức - Nguyễn Chí Kiên