Skip to content

Instantly share code, notes, and snippets.

@mashingan
Created December 17, 2019 14:05
Show Gist options
  • Save mashingan/e94d41e21c6e0ce4c9f19cea72a57dc4 to your computer and use it in GitHub Desktop.
Save mashingan/e94d41e21c6e0ce4c9f19cea72a57dc4 to your computer and use it in GitHub Desktop.
Simplest example of creating player using FFMpeg and SDL2. Currently with choppy audio playing.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <windows.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
//#include <libavutil/frame.h>
#include <SDL2/SDL.h>
/*
ffmpeg path: d:/dev/libffmpeg
sdl2 path: d:/dev/sdl2
to compile it:
gcc -Id:/dev/libffmpeg/include -Id:/dev/sdl2/include \
-Ld:/dev/libffmpeg/lib -Ld:/dev/sdl2/lib \
fplay.c -lavcodec -lavutil -lavformat -lmingw32 -lSDL2main -lSDL2
*/
void display(AVCodecContext*, AVPacket*, AVFrame*, SDL_Rect*,
SDL_Texture*, SDL_Renderer*, double);
void playaudio(AVCodecContext *ctx, AVPacket *pkt, AVFrame *frame,
SDL_AudioDeviceID auddev);
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("usage: %s <filename>\n", argv[0]);
exit(EXIT_FAILURE);
}
// ffmpeg part
AVFormatContext *pFormatCtx;
int vidId = -1, audId = -1;
double fpsrendering = 0.0;
AVCodecContext *vidCtx, *audCtx;
AVCodec *vidCodec, *audCodec;
AVCodecParameters *vidpar, *audpar;
AVFrame *vframe, *aframe;
AVPacket *packet;
//sdl part
int swidth, sheight;
SDL_Window *screen;
SDL_Renderer *renderer;
SDL_Texture *texture;
SDL_Rect rect;
SDL_AudioDeviceID auddev;
SDL_AudioSpec want, have;
SDL_Init(SDL_INIT_EVERYTHING);
pFormatCtx = avformat_alloc_context();
char bufmsg[1024];
if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) < 0) {
sprintf(bufmsg, "Cannot open %s", argv[1]);
perror(bufmsg);
goto clean_format_context;
}
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
perror("Cannot find stream info. Quitting.");
goto clean_format_context;
}
bool foundVideo = false, foundAudio = false;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
AVCodecParameters *localparam = pFormatCtx->streams[i]->codecpar;
AVCodec *localcodec = avcodec_find_decoder(localparam->codec_id);
if (localparam->codec_type == AVMEDIA_TYPE_VIDEO && !foundVideo) {
vidCodec = localcodec;
vidpar = localparam;
vidId = i;
AVRational rational = pFormatCtx->streams[i]->avg_frame_rate;
fpsrendering = 1.0 / ((double)rational.num / (double)(rational.den));
foundVideo = true;
} else if (localparam->codec_type == AVMEDIA_TYPE_AUDIO && !foundAudio) {
audCodec = localcodec;
audpar = localparam;
audId = i;
foundAudio = true;
}
if (foundVideo && foundAudio) { break; }
}
vidCtx = avcodec_alloc_context3(vidCodec);
audCtx = avcodec_alloc_context3(audCodec);
if (avcodec_parameters_to_context(vidCtx, vidpar) < 0) {
perror("vidCtx");
goto clean_codec_context;
}
if (avcodec_parameters_to_context(audCtx, audpar) < 0) {
perror("audCtx");
goto clean_codec_context;
}
if (avcodec_open2(vidCtx, vidCodec, NULL) < 0) {
perror("vidCtx");
goto clean_codec_context;
}
if (avcodec_open2(audCtx, audCodec, NULL) < 0) {
perror("audCtx");
goto clean_codec_context;
}
vframe = av_frame_alloc();
aframe = av_frame_alloc();
packet = av_packet_alloc();
swidth = vidpar->width;
sheight = vidpar->height;
screen = SDL_CreateWindow("Fplay", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
swidth, sheight, SDL_WINDOW_OPENGL);
if (!screen) {
perror("screen");
goto clean_packet_frame;
}
renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED);
if (!renderer) {
perror("renderer");
goto clean_renderer;
}
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV,
SDL_TEXTUREACCESS_STREAMING | SDL_TEXTUREACCESS_TARGET,
swidth, sheight);
if (!texture) {
perror("texture");
goto clean_texture;
}
rect.x = 0;
rect.y = 0;
rect.w = swidth;
rect.h = sheight;
SDL_zero(want);
SDL_zero(have);
want.samples = audpar->sample_rate;
want.channels = audpar->channels;
auddev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
SDL_PauseAudioDevice(auddev, 0);
if (!auddev) {
perror("auddev");
goto clean_audio_device;
}
SDL_Event evt;
uint32_t windowID = SDL_GetWindowID(screen);
bool running = true;
while (running) {
while (av_read_frame(pFormatCtx, packet) >= 0)
{
while (SDL_PollEvent(&evt))
{
switch (evt.type) {
case SDL_WINDOWEVENT: {
if (evt.window.windowID == windowID) {
switch (evt.window.event) {
case SDL_WINDOWEVENT_CLOSE: {
evt.type = SDL_QUIT;
running = false;
SDL_PushEvent(&evt);
break;
}
};
}
break;
}
case SDL_QUIT: {
running = false;
break;
}
}
}
if (packet->stream_index == vidId) {
display(vidCtx, packet, vframe, &rect,
texture, renderer, fpsrendering);
} else if (packet->stream_index == audId) {
playaudio(audCtx, packet, aframe, auddev);
}
av_packet_unref(packet);
}
}
clean_audio_device:
SDL_CloseAudioDevice(auddev);
clean_texture:
SDL_DestroyTexture(texture);
clean_renderer:
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(screen);
clean_packet_frame:
av_packet_free(&packet);
av_frame_free(&vframe);
av_frame_free(&aframe);
clean_codec_context:
avcodec_free_context(&vidCtx);
avcodec_free_context(&audCtx);
clean_format_context:
avformat_close_input(&pFormatCtx);
avformat_free_context(pFormatCtx);
SDL_Quit();
return 0;
}
void display(AVCodecContext* ctx, AVPacket* pkt, AVFrame* frame, SDL_Rect* rect,
SDL_Texture* texture, SDL_Renderer* renderer, double fpsrend)
{
time_t start = time(NULL);
if (avcodec_send_packet(ctx, pkt) < 0) {
perror("send packet");
return;
}
if (avcodec_receive_frame(ctx, frame) < 0) {
perror("receive frame");
return;
}
int framenum = ctx->frame_number;
if ((framenum % 1000) == 0) {
printf("Frame %d (size=%d pts %d dts %d key_frame %d"
" [ codec_picture_number %d, display_picture_number %d\n",
framenum, frame->pkt_size, frame->pts, frame->pkt_dts, frame->key_frame,
frame->coded_picture_number, frame->display_picture_number);
}
SDL_UpdateYUVTexture(texture, rect,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],
frame->data[2], frame->linesize[2]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, rect);
SDL_RenderPresent(renderer);
time_t end = time(NULL);
double diffms = difftime(end, start) / 1000.0;
if (diffms < fpsrend) {
uint32_t diff = (uint32_t)((fpsrend - diffms) * 1000);
printf("diffms: %f, delay time %d ms.\n", diffms, diff);
SDL_Delay(diff);
}
}
void playaudio(AVCodecContext *ctx, AVPacket *pkt, AVFrame *frame,
SDL_AudioDeviceID auddev)
{
if (avcodec_send_packet(ctx, pkt) < 0) {
perror("send packet");
return;
}
if (avcodec_receive_frame(ctx, frame) < 0) {
perror("receive frame");
return;
}
int size;
int bufsize = av_samples_get_buffer_size(&size, ctx->channels,
frame->nb_samples, frame->format, 0);
bool isplanar = av_sample_fmt_is_planar(frame->format) == 1;
for (int ch = 0; ch < ctx->channels; ch++) {
if (!isplanar) {
if (SDL_QueueAudio(auddev, frame->data[ch], frame->linesize[ch]) < 0) {
perror("playaudio");
printf(" %s\n", SDL_GetError());
return;
}
} else {
if (SDL_QueueAudio(auddev, frame->data[0] + size*ch, size) < 0) {
perror("playaudio");
printf(" %s\n", SDL_GetError());
return;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment