#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "swscale.lib")
#pragma comment(lib, "gdi32.lib") // GDI 库#define VIDEO_WIDTH 640
#define VIDEO_HEIGHT 480// 渲染帧到窗口的函数(使用 GDI 绘制)
void RenderFrameToWindow(HWND hwnd, AVFrame *frame) {// 获取设备上下文HDC hdc = GetDC(hwnd);if (!hdc) {fprintf(stderr, "Failed to get device context\n");return;}// 创建一个兼容的内存 DCHDC memDC = CreateCompatibleDC(hdc);if (!memDC) {fprintf(stderr, "Failed to create memory DC\n");ReleaseDC(hwnd, hdc);return;}// 创建位图对象HBITMAP hBitmap = CreateCompatibleBitmap(hdc, VIDEO_WIDTH, VIDEO_HEIGHT);if (!hBitmap) {fprintf(stderr, "Failed to create bitmap\n");DeleteDC(memDC);ReleaseDC(hwnd, hdc);return;}// 将内存 DC 与位图关联HGDIOBJ oldBitmap = SelectObject(memDC, hBitmap);// 获取 RGB 数据uint8_t *rgb_data = (uint8_t *)malloc(VIDEO_WIDTH * VIDEO_HEIGHT * 3);uint8_t *dst_data[4] = {rgb_data, NULL, NULL, NULL};int dst_linesize[4] = {VIDEO_WIDTH * 3, 0, 0, 0};// 使用 sws_getContext 来确保转换正确struct SwsContext *sws_ctx = sws_getContext(frame->width, frame->height, frame->format,VIDEO_WIDTH, VIDEO_HEIGHT, AV_PIX_FMT_RGB24,SWS_BICUBIC, NULL, NULL, NULL);sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, dst_data, dst_linesize);// 反转图像上下for (int y = 0; y < VIDEO_HEIGHT / 2; y++) {for (int x = 0; x < VIDEO_WIDTH * 3; x++) {uint8_t temp = rgb_data[y * VIDEO_WIDTH * 3 + x];rgb_data[y * VIDEO_WIDTH * 3 + x] = rgb_data[(VIDEO_HEIGHT - 1 - y) * VIDEO_WIDTH * 3 + x];rgb_data[(VIDEO_HEIGHT - 1 - y) * VIDEO_WIDTH * 3 + x] = temp;}}// 创建 BITMAPINFOBITMAPINFOHEADER biHeader;biHeader.biSize = sizeof(BITMAPINFOHEADER);biHeader.biWidth = VIDEO_WIDTH;biHeader.biHeight = VIDEO_HEIGHT;biHeader.biPlanes = 1;biHeader.biBitCount = 24; // RGB24biHeader.biCompression = BI_RGB;biHeader.biSizeImage = VIDEO_WIDTH * VIDEO_HEIGHT * 3;biHeader.biXPelsPerMeter = 0;biHeader.biYPelsPerMeter = 0;biHeader.biClrUsed = 0;biHeader.biClrImportant = 0;BITMAPINFO bmi = {0};bmi.bmiHeader = biHeader;// 使用 GDI 绘制位图SetDIBitsToDevice(hdc, 0, 0, VIDEO_WIDTH, VIDEO_HEIGHT, 0, 0, 0, VIDEO_HEIGHT,rgb_data, &bmi, DIB_RGB_COLORS);// 清理free(rgb_data);SelectObject(memDC, oldBitmap);DeleteObject(hBitmap);DeleteDC(memDC);ReleaseDC(hwnd, hdc);
}// 自定义窗口过程函数,用来处理窗口的消息
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {switch (uMsg) {case WM_DESTROY:PostQuitMessage(0);return 0;default:return DefWindowProc(hwnd, uMsg, wParam, lParam);}
}int main(int argc, char *argv[]) {if (argc < 2) {fprintf(stderr, "Usage: %s <video_file>\n", argv[0]);return -1;}// 初始化 FFmpegavformat_network_init();AVFormatContext *fmt_ctx = NULL;if (avformat_open_input(&fmt_ctx, argv[1], NULL, NULL) < 0) {fprintf(stderr, "Could not open input file\n");return -1;}if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");return -1;}// 查找视频流int video_stream_index = -1;for (int i = 0; i < fmt_ctx->nb_streams; i++) {if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_index = i;break;}}if (video_stream_index == -1) {fprintf(stderr, "Could not find video stream\n");return -1;}AVStream *video_stream = fmt_ctx->streams[video_stream_index];AVCodec *codec = avcodec_find_decoder(video_stream->codecpar->codec_id);if (!codec) {fprintf(stderr, "Codec not found\n");return -1;}AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);if (avcodec_parameters_to_context(codec_ctx, video_stream->codecpar) < 0) {fprintf(stderr, "Could not copy codec parameters to context\n");return -1;}if (avcodec_open2(codec_ctx, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");return -1;}// 初始化窗口WNDCLASSA cls={0};cls.lpszClassName="main";cls.lpfnWndProc=WindowProc;RegisterClassA(&cls);HWND hwnd = CreateWindowA("main", "FFmpeg Video Player", WS_OVERLAPPEDWINDOW,100, 100, VIDEO_WIDTH, VIDEO_HEIGHT, NULL, NULL, NULL, NULL);ShowWindow(hwnd, SW_SHOW);UpdateWindow(hwnd);// 解码和渲染部分AVPacket packet;AVFrame *frame = av_frame_alloc();MSG msg;while (av_read_frame(fmt_ctx, &packet) >= 0) {if (packet.stream_index == video_stream_index) {if (avcodec_send_packet(codec_ctx, &packet) < 0) {fprintf(stderr, "Error sending packet to codec\n");break;}while (avcodec_receive_frame(codec_ctx, frame) >= 0) {// 渲染帧到窗口RenderFrameToWindow(hwnd, frame);// 处理消息if (PeekMessageA(&msg, hwnd, 0, 0, PM_REMOVE)) {TranslateMessage(&msg);DispatchMessageA(&msg);}Sleep(30); // 为了控制视频播放的帧率}}av_packet_unref(&packet);}// 清理资源av_frame_free(&frame);avcodec_free_context(&codec_ctx);avformat_close_input(&fmt_ctx);return 0;
}
mingw编译命令为
gcc -o a.exe a.c -IYouIncludePath -LYouLibPath -l:avcodec.lib -l:avformat.lib -l:swscale.lib -llibavutil -lgdi32 -luser32