Created
February 1, 2023 14:04
-
-
Save habibg1232191/6a235196f742b55b700933f59b7779d4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> /* This enables access to Microsoft Windows specific data types that are required to use the Graphical User Interface of Windows. (HWND, WNDCLASSEX, etc.) */ | |
#include <tchar.h> /* _T */ | |
#include <iostream> | |
#include <string> | |
#include <chrono> | |
#include <thread> | |
#include "skia/core/SkBitmap.h" | |
#include "skia/core/SkCanvas.h" | |
#include "skia/core/SkSurface.h" | |
#include "skia/core/SkPaint.h" | |
#include "skia/core/SkFont.h" | |
#include "skia/core/SkTextBlob.h" | |
#include "skia/core/SkImageEncoder.h" | |
#include "skia/core/SkStream.h" | |
extern "C" { | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
#include <libavutil/samplefmt.h> | |
#include <libavutil/timestamp.h> | |
#include <libswscale/swscale.h> | |
} | |
int ret = 0; | |
AVFormatContext *pFormatCtx = NULL; | |
int videoStreamIndex = -1; | |
AVCodecContext *pCodecCtx = NULL; | |
AVCodec *pCodec = NULL; | |
AVFrame *pFrame = NULL; | |
AVPacket packet; | |
int frameFinished; | |
LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |
HWND hwnd; | |
void Draw(SkCanvas* canvas, int w, int h); | |
int init_ffmpeg() | |
{ | |
av_register_all(); | |
// Open video file | |
if (avformat_open_input(&pFormatCtx, "C:\\Users\\habib\\Videos\\Captures\\July 2022-08-13 13-06-51.mp4", NULL, NULL) != 0) | |
return -1; // Couldn't open file | |
// Retrieve stream information | |
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) | |
return -1; // Couldn't find stream information | |
// Find the first video stream | |
for (int i = 0; i < pFormatCtx->nb_streams; i++) { | |
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { | |
videoStreamIndex = i; | |
break; | |
} | |
} | |
if (videoStreamIndex == -1) | |
return -1; // Didn't find a video stream | |
// Get a pointer to the codec context for the video stream | |
pCodecCtx = pFormatCtx->streams[videoStreamIndex]->codec; | |
// Find the decoder for the video stream | |
pCodec = avcodec_find_decoder(pCodecCtx->codec_id); | |
if (pCodec == NULL) { | |
fprintf(stderr, "Unsupported codec!\n"); | |
return -1; // Codec not found | |
} | |
// Open codec | |
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) | |
return -1; // Could not open codec | |
// Allocate video frame | |
pFrame = av_frame_alloc(); | |
return 0; | |
} | |
int main(int argc, char* argv[]) { | |
if(init_ffmpeg() > 0) | |
return -1; | |
WNDCLASS wc; | |
const auto h_instance = GetModuleHandle(nullptr); | |
wc.style = 0; | |
wc.lpfnWndProc = reinterpret_cast<WNDPROC>(WndProc); | |
wc.cbClsExtra = 0; | |
wc.cbWndExtra = 0; | |
wc.hInstance = h_instance; | |
wc.hIcon = LoadIcon(nullptr, | |
IDI_APPLICATION); | |
wc.hCursor = LoadCursor(nullptr, | |
IDC_ARROW); | |
wc.hbrBackground = CreateSolidBrush(0xff000000); | |
wc.lpszMenuName = reinterpret_cast<LPCSTR>(L"MainMenu"); | |
wc.lpszClassName = reinterpret_cast<LPCSTR>(L"MainWndClass"); | |
wc.style = CS_VREDRAW | CS_HREDRAW; | |
if (!RegisterClass(&wc)) | |
return FALSE; | |
hwnd = CreateWindowA(reinterpret_cast<LPCSTR>(L"MainWndClass"), "July", | |
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, | |
CW_USEDEFAULT, CW_USEDEFAULT, nullptr, | |
nullptr, h_instance, nullptr); | |
ShowWindow(hwnd, SW_SHOW); | |
UpdateWindow(hwnd); | |
BOOL bRet; | |
MSG msg; | |
while ((bRet = GetMessage(&msg, hwnd, 0, 0)) != 0) { | |
if (bRet == -1) { | |
// handle the error and possibly exit | |
} | |
else { | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
} | |
return 0; | |
} | |
void AVFrameToSkBitmap(AVFrame *frame, int width, int height, SkBitmap *pBitmap) | |
{ | |
pBitmap->allocN32Pixels(width, height); | |
struct SwsContext *pSwsCtx = sws_getContext(width, height, AV_PIX_FMT_YUV420P, | |
width, height, AV_PIX_FMT_RGBA, | |
SWS_BILINEAR, nullptr, nullptr, nullptr); | |
uint8_t *dstData[1] = { (uint8_t*)pBitmap->getAddr(0, 0) }; | |
int dstLinesize[1] = { 4 * width }; | |
if(dstData[0] == nullptr || dstLinesize[0] == 0) return; | |
sws_scale(pSwsCtx, frame->data, frame->linesize, 0, height, dstData, dstLinesize); | |
// Free the scaling context | |
sws_freeContext(pSwsCtx); | |
} | |
int image_cout = 0; | |
void Draw(SkCanvas* canvas, int w, int h) { | |
SkPaint paint1, paint2, paint3; | |
paint1.setAntiAlias(true); | |
paint1.setColor(SK_ColorBLACK); | |
int fps = 60; | |
int frame_duration_ms = 1000 / fps; | |
auto start_time = std::chrono::high_resolution_clock::now(); | |
if(av_read_frame(pFormatCtx, &packet) == 0) | |
{ | |
if (packet.stream_index == videoStreamIndex) { | |
int result = avcodec_send_packet(pCodecCtx, &packet); | |
if (result < 0) { | |
std::cout << "avcodec_send_packet failed" << std::endl; | |
} | |
AVFrame* pFrame = av_frame_alloc(); | |
if(avcodec_receive_frame(pCodecCtx, pFrame) > 0) return; | |
SkBitmap bitmap; | |
AVFrameToSkBitmap(pFrame, pFrame->width, pFrame->height, &bitmap); | |
auto image = SkImage::MakeFromBitmap(bitmap); | |
canvas->drawImage(image, 0, 0); | |
av_packet_alloc(); | |
auto end_time = std::chrono::high_resolution_clock::now(); | |
auto elapsed_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count(); | |
if (elapsed_time_ms < frame_duration_ms) { | |
std::this_thread::sleep_for(std::chrono::milliseconds(frame_duration_ms - elapsed_time_ms)); | |
} | |
start_time = std::chrono::high_resolution_clock::now(); | |
} | |
} | |
av_frame_free(&pFrame); | |
RECT size; | |
GetClientRect(hwnd, &size); | |
canvas->flush(); | |
} | |
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { | |
switch (message) | |
{ | |
case WM_PAINT: | |
{ | |
PAINTSTRUCT ps; | |
HDC hdc = BeginPaint(hWnd, &ps); | |
RECT rt; | |
GetClientRect(hWnd, &rt); | |
int bmpw = rt.right - rt.left; | |
int bmph = rt.bottom - rt.top; | |
const size_t bmpSize = sizeof(BITMAPINFOHEADER) + bmpw * bmph * sizeof(uint32_t); | |
BITMAPINFO* bmpInfo = (BITMAPINFO*)new BYTE[bmpSize](); | |
bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
bmpInfo->bmiHeader.biWidth = bmpw; | |
bmpInfo->bmiHeader.biHeight = -bmph; | |
bmpInfo->bmiHeader.biPlanes = 1; | |
bmpInfo->bmiHeader.biBitCount = 32;//Image 32 bits per pixel | |
bmpInfo->bmiHeader.biCompression = BI_RGB; | |
void* pixels = bmpInfo->bmiColors;//Picture pixel position pointer | |
SkImageInfo info = SkImageInfo::Make(bmpw, bmph, | |
kBGRA_8888_SkColorType, kPremul_SkAlphaType); | |
sk_sp<SkSurface> surface = SkSurface::MakeRasterDirect(info, pixels, bmpw * sizeof(uint32_t)); | |
SkCanvas* canvas = surface->getCanvas(); | |
canvas->clear(SK_ColorWHITE);//fill the white background | |
Draw(canvas, bmpw, bmph);//Call the function here and pass in the pointer drawing. | |
StretchDIBits(hdc, 0, 0, bmpw, bmph, | |
0, 0, bmpw, bmph, | |
pixels, bmpInfo, | |
DIB_RGB_COLORS, SRCCOPY); | |
delete[] bmpInfo; | |
EndPaint(hWnd, &ps); | |
} | |
break; | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
av_free(pFrame); | |
avcodec_close(pCodecCtx); | |
avformat_close_input(&pFormatCtx); | |
exit(0); | |
default: | |
goto label_msg_end; | |
} | |
return 0; | |
label_msg_end: | |
return DefWindowProc(hWnd, message, wParam, lParam); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment