📄 ffmpeg_video.cpp
字号:
if (m_fmt.frameduration <= 0) { // And convert the timestamp#if LIBAVFORMAT_BUILD <= 4623 timestamp_t framerate = m_con->frame_rate; timestamp_t framebase = m_con->frame_rate_base; timestamp_t frameduration = (framebase*1000000)/framerate; AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::_need_fmt_uptodate(): frameduration = %lld", frameduration);#else timestamp_t frameduration = (timestamp_t) round(m_con->time_base.num *1000000.0 / (double) m_con->time_base.den) ; AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::_need_fmt_uptodate(): frameduration = %lld, %d %d", frameduration, m_con->time_base.num, m_con->time_base.den);#endif m_fmt.frameduration = frameduration; }}voidffmpeg_video_decoder_datasource::read_ahead(timestamp_t clip_begin){ assert(m_src); m_src->read_ahead(clip_begin);}void ffmpeg_video_decoder_datasource::data_avail(){ m_lock.enter(); int got_pic; AVPicture picture; int len, dummy2; int pic_fmt, dst_pic_fmt; int w,h; unsigned char* ptr; timestamp_t ipts = 0; uint8_t *inbuf; int sz; got_pic = 0; if (!m_src) { // Cleaning up, apparently m_lock.leave(); return; } // Now that we have gotten this callback we need to restart input at some point. m_start_input = true; // Get the input data inbuf = (uint8_t*) m_src->get_frame(0, &ipts, &sz); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: %d bytes available", sz); if(sz == 0 && !m_src->end_of_file() ) { lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: no data, not eof?"); m_lock.leave(); return; } // No easy error conditions, so let's allocate our frame. AVFrame *frame = avcodec_alloc_frame(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail called (0x%x) ", (void*) this); while(inbuf && sz && m_con) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail:start decoding (0x%x) ", m_con); assert(&m_con != NULL); assert(inbuf); assert(sz < 1000000); // XXXX This is soft, and probably incorrect. Remove when it fails. ptr = inbuf; while (sz > 0) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: decoding picture(s), %d byteas of data ", sz); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: m_con: 0x%x, gotpic = %d, sz = %d ", m_con, got_pic, sz); len = avcodec_decode_video(m_con, frame, &got_pic, ptr, sz); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: avcodec_decode_video: used %d of %d bytes, gotpic = %d, ipts = %lld", len, sz, got_pic, ipts);#if LIBAVFORMAT_BUILD > 4609 // XXX Dirac hack, to be removed. // Some codecs (notably Dirac) always gobble up all bytes, // and only return len==sz if got_pic is true. len = sz;#endif if (len >= 0) { assert(len <= sz); ptr +=len; sz -= len; if (got_pic) { AM_DBG lib::logger::get_logger()->debug("pts seems to be : %lld",ipts); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: decoded picture, used %d bytes, %d left", len, sz); // Setup the AVPicture for the format we want, plus the data pointer _need_fmt_uptodate(); w = m_fmt.width; h = m_fmt.height; m_size = w * h * 4; assert(m_size); char *framedata = (char*) malloc(m_size); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail:framedata=0x%x", framedata); assert(framedata != NULL); dst_pic_fmt = PIX_FMT_RGBA32; dummy2 = avpicture_fill(&picture, (uint8_t*) framedata, dst_pic_fmt, w, h); // The format we have is already in frame. Convert. pic_fmt = m_con->pix_fmt; img_convert(&picture, dst_pic_fmt, (AVPicture*) frame, pic_fmt, w, h);#if defined(AMBULANT_PLATFORM_MACOS) && defined(__LITTLE_ENDIAN__) // The format is now RGBARGBA, but on the Intel mac we need BGRABGRA char *p, c; for (p=framedata; p<framedata+m_size; p+=4) { c = p[0]; p[0] = p[2]; p[2] = c; }#endif // Try and compute the timestamp and update the video clock. timestamp_t pts = 0; timestamp_t frame_delay = 0; #if LIBAVFORMAT_BUILD > 4906 pts = ipts; if (pts != 0) { m_video_clock = pts; } else { pts = m_video_clock; } frame_delay = m_fmt.frameduration; if (frame->repeat_pict) frame_delay += (timestamp_t)(frame->repeat_pict*m_fmt.frameduration*0.5); m_video_clock += frame_delay;#else // ffmpeg 0.4.8 if (ipts != AV_NOPTS_VALUE) pts = ipts; if (m_con->has_b_frames && frame->pict_type != FF_B_TYPE) { pts = m_last_p_pts; m_last_p_pts = ipts; AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail:frame has B frames but this frame is no B frame (this=0x%x) ", this); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail:pts set to %f, remember %f", pts, m_last_p_pts); } if (pts != 0) { m_video_clock = pts; } else { pts = m_video_clock; frame_delay = m_fmt.frameduration; if (frame->repeat_pict) frame_delay += (timestamp_t)(frame->repeat_pict*m_fmt.frameduration*0.5); m_video_clock += frame_delay; }#endif AM_DBG lib::logger::get_logger()->debug("videoclock: ipts=%lld pts=%lld video_clock=%lld, frame_delay=%lld", ipts, pts, m_video_clock, frame_delay); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: storing frame with pts = %lld",pts ); m_frame_count++; bool drop_this_frame = false; if (m_con->has_b_frames && frame->pict_type == FF_B_TYPE && pts < m_src->get_clip_begin()) { // A non-essential frame while skipping forward. AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder: could drop B frame %d, ts=%lld", m_frame_count, pts); drop_this_frame = true; } if (pts < m_old_frame.first) { // A frame that came after this frame has already been consumed. // We should drop this frame. AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder: dropping frame %d: too late", m_frame_count); drop_this_frame = true; } if (!drop_this_frame) { std::pair<timestamp_t, char*> element(pts, framedata); m_frames.push(element); } m_elapsed = pts; } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail: incomplete picture, used %d bytes, %d left", len, sz); } } else { lib::logger::get_logger()->error(gettext("error decoding video frame")); } } // End of while loop AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource.data_avail:done decoding (0x%x) ", m_con); m_src->frame_done(0, false); } av_free(frame); // Now tell our client, if we have data available or are at end of file. AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::data_avail(): m_frames.size() returns %d, (eof=%d)", m_frames.size(), m_src->end_of_file()); if ( m_frames.size() > MIN_VIDEO_FRAMES || m_src->end_of_file()) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::data_avail(): there is some data for the renderer ! (eof=%d)", m_src->end_of_file()); if ( m_client_callback ) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::data_avail(): calling client callback (eof=%d)", m_src->end_of_file()); assert(m_event_processor); m_event_processor->add_event(m_client_callback, 0, ambulant::lib::ep_high); m_client_callback = NULL; //m_event_processor = NULL; } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::data_avail(): No client callback!"); } } // Restart input if there is buffer space and anything remains to be read. Otherwise we // leave m_start_input true, and restarting is taken care of in start_frame(). if (!m_src->end_of_file() && !_buffer_full()) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::start_frame() Calling m_src->start_frame(..)"); lib::event *e = new framedone_callback(this, &ffmpeg_video_decoder_datasource::data_avail); m_src->start_frame(m_event_processor, e, ipts); m_start_input = false; } m_lock.leave();}bool ffmpeg_video_decoder_datasource::end_of_file(){ m_lock.enter(); if (_clip_end()) { m_lock.leave(); return true; } bool rv = _end_of_file(); m_lock.leave(); return rv;}bool ffmpeg_video_decoder_datasource::_end_of_file(){ // private method - no need to lock if (m_frames.size() > 0) { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::_end_of_file() returning false (still %d frames in local buffer)", m_frames.size()); return false; } return m_src == NULL || m_src->end_of_file();}bool ffmpeg_video_decoder_datasource::_clip_end(){ return false;}bool ffmpeg_video_decoder_datasource::buffer_full(){ m_lock.enter(); bool rv = _buffer_full(); m_lock.leave(); return rv;} bool ffmpeg_video_decoder_datasource::_buffer_full(){ // private method - no need to lock AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::_buffer_full() (this=0x%x, count=%d)", (void*) this, m_frames.size()); bool rv = (m_frames.size() > MAX_VIDEO_FRAMES); return rv;} char* ffmpeg_video_decoder_datasource::get_frame(timestamp_t now, timestamp_t *timestamp_p, int *size_p){ // pop frames until (just before) "now". Then return the last frame popped. m_lock.enter(); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::get_frame(now=%lld)\n", now); // XXX now can be negative, due to time manipulation by the scheduler. assert(now >= 0); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::get_frame() %d frames available\n", m_frames.size()); assert(m_frames.size() > 0 || _end_of_file()); timestamp_t frame_duration = frameduration(); assert (frame_duration > 0); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::get_frame: timestamp=%lld, now=%lld, frameduration = %lld",m_old_frame.first,now, frame_duration); AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::get_frame(now=%lld): %lld (m_old_frame.first) < %lld (now - frame_duration)", now, m_old_frame.first, now - frame_duration );#if 1 // XXX Jack thinks it may be better not to do any framedropping here, and in stead do it only in the // renderer (where we can gather statistics) bool firstdrop = true; while ( m_frames.size() && m_old_frame.first < now - (2*frame_duration)) { //HACK:Due to jitter, the previous condition of dropping frames older than one frameduration was too strict! //A better method to tolerate jitter required ??? This hack may still fail for high fps videos AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::get_frame: discarding m_old_frame timestamp=%lld, now=%lld, data ptr = 0x%x", m_old_frame.first,now, m_old_frame.second); _pop_top_frame(); if (!firstdrop) m_dropped_count++; firstdrop = false; }#endif AM_DBG if (m_frames.size()) lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::get_frame: next timestamp=%lld, now=%lld", m_frames.top().first, now); // The next assert assures that we have indeed removed all old frames (and, therefore, either there // are no frames left, or the first frame has a time that is in the future). It also assures that // the frames in m_frames are indeed in the right order.#if 0 if (!(m_frames.size() == 0 || m_frames.top().first >= now-frame_duration)) { lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::get_frame: top frame before now!"); lib::logger::get_logger()->debug("now-frameduration = %lld-%lld = %lld", now, frame_duration, now-frame_duration); lib::logger::get_logger()->debug("m_old_frame.first = %lld", m_old_frame.first); lib::logger::get_logger()->debug("m_frames.top().first = %lld", m_frames.top().first); lib::logger::get_logger()->debug("go figure..."); }#endif assert(m_frames.size() == 0 || m_frames.top().first >= now-(2*frame_duration)); if (timestamp_p) *timestamp_p = m_old_frame.first; if (size_p) *size_p = m_size; char *rv = m_old_frame.second; m_lock.leave(); return rv;}common::durationffmpeg_video_decoder_datasource::get_dur(){ if( m_src == 0) return common::duration(0, true); return m_src->get_dur();}bool ffmpeg_video_decoder_datasource::_select_decoder(const char* file_ext){ // private method - no need to lock AVCodec *codec = avcodec_find_decoder_by_name(file_ext); if (codec == NULL) { lib::logger::get_logger()->trace("ffmpeg_video_decoder_datasource._select_decoder: Failed to find codec for \"%s\"", file_ext); lib::logger::get_logger()->error(gettext("No support for \"%s\" video"), file_ext); return false; } m_con = avcodec_alloc_context(); if(avcodec_open(m_con,codec) < 0) { lib::logger::get_logger()->trace("ffmpeg_video_decoder_datasource._select_decoder: Failed to open avcodec for \"%s\"", file_ext); lib::logger::get_logger()->error(gettext("No support for \"%s\" video"), file_ext); return false; } return true;}bool ffmpeg_video_decoder_datasource::_select_decoder(video_format &fmt){ // private method - no need to lock if (fmt.name == "ffmpeg") { AVCodecContext *enc = (AVCodecContext *)fmt.parameters; m_con = enc; if (enc == NULL) { lib::logger::get_logger()->debug("Internal error: ffmpeg_video_decoder_datasource._select_decoder: Parameters missing for %s(0x%x)", fmt.name.c_str(), fmt.parameters); lib::logger::get_logger()->warn(gettext("Programmer error encountered during video playback")); return false; } if (enc->codec_type != CODEC_TYPE_VIDEO) { lib::logger::get_logger()->debug("Internal error: ffmpeg_video_decoder_datasource._select_decoder: Non-video stream for %s(0x%x)", fmt.name.c_str(), enc->codec_type); lib::logger::get_logger()->warn(gettext("Programmer error encountered during video playback")); return false; } AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource._select_decoder: enc->codec_id = 0x%x", enc->codec_id); AVCodec *codec = avcodec_find_decoder(enc->codec_id); if (codec == NULL) { lib::logger::get_logger()->debug("Internal error: ffmpeg_video_decoder_datasource._select_decoder: Failed to find codec for %s(0x%x)", fmt.name.c_str(), enc->codec_id); lib::logger::get_logger()->warn(gettext("Programmer error encountered during video playback")); return false; } //m_con = avcodec_alloc_context(); if(avcodec_open(m_con,codec) < 0) { lib::logger::get_logger()->debug("Internal error: ffmpeg_video_decoder_datasource._select_decoder: Failed to open avcodec for %s(0x%x)", fmt.name.c_str(), enc->codec_id); lib::logger::get_logger()->warn(gettext("Programmer error encountered during video playback")); return false; } if (fmt.width == 0) fmt.width = m_con->width; if (fmt.height == 0) fmt.width = m_con->height; return true; } else if (fmt.name == "live") { const char* codec_name = (char*) fmt.parameters; AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::selectdecoder(): video codec : %s", codec_name); ffmpeg_codec_id* codecid = ffmpeg_codec_id::instance(); AVCodec *codec = avcodec_find_decoder(codecid->get_codec_id(codec_name)); if( !codec) { return false; } AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::selectdecoder(): codec found!"); m_con = avcodec_alloc_context(); if((avcodec_open(m_con,codec) < 0) ) { //lib::logger::get_logger()->error(gettext("%s: Cannot open video codec %d(%s)"), repr(url).c_str(), m_con->codec_id, m_con->codec_name); return false; } else { AM_DBG lib::logger::get_logger()->debug("ffmpeg_video_decoder_datasource::ffmpeg_decoder_datasource(): succesfully opened codec"); } m_con->codec_type = CODEC_TYPE_VIDEO; // We doe a fmt update here to sure that we have the correct values. _need_fmt_uptodate(); return true; } // Could add support here for raw mp3, etc. return false;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -