Created
December 17, 2019 14:05
-
-
Save mashingan/e94d41e21c6e0ce4c9f19cea72a57dc4 to your computer and use it in GitHub Desktop.
Revisions
-
mashingan created this gist
Dec 17, 2019 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,269 @@ #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; } } } }