From c2b711d195c6ce6702f693703c249b22add7f2e7 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 14 Dec 2012 02:36:29 -0800 Subject: [PATCH] Move some more methods to the class they're part of --- apps/openmw/mwrender/videoplayer.cpp | 892 +++++++++++++-------------- apps/openmw/mwrender/videoplayer.hpp | 14 + 2 files changed, 449 insertions(+), 457 deletions(-) diff --git a/apps/openmw/mwrender/videoplayer.cpp b/apps/openmw/mwrender/videoplayer.cpp index ec68359aa4..4334bc634c 100644 --- a/apps/openmw/mwrender/videoplayer.cpp +++ b/apps/openmw/mwrender/videoplayer.cpp @@ -97,32 +97,33 @@ void PacketQueue::flush() } - double get_audio_clock(VideoState *is) - { - return is->AudioTrack->getTimeOffset(); - } +static double get_audio_clock(VideoState *is) +{ + return is->AudioTrack->getTimeOffset(); +} - double get_video_clock(VideoState *is) - { - double delta; +static double get_video_clock(VideoState *is) +{ + double delta; - delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; - return is->video_current_pts + delta; - } + delta = (av_gettime() - is->video_current_pts_time) / 1000000.0; + return is->video_current_pts + delta; +} - double get_external_clock(VideoState *is) - { - return av_gettime() / 1000000.0; - } +static double get_external_clock(VideoState *is) +{ + return av_gettime() / 1000000.0; +} + +static double get_master_clock(VideoState *is) +{ + if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) + return get_video_clock(is); + if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) + return get_audio_clock(is); + return get_external_clock(is); +} - double get_master_clock(VideoState *is) - { - if(is->av_sync_type == AV_SYNC_VIDEO_MASTER) - return get_video_clock(is); - if(is->av_sync_type == AV_SYNC_AUDIO_MASTER) - return get_audio_clock(is); - return get_external_clock(is); - } class MovieAudioDecoder : public MWSound::Sound_Decoder { @@ -411,479 +412,459 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc } - void timer_callback(boost::system_time t, VideoState* is) +void VideoState::timer_callback(VideoState* is, boost::system_time t) +{ + boost::this_thread::sleep(t); + is->refresh = true; +} + +/* schedule a video refresh in 'delay' ms */ +void VideoState::schedule_refresh(int delay) +{ + boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); + boost::thread(boost::bind(&timer_callback, this, t)).detach(); +} + + +void VideoState::video_display() +{ + VideoPicture *vp = &this->pictq[this->pictq_rindex]; + + if(this->video_st->codec->width != 0 && this->video_st->codec->height != 0) { - boost::this_thread::sleep(t); - is->refresh = true; + Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName("VideoTexture"); + if(texture.isNull () || static_cast(texture->getWidth()) != this->video_st->codec->width + || static_cast(texture->getHeight()) != this->video_st->codec->height) + { + Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); + texture = Ogre::TextureManager::getSingleton().createManual( + "VideoTexture", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, + this->video_st->codec->width, this->video_st->codec->height, + 0, + Ogre::PF_BYTE_RGBA, + Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + } + Ogre::PixelBox pb(this->video_st->codec->width, this->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); + Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); + buffer->blitFromMemory(pb); + this->display_ready = 1; } - /* schedule a video refresh in 'delay' ms */ - static void schedule_refresh(VideoState *is, int delay) + free(vp->data); +} + +void VideoState::video_refresh_timer() +{ + VideoPicture *vp; + double actual_delay, delay, sync_threshold, ref_clock, diff; + + if(!this->video_st) { - boost::system_time t = boost::get_system_time() + boost::posix_time::milliseconds(delay); - boost::thread(boost::bind(&timer_callback, t, is)).detach(); + this->schedule_refresh(100); + return; + } + if(this->pictq_size == 0) + { + this->refresh = true; + return; } - void video_display(VideoState *is) + vp = &this->pictq[this->pictq_rindex]; + + this->video_current_pts = vp->pts; + this->video_current_pts_time = av_gettime(); + + delay = vp->pts - this->frame_last_pts; /* the pts from last time */ + if(delay <= 0 || delay >= 1.0) { + /* if incorrect delay, use previous one */ + delay = this->frame_last_delay; + } + /* save for next time */ + this->frame_last_delay = delay; + this->frame_last_pts = vp->pts; + + /* update delay to sync to audio if not master source */ + if(this->av_sync_type != AV_SYNC_VIDEO_MASTER) { - VideoPicture *vp; + ref_clock = get_master_clock(this); + diff = vp->pts - ref_clock; - vp = &is->pictq[is->pictq_rindex]; - - if (is->video_st->codec->width != 0 && is->video_st->codec->height != 0) + /* Skip or repeat the frame. Take delay into account + FFPlay still doesn't "know if this is the best guess." */ + sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; + if(fabs(diff) < AV_NOSYNC_THRESHOLD) { - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton ().getByName("VideoTexture"); - if (texture.isNull () || static_cast(texture->getWidth()) != is->video_st->codec->width - || static_cast(texture->getHeight()) != is->video_st->codec->height) - { - Ogre::TextureManager::getSingleton ().remove ("VideoTexture"); - texture = Ogre::TextureManager::getSingleton().createManual( - "VideoTexture", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - is->video_st->codec->width, is->video_st->codec->height, - 0, - Ogre::PF_BYTE_RGBA, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); - } - Ogre::PixelBox pb(is->video_st->codec->width, is->video_st->codec->height, 1, Ogre::PF_BYTE_RGBA, vp->data); - Ogre::HardwarePixelBufferSharedPtr buffer = texture->getBuffer(); - buffer->blitFromMemory(pb); - is->display_ready = 1; + if(diff <= -sync_threshold) + delay = 0; + else if(diff >= sync_threshold) + delay = 2 * delay; } + } + this->frame_timer += delay; - free(vp->data); + /* compute the REAL delay */ + actual_delay = this->frame_timer - (av_gettime() / 1000000.0); + if(actual_delay < 0.010) + { + /* Really it should skip the picture instead */ + actual_delay = 0.010; + } + this->schedule_refresh((int)(actual_delay * 1000 + 0.5)); + + /* show the picture! */ + this->video_display(); + + /* update queue for next picture! */ + if(++this->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) + this->pictq_rindex = 0; + this->pictq_mutex.lock(); + this->pictq_size--; + this->pictq_cond.notify_one(); + this->pictq_mutex.unlock(); +} + + +int VideoState::queue_picture(AVFrame *pFrame, double pts) +{ + VideoPicture *vp; + + /* wait until we have a new pic */ + { + boost::unique_lock lock(this->pictq_mutex); + while(this->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !this->quit) + this->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); + } + if(this->quit) + return -1; + + // windex is set to 0 initially + vp = &this->pictq[this->pictq_windex]; + + // Convert the image into RGBA format for Ogre + if(this->sws_context == NULL) + { + int w = this->video_st->codec->width; + int h = this->video_st->codec->height; + this->sws_context = sws_getContext(w, h, this->video_st->codec->pix_fmt, + w, h, PIX_FMT_RGBA, SWS_BICUBIC, + NULL, NULL, NULL); + if(this->sws_context == NULL) + throw std::runtime_error("Cannot initialize the conversion context!\n"); } + vp->data = (uint8_t*)malloc(this->video_st->codec->width * this->video_st->codec->height * 4); - void video_refresh_timer(void *userdata) + sws_scale(this->sws_context, pFrame->data, pFrame->linesize, + 0, this->video_st->codec->height, &vp->data, this->rgbaFrame->linesize); + + vp->pts = pts; + + // now we inform our display thread that we have a pic ready + this->pictq_windex = (this->pictq_windex+1) % VIDEO_PICTURE_QUEUE_SIZE; + this->pictq_mutex.lock(); + this->pictq_size++; + this->pictq_mutex.unlock(); + + return 0; +} + +double VideoState::synchronize_video(AVFrame *src_frame, double pts) +{ + double frame_delay; + + /* if we have pts, set video clock to it */ + if(pts != 0) + this->video_clock = pts; + else + pts = this->video_clock; + + /* update the video clock */ + frame_delay = av_q2d(this->video_st->codec->time_base); + + /* if we are repeating a frame, adjust clock accordingly */ + frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); + this->video_clock += frame_delay; + + return pts; +} + + +/* These are called whenever we allocate a frame + * buffer. We use this to store the global_pts in + * a frame at the time it is allocated. + */ +static uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; +static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + int ret = avcodec_default_get_buffer(c, pic); + uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); + *pts = global_video_pkt_pts; + pic->opaque = pts; + return ret; +} +static void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + if(pic) av_freep(&pic->opaque); + avcodec_default_release_buffer(c, pic); +} + + +void VideoState::video_thread_loop(VideoState *self) +{ + AVPacket pkt1, *packet = &pkt1; + int frameFinished; + AVFrame *pFrame; + double pts; + + pFrame = avcodec_alloc_frame(); + + self->rgbaFrame = avcodec_alloc_frame(); + avpicture_alloc((AVPicture*)self->rgbaFrame, PIX_FMT_RGBA, self->video_st->codec->width, self->video_st->codec->height); + + while(self->videoq.get(packet, self, 1) >= 0) { - VideoState *is = (VideoState *)userdata; - VideoPicture *vp; - double actual_delay, delay, sync_threshold, ref_clock, diff; + // Save global pts to be stored in pFrame + global_video_pkt_pts = packet->pts; + // Decode video frame + if(avcodec_decode_video2(self->video_st->codec, pFrame, &frameFinished, packet) < 0) + throw std::runtime_error("Error decoding video frame"); - if(!is->video_st) + pts = 0; + if((uint64_t)packet->dts != AV_NOPTS_VALUE) + pts = packet->dts; + else if(pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) + pts = *(uint64_t*)pFrame->opaque; + pts *= av_q2d(self->video_st->time_base); + + // Did we get a video frame? + if(frameFinished) { - schedule_refresh(is, 100); - return; + pts = self->synchronize_video(pFrame, pts); + if(self->queue_picture(pFrame, pts) < 0) + break; } - if(is->pictq_size == 0) - { - is->refresh = true; - return; - } - - vp = &is->pictq[is->pictq_rindex]; - - is->video_current_pts = vp->pts; - is->video_current_pts_time = av_gettime(); - - delay = vp->pts - is->frame_last_pts; /* the pts from last time */ - if(delay <= 0 || delay >= 1.0) { - /* if incorrect delay, use previous one */ - delay = is->frame_last_delay; - } - /* save for next time */ - is->frame_last_delay = delay; - is->frame_last_pts = vp->pts; - - /* update delay to sync to audio if not master source */ - if(is->av_sync_type != AV_SYNC_VIDEO_MASTER) - { - ref_clock = get_master_clock(is); - diff = vp->pts - ref_clock; - - /* Skip or repeat the frame. Take delay into account - FFPlay still doesn't "know if this is the best guess." */ - sync_threshold = (delay > AV_SYNC_THRESHOLD) ? delay : AV_SYNC_THRESHOLD; - if(fabs(diff) < AV_NOSYNC_THRESHOLD) - { - if(diff <= -sync_threshold) - delay = 0; - else if(diff >= sync_threshold) - delay = 2 * delay; - } - } - - is->frame_timer += delay; - /* computer the REAL delay */ - actual_delay = is->frame_timer - (av_gettime() / 1000000.0); - if(actual_delay < 0.010) - { - /* Really it should skip the picture instead */ - actual_delay = 0.010; - } - schedule_refresh(is, (int)(actual_delay * 1000 + 0.5)); - - /* show the picture! */ - video_display(is); - - /* update queue for next picture! */ - if(++is->pictq_rindex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_rindex = 0; - is->pictq_mutex.lock(); - is->pictq_size--; - is->pictq_cond.notify_one (); - is->pictq_mutex.unlock (); + av_free_packet(packet); } - int queue_picture(VideoState *is, AVFrame *pFrame, double pts) + av_free(pFrame); + + avpicture_free((AVPicture*)self->rgbaFrame); + av_free(self->rgbaFrame); +} + +void VideoState::decode_thread_loop(VideoState *self) +{ + AVFormatContext *pFormatCtx = self->format_ctx; + AVPacket pkt1, *packet = &pkt1; + + try { - VideoPicture *vp; - - /* wait until we have a new pic */ - { - boost::unique_lock lock(is->pictq_mutex); - while(is->pictq_size >= VIDEO_PICTURE_QUEUE_SIZE && !is->quit) - is->pictq_cond.timed_wait(lock, boost::posix_time::milliseconds(1)); - } - - if(is->quit) - return -1; - - // windex is set to 0 initially - vp = &is->pictq[is->pictq_windex]; - - // Convert the image into YUV format that SDL uses - if(is->sws_context == NULL) - { - int w = is->video_st->codec->width; - int h = is->video_st->codec->height; - is->sws_context = sws_getContext(w, h, is->video_st->codec->pix_fmt, - w, h, PIX_FMT_RGBA, SWS_BICUBIC, - NULL, NULL, NULL); - if(is->sws_context == NULL) - throw std::runtime_error("Cannot initialize the conversion context!\n"); - } - - vp->data =(uint8_t*) malloc(is->video_st->codec->width * is->video_st->codec->height * 4); - - sws_scale(is->sws_context, pFrame->data, pFrame->linesize, - 0, is->video_st->codec->height, &vp->data, is->rgbaFrame->linesize); - - vp->pts = pts; - - // now we inform our display thread that we have a pic ready - if(++is->pictq_windex == VIDEO_PICTURE_QUEUE_SIZE) - is->pictq_windex = 0; - is->pictq_mutex.lock(); - is->pictq_size++; - is->pictq_mutex.unlock(); - - return 0; - } - - double synchronize_video(VideoState *is, AVFrame *src_frame, double pts) - { - double frame_delay; - - if(pts != 0) - { - /* if we have pts, set video clock to it */ - is->video_clock = pts; - } - else - { - /* if we aren't given a pts, set it to the clock */ - pts = is->video_clock; - } - /* update the video clock */ - frame_delay = av_q2d(is->video_st->codec->time_base); - /* if we are repeating a frame, adjust clock accordingly */ - frame_delay += src_frame->repeat_pict * (frame_delay * 0.5); - is->video_clock += frame_delay; - return pts; - } - - uint64_t global_video_pkt_pts = AV_NOPTS_VALUE; - - /* These are called whenever we allocate a frame - * buffer. We use this to store the global_pts in - * a frame at the time it is allocated. - */ - int our_get_buffer(struct AVCodecContext *c, AVFrame *pic) - { - int ret = avcodec_default_get_buffer(c, pic); - uint64_t *pts = (uint64_t*)av_malloc(sizeof(uint64_t)); - *pts = global_video_pkt_pts; - pic->opaque = pts; - return ret; - } - void our_release_buffer(struct AVCodecContext *c, AVFrame *pic) - { - if(pic) av_freep(&pic->opaque); - avcodec_default_release_buffer(c, pic); - } - - int video_thread(void *arg) - { - VideoState *is = (VideoState *)arg; - AVPacket pkt1, *packet = &pkt1; - int frameFinished; - AVFrame *pFrame; - double pts; - - pFrame = avcodec_alloc_frame(); - - is->rgbaFrame = avcodec_alloc_frame(); - avpicture_alloc((AVPicture*)is->rgbaFrame, PIX_FMT_RGBA, is->video_st->codec->width, is->video_st->codec->height); + if(self->videoStream < 0 && self->audioStream < 0) + throw std::runtime_error("No streams to decode"); + // main decode loop for(;;) { - if(is->videoq.get(packet, is, 1) < 0) - { - // means we quit getting packets + if(self->quit) break; + + if((self->audioStream >= 0 && self->audioq.size > MAX_AUDIOQ_SIZE) || + (self->videoStream >= 0 && self->videoq.size > MAX_VIDEOQ_SIZE)) + { + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + continue; } - pts = 0; - // Save global pts to be stored in pFrame - global_video_pkt_pts = packet->pts; - // Decode video frame - if (avcodec_decode_video2(is->video_st->codec, pFrame, &frameFinished, packet) < 0) - throw std::runtime_error("Error decoding video frame"); + if(av_read_frame(pFormatCtx, packet) < 0) + break; - if((uint64_t)packet->dts == AV_NOPTS_VALUE && - pFrame->opaque && *(uint64_t*)pFrame->opaque != AV_NOPTS_VALUE) - pts = *(uint64_t *)pFrame->opaque; - else if((uint64_t)packet->dts != AV_NOPTS_VALUE) - pts = packet->dts; + // Is this a packet from the video stream? + if(packet->stream_index == self->videoStream) + self->videoq.put(packet); + else if(packet->stream_index == self->audioStream) + self->audioq.put(packet); else - pts = 0; - pts *= av_q2d(is->video_st->time_base); - - // Did we get a video frame? - if(frameFinished) - { - pts = synchronize_video(is, pFrame, pts); - if(queue_picture(is, pFrame, pts) < 0) - break; - } - av_free_packet(packet); + av_free_packet(packet); } - - av_free(pFrame); - - avpicture_free((AVPicture *)is->rgbaFrame); - av_free(is->rgbaFrame); - - return 0; + /* all done - wait for it */ + while(!self->quit) + { + // EOF reached, all packets processed, we can exit now + if(self->audioq.nb_packets == 0 && self->videoq.nb_packets == 0) + break; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + } + catch(std::runtime_error& e) { + std::cerr << "An error occured playing the video: " << e.what () << std::endl; + } + catch(Ogre::Exception& e) { + std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; } - int stream_component_open(VideoState *is, int stream_index, AVFormatContext *pFormatCtx) + self->quit = 1; +} + + +int VideoState::stream_open(int stream_index, AVFormatContext *pFormatCtx) +{ + MWSound::DecoderPtr decoder; + AVCodecContext *codecCtx; + AVCodec *codec; + + if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) + return -1; + + // Get a pointer to the codec context for the video stream + codecCtx = pFormatCtx->streams[stream_index]->codec; + codec = avcodec_find_decoder(codecCtx->codec_id); + if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) { - MWSound::DecoderPtr decoder; - AVCodecContext *codecCtx; - AVCodec *codec; - - if(stream_index < 0 || stream_index >= static_cast(pFormatCtx->nb_streams)) - return -1; - - // Get a pointer to the codec context for the video stream - codecCtx = pFormatCtx->streams[stream_index]->codec; - codec = avcodec_find_decoder(codecCtx->codec_id); - if(!codec || (avcodec_open2(codecCtx, codec, NULL) < 0)) - { - fprintf(stderr, "Unsupported codec!\n"); - return -1; - } - - switch(codecCtx->codec_type) - { - case AVMEDIA_TYPE_AUDIO: - is->audioStream = stream_index; - is->audio_st = pFormatCtx->streams[stream_index]; - - /* averaging filter for audio sync */ - is->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); - is->audio_diff_avg_count = 0; - /* Correct audio only if larger error than this */ - is->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; - - memset(&is->audio_pkt, 0, sizeof(is->audio_pkt)); - - decoder.reset(new MovieAudioDecoder(is)); - is->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); - if(!is->AudioTrack) - { - is->audioStream = -1; - avcodec_close(is->audio_st->codec); - is->audio_st = NULL; - return -1; - } - break; - - case AVMEDIA_TYPE_VIDEO: - is->videoStream = stream_index; - is->video_st = pFormatCtx->streams[stream_index]; - - is->frame_timer = (double)av_gettime() / 1000000.0; - is->frame_last_delay = 40e-3; - is->video_current_pts_time = av_gettime(); - - codecCtx->get_buffer = our_get_buffer; - codecCtx->release_buffer = our_release_buffer; - is->video_thread = boost::thread(video_thread, is); - break; - - default: - break; - } - - return 0; + fprintf(stderr, "Unsupported codec!\n"); + return -1; } - int decode_thread(void *arg) + switch(codecCtx->codec_type) { - VideoState *is = (VideoState *)arg; - try + case AVMEDIA_TYPE_AUDIO: + this->audioStream = stream_index; + this->audio_st = pFormatCtx->streams[stream_index]; + + /* averaging filter for audio sync */ + this->audio_diff_avg_coef = exp(log(0.01 / AUDIO_DIFF_AVG_NB)); + this->audio_diff_avg_count = 0; + /* Correct audio only if larger error than this */ + this->audio_diff_threshold = 2.0 * 0.1/* 100 ms */; + + memset(&this->audio_pkt, 0, sizeof(this->audio_pkt)); + + decoder.reset(new MovieAudioDecoder(this)); + this->AudioTrack = MWBase::Environment::get().getSoundManager()->playTrack(decoder); + if(!this->AudioTrack) { - AVFormatContext *pFormatCtx = is->format_ctx; - AVPacket pkt1, *packet = &pkt1; - - if(is->videoStream >= 0 /*|| is->audioStream < 0*/) - { - // main decode loop - for(;;) - { - if(is->quit) - break; - - if((is->audioStream >= 0 && is->audioq.size > MAX_AUDIOQ_SIZE) || - is->videoq.size > MAX_VIDEOQ_SIZE) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - continue; - } - - if(av_read_frame(pFormatCtx, packet) < 0) - break; - - // Is this a packet from the video stream? - if(packet->stream_index == is->videoStream) - is->videoq.put(packet); - else if(packet->stream_index == is->audioStream) - is->audioq.put(packet); - else - av_free_packet(packet); - } - /* all done - wait for it */ - while(!is->quit) - { - // EOF reached, all packets processed, we can exit now - if(is->audioq.nb_packets == 0 && is->videoq.nb_packets == 0) - break; - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - } - } - - is->quit = 1; - } - catch (std::runtime_error& e) - { - std::cerr << "An error occured playing the video: " << e.what () << std::endl; - is->quit = 1; - } - catch (Ogre::Exception& e) - { - std::cerr << "An error occured playing the video: " << e.getFullDescription () << std::endl; - is->quit = 1; - } - - return 0; - } - - void VideoState::init(const std::string& resourceName) - { - try - { - int video_index = -1; - int audio_index = -1; - unsigned int i; - - this->av_sync_type = DEFAULT_AV_SYNC_TYPE; - this->videoStream = -1; this->audioStream = -1; - this->refresh = false; - this->quit = 0; - - this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); - if(this->stream.isNull()) - throw std::runtime_error("Failed to open video resource"); - - this->format_ctx = avformat_alloc_context(); - this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); - if(!this->format_ctx->pb) - { - avformat_free_context(this->format_ctx); - throw std::runtime_error("Failed to allocate ioContext "); - } - - // Open video file - /// \todo leak here, ffmpeg or valgrind bug ? - if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) - { - // "Note that a user-supplied AVFormatContext will be freed on failure." - this->format_ctx = NULL; - throw std::runtime_error("Failed to open video input"); - } - - // Retrieve stream information - if(avformat_find_stream_info(this->format_ctx, NULL) < 0) - throw std::runtime_error("Failed to retrieve stream information"); - - // Dump information about file onto standard error - av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); - - for(i = 0;i < this->format_ctx->nb_streams;i++) - { - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) - video_index = i; - if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) - audio_index = i; - } - - if(audio_index >= 0) - stream_component_open(this, audio_index, this->format_ctx); - if(video_index >= 0) - stream_component_open(this, video_index, this->format_ctx); - } - catch(std::runtime_error& e) - { - this->quit = 1; - throw; - } - catch(Ogre::Exception& e) - { - this->quit = 1; - throw; - } - } - - void VideoState::deinit() - { - this->audioq.cond.notify_one(); - this->videoq.cond.notify_one(); - - this->parse_thread.join(); - this->video_thread.join(); - - if(this->audioStream >= 0) avcodec_close(this->audio_st->codec); - if(this->videoStream >= 0) - avcodec_close(this->video_st->codec); - - if(this->sws_context) - sws_freeContext(this->sws_context); - - if(this->format_ctx) - { - AVIOContext *ioContext = this->format_ctx->pb; - avformat_close_input(&this->format_ctx); - av_free(ioContext); + this->audio_st = NULL; + return -1; } + break; + + case AVMEDIA_TYPE_VIDEO: + this->videoStream = stream_index; + this->video_st = pFormatCtx->streams[stream_index]; + + this->frame_timer = (double)av_gettime() / 1000000.0; + this->frame_last_delay = 40e-3; + this->video_current_pts_time = av_gettime(); + + codecCtx->get_buffer = our_get_buffer; + codecCtx->release_buffer = our_release_buffer; + this->video_thread = boost::thread(video_thread_loop, this); + break; + + default: + break; } + return 0; +} + +void VideoState::init(const std::string& resourceName) +{ + try + { + int video_index = -1; + int audio_index = -1; + unsigned int i; + + this->av_sync_type = DEFAULT_AV_SYNC_TYPE; + this->videoStream = -1; + this->audioStream = -1; + this->refresh = false; + this->quit = 0; + + this->stream = Ogre::ResourceGroupManager::getSingleton().openResource(resourceName); + if(this->stream.isNull()) + throw std::runtime_error("Failed to open video resource"); + + this->format_ctx = avformat_alloc_context(); + this->format_ctx->pb = avio_alloc_context(NULL, 0, 0, this, OgreResource_Read, OgreResource_Write, OgreResource_Seek); + if(!this->format_ctx->pb) + { + avformat_free_context(this->format_ctx); + throw std::runtime_error("Failed to allocate ioContext "); + } + + // Open video file + /// \todo leak here, ffmpeg or valgrind bug ? + if (avformat_open_input(&this->format_ctx, resourceName.c_str(), NULL, NULL)) + { + // "Note that a user-supplied AVFormatContext will be freed on failure." + this->format_ctx = NULL; + throw std::runtime_error("Failed to open video input"); + } + + // Retrieve stream information + if(avformat_find_stream_info(this->format_ctx, NULL) < 0) + throw std::runtime_error("Failed to retrieve stream information"); + + // Dump information about file onto standard error + av_dump_format(this->format_ctx, 0, resourceName.c_str(), 0); + + for(i = 0;i < this->format_ctx->nb_streams;i++) + { + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index < 0) + video_index = i; + if(this->format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index < 0) + audio_index = i; + } + + if(audio_index >= 0) + this->stream_open(audio_index, this->format_ctx); + if(video_index >= 0) + this->stream_open(video_index, this->format_ctx); + + this->schedule_refresh(40); + this->parse_thread = boost::thread(decode_thread_loop, this); + } + catch(std::runtime_error& e) + { + this->quit = 1; + throw; + } + catch(Ogre::Exception& e) + { + this->quit = 1; + throw; + } +} + +void VideoState::deinit() +{ + this->audioq.cond.notify_one(); + this->videoq.cond.notify_one(); + + this->parse_thread.join(); + this->video_thread.join(); + + if(this->audioStream >= 0) + avcodec_close(this->audio_st->codec); + if(this->videoStream >= 0) + avcodec_close(this->video_st->codec); + + if(this->sws_context) + sws_freeContext(this->sws_context); + + if(this->format_ctx) + { + AVIOContext *ioContext = this->format_ctx->pb; + avformat_close_input(&this->format_ctx); + av_free(ioContext); + } +} + VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr) : mState(NULL) @@ -942,9 +923,6 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc mState = new VideoState; mState->init(resourceName); - - schedule_refresh(mState, 40); - mState->parse_thread = boost::thread(decode_thread, mState); } void VideoPlayer::update () @@ -956,7 +934,7 @@ int64_t VideoState::OgreResource_Seek(void *user_data, int64_t offset, int whenc else if(mState->refresh) { mState->refresh = false; - video_refresh_timer(mState); + mState->video_refresh_timer(); } } diff --git a/apps/openmw/mwrender/videoplayer.hpp b/apps/openmw/mwrender/videoplayer.hpp index 82a7083ae6..4bbd81a6e7 100644 --- a/apps/openmw/mwrender/videoplayer.hpp +++ b/apps/openmw/mwrender/videoplayer.hpp @@ -83,6 +83,20 @@ namespace MWRender void init(const std::string& resourceName); void deinit(); + int stream_open(int stream_index, AVFormatContext *pFormatCtx); + + static void video_thread_loop(VideoState *is); + static void decode_thread_loop(VideoState *is); + + void video_display(); + void video_refresh_timer(); + + int queue_picture(AVFrame *pFrame, double pts); + double synchronize_video(AVFrame *src_frame, double pts); + + static void timer_callback(VideoState* is, boost::system_time t); + void schedule_refresh(int delay); + static int OgreResource_Read(void *user_data, uint8_t *buf, int buf_size); static int OgreResource_Write(void *user_data, uint8_t *buf, int buf_size); static int64_t OgreResource_Seek(void *user_data, int64_t offset, int whence);