-
-
Save pumbur/4c109088e0b8b076057124980318f558 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
// 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