nuclear@4: /* nuclear@4: Stereoplay - an OpenGL stereoscopic video player. nuclear@4: Copyright (C) 2011 John Tsiombikas nuclear@4: nuclear@4: This program is free software: you can redistribute it and/or modify nuclear@4: it under the terms of the GNU General Public License as published by nuclear@4: the Free Software Foundation, either version 3 of the License, or nuclear@4: (at your option) any later version. nuclear@4: nuclear@4: This program is distributed in the hope that it will be useful, nuclear@4: but WITHOUT ANY WARRANTY; without even the implied warranty of nuclear@4: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nuclear@4: GNU General Public License for more details. nuclear@4: nuclear@4: You should have received a copy of the GNU General Public License nuclear@4: along with this program. If not, see . nuclear@4: */ nuclear@4: nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "vid.h" nuclear@0: nuclear@0: struct video_file { nuclear@0: AVFormatContext *avctx; nuclear@0: AVCodecContext *cctx; nuclear@0: AVCodec *codec; nuclear@0: int vstream, audio_stream; nuclear@0: struct SwsContext *sws; nuclear@0: nuclear@0: AVFrame *frm, *rgbfrm; nuclear@0: }; nuclear@0: nuclear@0: struct video_file *vid_open(const char *fname) nuclear@0: { nuclear@0: static int initialized; nuclear@0: struct video_file *vf; nuclear@0: int i; nuclear@0: nuclear@0: if(!initialized) { nuclear@0: av_register_all(); nuclear@0: initialized = 1; nuclear@0: } nuclear@0: nuclear@0: if(!(vf = malloc(sizeof *vf))) { nuclear@0: fprintf(stderr, "open_video(%s): failed to allocate memory: %s\n", fname, strerror(errno)); nuclear@0: return 0; nuclear@0: } nuclear@0: memset(vf, 0, sizeof *vf); nuclear@0: nuclear@0: if(av_open_input_file(&vf->avctx, fname, 0, 0, 0) != 0) { nuclear@0: fprintf(stderr, "open_video(%s): failed to open file\n", fname); nuclear@0: vid_close(vf); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(av_find_stream_info(vf->avctx) < 0) { nuclear@0: fprintf(stderr, "open_video(%s): failed to find stream info\n", fname); nuclear@0: vid_close(vf); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: vf->vstream = -1; nuclear@0: for(i=0; iavctx->nb_streams; i++) { nuclear@2: if(vf->avctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { nuclear@0: vf->vstream = i; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: if(vf->vstream == -1) { nuclear@0: fprintf(stderr, "open_video(%s): didn't find a video stream\n", fname); nuclear@0: vid_close(vf); nuclear@0: return 0; nuclear@0: } nuclear@0: vf->cctx = vf->avctx->streams[vf->vstream]->codec; nuclear@0: nuclear@0: if(!(vf->codec = avcodec_find_decoder(vf->cctx->codec_id))) { nuclear@0: fprintf(stderr, "open_video(%s): unsupported codec\n", fname); nuclear@0: vid_close(vf); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(avcodec_open(vf->cctx, vf->codec) < 0) { nuclear@0: fprintf(stderr, "open_video(%s): failed to open codec\n", fname); nuclear@0: vid_close(vf); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: if(!(vf->frm = avcodec_alloc_frame()) || !(vf->rgbfrm = avcodec_alloc_frame())) { nuclear@0: fprintf(stderr, "open_video(%s): failed to allocate frame\n", fname); nuclear@0: vid_close(vf); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: vf->sws = sws_getContext(vf->cctx->width, vf->cctx->height, vf->cctx->pix_fmt, nuclear@0: vf->cctx->width, vf->cctx->height, PIX_FMT_RGB32, SWS_POINT, 0, 0, 0); nuclear@0: if(!vf->sws) { nuclear@0: fprintf(stderr, "open_video(%s): failed to allocate sws context\n", fname); nuclear@0: vid_close(vf); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: printf("using codec: %s\n", vf->codec->name); nuclear@0: printf("fps: %f (%u usec frame interval)\n", vid_fps(vf), vid_frame_interval(vf)); nuclear@0: printf("size: %dx%d\n", vid_frame_width(vf), vid_frame_height(vf)); nuclear@0: nuclear@0: return vf; nuclear@0: } nuclear@0: nuclear@0: void vid_close(struct video_file *vf) nuclear@0: { nuclear@0: if(!vf) return; nuclear@0: nuclear@0: /* TODO how do we deallocate sws contexts? */ nuclear@0: if(vf->rgbfrm) av_free(vf->rgbfrm); nuclear@0: if(vf->frm) av_free(vf->frm); nuclear@0: if(vf->cctx) avcodec_close(vf->cctx); nuclear@0: if(vf->avctx) av_close_input_file(vf->avctx); nuclear@0: free(vf); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: float vid_fps(struct video_file *vf) nuclear@0: { nuclear@0: float inv_tb = (float)vf->cctx->time_base.den / (float)vf->cctx->time_base.num; nuclear@0: return inv_tb / (float)vf->cctx->ticks_per_frame; nuclear@0: } nuclear@0: nuclear@0: unsigned int vid_frame_interval(struct video_file *vf) nuclear@0: { nuclear@0: float fps = vid_fps(vf); nuclear@0: return 1000000 / fps; nuclear@0: } nuclear@0: nuclear@0: int vid_frame_width(struct video_file *vf) nuclear@0: { nuclear@0: return vf->cctx->width; nuclear@0: } nuclear@0: nuclear@0: int vid_frame_height(struct video_file *vf) nuclear@0: { nuclear@0: return vf->cctx->height; nuclear@0: } nuclear@0: nuclear@0: size_t vid_frame_size(struct video_file *vf) nuclear@0: { nuclear@0: return vf->cctx->width * vf->cctx->height * 4; nuclear@0: } nuclear@0: nuclear@0: int vid_get_frame(struct video_file *vf, uint32_t *img) nuclear@0: { nuclear@0: AVPacket packet; nuclear@0: int frame_done = 0; nuclear@0: AVPicture *rgbfrm = (AVPicture*)vf->rgbfrm; nuclear@0: AVPicture *frm = (AVPicture*)vf->frm; nuclear@0: nuclear@0: avpicture_fill((AVPicture*)vf->rgbfrm, (uint8_t*)img, PIX_FMT_RGB32, vf->cctx->width, vf->cctx->height); nuclear@0: nuclear@0: while(av_read_frame(vf->avctx, &packet) >= 0) { nuclear@0: if(packet.stream_index == vf->vstream) { nuclear@0: avcodec_decode_video2(vf->cctx, vf->frm, &frame_done, &packet); nuclear@0: nuclear@0: if(frame_done) { nuclear@0: sws_scale(vf->sws, frm->data, frm->linesize, 0, vf->cctx->height, rgbfrm->data, rgbfrm->linesize); nuclear@0: av_free_packet(&packet); nuclear@0: return 0; nuclear@0: } nuclear@0: } nuclear@0: av_free_packet(&packet); nuclear@0: } nuclear@0: nuclear@0: return -1; nuclear@0: }