Last active
February 24, 2020 13:48
-
-
Save Aniketh01/68d79af6749cc56a1866eba8b6a9ad65 to your computer and use it in GitHub Desktop.
cc -o main main.cpp -lglfw -lavcodec -lavformat -lavutil -lswresample -lswscale
This file contains hidden or 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 <stdio.h> | |
#include <GLFW/glfw3.h> | |
extern "C" { | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
#include <libswscale/swscale.h> | |
#include <inttypes.h> | |
} | |
struct VideoReaderState { | |
// Public things for other parts of the program to read from | |
int width, height; | |
AVRational time_base; | |
// Private internal state | |
AVFormatContext* av_format_ctx; | |
AVCodecContext* av_codec_ctx; | |
int video_stream_index; | |
AVFrame* av_frame; | |
AVPacket* av_packet; | |
SwsContext* sws_scaler_ctx; | |
}; | |
bool video_reader_open(VideoReaderState* state, const char* filename); | |
bool video_reader_read_frame(VideoReaderState* state, uint8_t* frame_buffer, int64_t* pts); | |
void video_reader_close(VideoReaderState* state); | |
int main(int argc, const char** argv) { | |
GLFWwindow* window; | |
if (!glfwInit()) { | |
printf("Couldn't init GLFW\n"); | |
return 1; | |
} | |
window = glfwCreateWindow(1280, 720, "QUIC streamer", NULL, NULL); | |
if (!window) { | |
printf("Couldn't open window\n"); | |
return 1; | |
} | |
VideoReaderState vr_state; | |
if (!video_reader_open(&vr_state, "/home/devbox/devel/quic_research/datasets/bbb_sunflower_1080p_60fps_normal.mp4")) { | |
printf("Couldn't open video file\n"); | |
return 1; | |
} | |
glfwMakeContextCurrent(window); | |
// Generate texture | |
GLuint tex_handle; | |
glGenTextures(1, &tex_handle); | |
glBindTexture(GL_TEXTURE_2D, tex_handle); | |
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); | |
// Allocate frame buffer | |
const int frame_width = vr_state.width; | |
fprintf(stderr, "Frame width: %d\n", frame_width); | |
const int frame_height = vr_state.height; | |
fprintf(stderr, "Frame height: %d\n", frame_height); | |
uint8_t* frame_data = new uint8_t[frame_width * frame_height * 4]; | |
fprintf(stderr, "Frame data: %d\n", frame_data); | |
while (!glfwWindowShouldClose(window)) { | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
// Set up orphographic projection | |
int window_width, window_height; | |
glfwGetFramebufferSize(window, &window_width, &window_height); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
glOrtho(0, window_width, window_height, 0, -1, 1); | |
glMatrixMode(GL_MODELVIEW); | |
// Read a new frame and load it into texture | |
int64_t pts; | |
if (!video_reader_read_frame(&vr_state, frame_data, &pts)) { | |
printf("Couldn't load video frame\n"); | |
return 1; | |
} | |
static bool first_frame = true; | |
if (first_frame) { | |
glfwSetTime(0.0); | |
first_frame = false; | |
} | |
double pt_in_seconds = pts * (double)vr_state.time_base.num / (double)vr_state.time_base.den; | |
while (pt_in_seconds > glfwGetTime()) { | |
glfwWaitEventsTimeout(pt_in_seconds - glfwGetTime()); | |
} | |
glBindTexture(GL_TEXTURE_2D, tex_handle); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, frame_width, frame_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame_data); | |
// Render whatever you want | |
glEnable(GL_TEXTURE_2D); | |
glBindTexture(GL_TEXTURE_2D, tex_handle); | |
glBegin(GL_QUADS); | |
glTexCoord2d(0,0); glVertex2i(200, 200); | |
glTexCoord2d(1,0); glVertex2i(200 + frame_width, 200); | |
glTexCoord2d(1,1); glVertex2i(200 + frame_width, 200 + frame_height); | |
glTexCoord2d(0,1); glVertex2i(200, 200 + frame_height); | |
glEnd(); | |
glDisable(GL_TEXTURE_2D); | |
glfwSwapBuffers(window); | |
glfwPollEvents(); | |
} | |
video_reader_close(&vr_state); | |
return 0; | |
} | |
bool video_reader_open(VideoReaderState* state, const char* filename) { | |
// Unpack members of state | |
auto& width = state->width; | |
auto& height = state->height; | |
auto& time_base = state->time_base; | |
auto& av_format_ctx = state->av_format_ctx; | |
auto& av_codec_ctx = state->av_codec_ctx; | |
auto& video_stream_index = state->video_stream_index; | |
auto& av_frame = state->av_frame; | |
auto& av_packet = state->av_packet; | |
// Open the file using libavformat | |
av_format_ctx = avformat_alloc_context(); | |
if (!av_format_ctx) { | |
printf("Couldn't created AVFormatContext\n"); | |
return false; | |
} | |
if (avformat_open_input(&av_format_ctx, filename, NULL, NULL) != 0) { | |
printf("Couldn't open video file\n"); | |
return false; | |
} | |
// Find the first valid video stream inside the file | |
video_stream_index = -1; | |
AVCodecParameters* av_codec_params; | |
AVCodec* av_codec; | |
for (int i = 0; i < av_format_ctx->nb_streams; ++i) { | |
av_codec_params = av_format_ctx->streams[i]->codecpar; | |
av_codec = avcodec_find_decoder(av_codec_params->codec_id); | |
if (!av_codec) { | |
continue; | |
} | |
if (av_codec_params->codec_type == AVMEDIA_TYPE_VIDEO) { | |
video_stream_index = i; | |
width = av_codec_params->width; | |
height = av_codec_params->height; | |
time_base = av_format_ctx->streams[i]->time_base; | |
break; | |
} | |
} | |
if (video_stream_index == -1) { | |
printf("Couldn't find valid video stream inside file\n"); | |
return false; | |
} | |
// Set up a codec context for the decoder | |
av_codec_ctx = avcodec_alloc_context3(av_codec); | |
if (!av_codec_ctx) { | |
printf("Couldn't create AVCodecContext\n"); | |
return false; | |
} | |
if (avcodec_parameters_to_context(av_codec_ctx, av_codec_params) < 0) { | |
printf("Couldn't initialize AVCodecContext\n"); | |
return false; | |
} | |
if (avcodec_open2(av_codec_ctx, av_codec, NULL) < 0) { | |
printf("Couldn't open codec\n"); | |
return false; | |
} | |
av_frame = av_frame_alloc(); | |
if (!av_frame) { | |
printf("Couldn't allocate AVFrame\n"); | |
return false; | |
} | |
av_packet = av_packet_alloc(); | |
if (!av_packet) { | |
printf("Couldn't allocate AVPacket\n"); | |
return false; | |
} | |
return true; | |
} | |
bool video_reader_read_frame(VideoReaderState* state, uint8_t* frame_buffer, int64_t* pts) { | |
// Unpack members of state | |
auto& width = state->width; | |
auto& height = state->height; | |
auto& av_format_ctx = state->av_format_ctx; | |
auto& av_codec_ctx = state->av_codec_ctx; | |
auto& video_stream_index = state->video_stream_index; | |
auto& av_frame = state->av_frame; | |
auto& av_packet = state->av_packet; | |
auto& sws_scaler_ctx = state->sws_scaler_ctx; | |
// Decode one frame | |
int response; | |
while (av_read_frame(av_format_ctx, av_packet) >= 0) { | |
if (av_packet->stream_index != video_stream_index) { | |
av_packet_unref(av_packet); | |
continue; | |
} | |
response = avcodec_send_packet(av_codec_ctx, av_packet); | |
if (response < 0) { | |
printf("Failed to decode packet: \n"); | |
return false; | |
} | |
response = avcodec_receive_frame(av_codec_ctx, av_frame); | |
if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { | |
av_packet_unref(av_packet); | |
continue; | |
} else if (response < 0) { | |
printf("Failed to decode packet: \n"); | |
return false; | |
} | |
av_packet_unref(av_packet); | |
break; | |
} | |
*pts = av_frame->pts; | |
// Set up sws scaler | |
//if (!sws_scaler_ctx) { | |
sws_scaler_ctx = sws_getContext(width, height, av_codec_ctx->pix_fmt, | |
width, height, AV_PIX_FMT_RGB0, | |
SWS_BILINEAR, NULL, NULL, NULL); | |
// } | |
if (!sws_scaler_ctx) { | |
printf("Couldn't initialize sw scaler\n"); | |
return false; | |
} | |
uint8_t* dest[4] = { frame_buffer, NULL, NULL, NULL }; | |
int dest_linesize[4] = { width * 4, 0, 0, 0 }; | |
sws_scale(sws_scaler_ctx, av_frame->data, av_frame->linesize, 0, av_frame->height, dest, dest_linesize); | |
return true; | |
} | |
void video_reader_close(VideoReaderState* state) { | |
sws_freeContext(state->sws_scaler_ctx); | |
avformat_close_input(&state->av_format_ctx); | |
avformat_free_context(state->av_format_ctx); | |
av_frame_free(&state->av_frame); | |
av_packet_free(&state->av_packet); | |
avcodec_free_context(&state->av_codec_ctx); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment