Skip to content

Instantly share code, notes, and snippets.

@pumbur
Created October 25, 2018 17:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pumbur/4c109088e0b8b076057124980318f558 to your computer and use it in GitHub Desktop.
Save pumbur/4c109088e0b8b076057124980318f558 to your computer and use it in GitHub Desktop.
// glfw + libao + libav*: video player outline.
// too simple for a real video player, too complex for a libav demo. (ffmpeg has it's own ways to output, see libavdevice/*)
// gcc av.c -o av -lav{util,format,codec,util} -lsw{scale,resample} -l{ao,epoxy,glfw,pthread}
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <epoxy/gl.h> //#include <GLES2/gl2.h>
#include <GLFW/glfw3.h>
#include <ao/ao.h>
int imgw = 640, imgh = 480, imgx = 0, imgy = 0;
int winw = 640, winh = 480, outw = 256, outh = 256;
int maxi = 0, full = 0, paus = 0, need = 0, quit = 0;
int vid = -1, aud = -1;
GLFWwindow* win; ao_device* ao; uint8_t *buf = NULL;
pthread_t t; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
AVFormatContext *fmt = NULL;
AVFrame *inp, *out; AVPacket *pck;
struct SwsContext *sws; SwrContext *swr;
AVCodecContext *vidctx = NULL, *audctx = NULL;
GLint vps_loc, crd_loc, tex_loc;
GLuint vrt_buf, vrt_shd, frg_shd, texID, prg;
static const GLfloat vrt[] = { 1,1,1,0, 1,-1,1,1, -1,1,0,0, -1,-1,0,1 };
static const char* vrt_shd_text =
"attribute vec2 vps; varying vec2 TexCoordOut; attribute vec2 crd;"
"void main(){ gl_Position = vec4(vps,0,1); TexCoordOut = mat2(0.5,0,0,-0.5)*(crd+vec2(1,1)); }";
static const char* frg_shd_text =
"uniform sampler2D tex; varying vec2 TexCoordOut;"
"void main(){ gl_FragColor = texture2D(tex, TexCoordOut); }";
void error_callback(int d, const char* s) {
fprintf(stderr, "error: %d %s\n", d, s);
}
void resize_callback(GLFWwindow* win, int w, int h){
if ((w!=0)&&(h!=0)) { winw=w,winh=h; } else { w=winw,h=winh; }
float n = ((float)outw / (float)outh);
int t = maxi || (w<outw) || (h<outh);
imgw = t ? ((h*n)<=w) ? h*n : w : outw;
imgh = t ? ((h*n)> w) ? w/n : h : outh;
imgx = (w-imgw)/2, imgy = (h-imgh)/2;
need = 1;
}
void key_callback(GLFWwindow* win,int i1,int i2,int i3, int i4){
if (i3 != 1) { return; }
if (i1 == GLFW_KEY_ENTER ){ paus ^= 1; }
if (i1 == GLFW_KEY_ESCAPE){ quit = 1; }
if (i1 == GLFW_KEY_M ){ maxi ^= 1; resize_callback(NULL, 0, 0); }
if (i1 == GLFW_KEY_F ){ full ^= 1; if (full) { glfwMaximizeWindow(win); } else { glfwRestoreWindow(win); glfwSetWindowSize(win,outw,outh); } }
}
int end(char* s){
if(s){ printf("%s\n", s); }
quit = 1;
pthread_mutex_unlock(&mutex);
pthread_join(t,NULL);
ao_shutdown();
if(inp){ av_frame_free(&inp); }
if(out){ av_frame_free(&out); }
if(buf){ av_free(buf); }
if(audctx){ avcodec_close(audctx); }
if(vidctx){ avcodec_close(vidctx); }
avformat_close_input(&fmt);
glfwTerminate();
return !!s;
}
int av(){
int ret;
ao_initialize();
ao_sample_format sample_format = {16,48000,2,AO_FMT_NATIVE,NULL};
ao = ao_open_live(ao_default_driver_id(), &sample_format, NULL);
while (!quit) {
ret = av_read_frame(fmt, pck);
if (ret < 0) {
if ((!paus)&&(aud!=-1)){ av_seek_frame(fmt,aud,0,0); }
if ((!paus)&&(vid!=-1)){ av_seek_frame(fmt,vid,0,0); }
paus = 1;
pthread_mutex_lock(&mutex);
continue;
}
if ((pck->stream_index==aud)&&(aud>=0)) {
out->channel_layout = AV_CH_LAYOUT_STEREO; out->format = AV_SAMPLE_FMT_S16; out->sample_rate = 48000;
ret = avcodec_send_packet(audctx, pck); if (ret == AVERROR(EAGAIN)) { continue; }; if (ret != 0) { return end("!5"); };
ret = avcodec_receive_frame(audctx, inp); if (ret == AVERROR(EAGAIN)) { continue; }; if (ret != 0) { return end("!6"); }
ret = swr_convert_frame(swr, out, inp); if (ret != 0) { return end("!7"); }
ao_play(ao, out->extended_data[0], out->nb_samples * 2 * 2);
}
if ((pck->stream_index==vid)&&(vid>=0)) {
ret = avcodec_send_packet(vidctx, pck); if (ret == AVERROR(EAGAIN)) { continue; }; if (ret != 0) { return end("!8"); }
ret = avcodec_receive_frame(vidctx, inp); if (ret == AVERROR(EAGAIN)) { continue; }; if (ret != 0) { return end("!9"); }
av_image_fill_arrays(out->data, out->linesize, buf, AV_PIX_FMT_RGB24, outw, outh, 1);
sws_scale(sws, (uint8_t const* const*)inp->data, inp->linesize, 0, vidctx->height, out->data, out->linesize);
pthread_mutex_lock(&mutex);
}
av_frame_unref(inp); av_frame_unref(out); av_packet_unref(pck);
}
}
int av_setup(const char *filename){
int ret;
if ((ret = avformat_open_input(&fmt, filename, 0, 0)) < 0) { return end(NULL); }
if ((ret = avformat_find_stream_info(fmt, 0)) < 0) { return end(NULL); }
for (int q=0; fmt->nb_streams>q; q++) {
int t = fmt->streams[q]->codecpar->codec_type;
if((-1==vid)&&(AVMEDIA_TYPE_VIDEO==t)){ vid = q; }
if((-1==aud)&&(AVMEDIA_TYPE_AUDIO==t)){ aud = q; }
}
if ((vid==-1)&&(aud==-1)) { return end("no media!"); }
if (vid >= 0) {
AVCodecParameters *par = fmt->streams[vid]->codecpar; AVCodec *cdc = avcodec_find_decoder(par->codec_id); if(!cdc){ return end("!1"); }
vidctx = avcodec_alloc_context3(cdc); avcodec_parameters_to_context(vidctx, par); ret = avcodec_open2(vidctx, cdc, NULL); if(ret){ return end("!2"); }
outw = (vidctx->width+7)&~7, outh = vidctx->height;
buf = (uint8_t *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, outw, outh, 1));
sws = sws_getContext(vidctx->width,vidctx->height,vidctx->pix_fmt, outw,outh,AV_PIX_FMT_RGB24, SWS_BILINEAR,NULL,NULL,NULL);
}
if (aud >= 0) {
AVCodecParameters *par = fmt->streams[aud]->codecpar; AVCodec *cdc = avcodec_find_decoder(par->codec_id); if(!cdc){ return end("!3"); }
audctx = avcodec_alloc_context3(cdc); avcodec_parameters_to_context(audctx, par); ret = avcodec_open2(audctx, cdc, NULL); if(ret){ return end("!4"); }
swr = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16,48000, par->channel_layout,par->format,par->sample_rate, 0, NULL);
}
inp = av_frame_alloc(); out = av_frame_alloc(); pck = av_packet_alloc();
return -1;
}
int main(int argc, const char *argv[]) {
if(argc < 2) { printf("no file!\n"); return -1; }
int ret = av_setup(argv[1]); if (ret != -1) { return ret; }
glfwSetErrorCallback(error_callback);
if (!glfwInit()) { return -1; }
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
win = glfwCreateWindow(imgw, imgh, "GLFW", NULL, NULL);
if (!win) { glfwTerminate(); return -2; }
glfwMakeContextCurrent(win); glfwSetFramebufferSizeCallback(win, resize_callback); glfwSetKeyCallback(win, key_callback);
glGenBuffers(1, &vrt_buf); glBindBuffer(GL_ARRAY_BUFFER, vrt_buf); glBufferData(GL_ARRAY_BUFFER, 4*16, vrt, GL_STATIC_DRAW);
vrt_shd = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vrt_shd, 1, &vrt_shd_text, NULL); glCompileShader(vrt_shd);
frg_shd = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(frg_shd, 1, &frg_shd_text, NULL); glCompileShader(frg_shd);
prg = glCreateProgram(); glAttachShader(prg, vrt_shd); glAttachShader(prg, frg_shd); glLinkProgram(prg);
tex_loc = glGetUniformLocation(prg, "tex"); vps_loc = glGetAttribLocation(prg, "vps"); crd_loc = glGetAttribLocation(prg, "crd");
glEnableVertexAttribArray(vps_loc); glVertexAttribPointer(vps_loc, 2, GL_FLOAT, GL_FALSE, 16, NULL);
glEnableVertexAttribArray(crd_loc); glVertexAttribPointer(crd_loc, 2, GL_FLOAT, GL_FALSE, 16, NULL);
glGenTextures(1,&texID); glBindTexture(GL_TEXTURE_2D, texID); glActiveTexture(GL_TEXTURE0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
resize_callback(NULL, winw, winh);
pthread_create(&t,NULL,(void *)av,NULL);
while (!quit) {
glfwPollEvents();
if (paus&&(!need)) { usleep(100000); continue; }
glUseProgram(prg); glViewport(imgx, imgy, imgw, imgh);
glClearColor(0.5,0.5,0.5, 1.0); glClear (GL_COLOR_BUFFER_BIT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, outw, outh, 0, GL_RGB, GL_UNSIGNED_BYTE, buf);
glDrawArrays(GL_TRIANGLES, 0, 3); glDrawArrays(GL_TRIANGLES, 1, 3);
glfwSwapBuffers(win);
need = 0;
if (!paus) { pthread_mutex_unlock(&mutex); }
}
return end(NULL);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment