static SDL_VoutOverlay *func_create_overlay_l(int width, int height, int frame_format, SDL_Vout *vout) { switch (frame_format) { case IJK_AV_PIX_FMT__ANDROID_MEDIACODEC: return SDL_VoutAMediaCodec_CreateOverlay(width, height, vout); default: return SDL_VoutFFmpeg_CreateOverlay(width, height, frame_format, vout); } }
H:\code\ijkplayer-android\ijkmedia\ijkplayer\android\pipeline\ffpipeline_android.c
static IJKFF_Pipenode *func_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp) { IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; IJKFF_Pipenode *node = NULL;
// 优先采用硬解码 if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2) node = ffpipenode_create_video_decoder_from_android_mediacodec(ffp, pipeline, opaque->weak_vout); if (!node) { node = ffpipenode_create_video_decoder_from_ffplay(ffp); } return node; }
struct IJKFF_Pipenode_Opaque {
FFPlayer *ffp;
};
IJKFF_Pipenode *ffpipenode_create_video_decoder_from_ffplay(FFPlayer *ffp) { IJKFF_Pipenode *node = ffpipenode_alloc(sizeof(IJKFF_Pipenode_Opaque)); if (!node) return node; IJKFF_Pipenode_Opaque *opaque = node->opaque; opaque->ffp = ffp; node->func_destroy = func_destroy; node->func_run_sync = func_run_sync; ffp_set_video_codec_info(ffp, AVCODEC_MODULE_NAME, avcodec_get_name(ffp->is->viddec.avctx->codec_id)); ffp->stat.vdec_type = FFP_PROPV_DECODER_AVCODEC; return node; }
static int func_run_sync(IJKFF_Pipenode *node)
{
IJKFF_Pipenode_Opaque *opaque = node->opaque;
return ffp_video_thread(opaque->ffp);
}
int ffp_video_thread(FFPlayer *ffp)
{
return ffplay_video_thread(ffp);
}
static int ffplay_video_thread(void *arg) 视频解码线程
static int ffplay_video_thread(void *arg) { FFPlayer *ffp = arg; VideoState *is = ffp->is; AVFrame *frame = av_frame_alloc(); double pts; double duration; int ret; AVRational tb = is->video_st->time_base; AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL); #if CONFIG_AVFILTER AVFilterGraph *graph = avfilter_graph_alloc(); AVFilterContext *filt_out = NULL, *filt_in = NULL; int last_w = 0; int last_h = 0; enum AVPixelFormat last_format = -2; int last_serial = -1; int last_vfilter_idx = 0; if (!graph) { av_frame_free(&frame); return AVERROR(ENOMEM); } #else ffp_notify_msg2(ffp, FFP_MSG_VIDEO_ROTATION_CHANGED, ffp_get_video_rotate_degrees(ffp)); #endif if (!frame) { #if CONFIG_AVFILTER avfilter_graph_free(&graph); #endif return AVERROR(ENOMEM); } for (;;) { ret = get_video_frame(ffp, frame); if (ret < 0) goto the_end; if (!ret) continue; #if CONFIG_AVFILTER if ( last_w != frame->width || last_h != frame->height || last_format != frame->format || last_serial != is->viddec.pkt_serial || ffp->vf_changed || last_vfilter_idx != is->vfilter_idx) { SDL_LockMutex(ffp->vf_mutex); ffp->vf_changed = 0; av_log(NULL, AV_LOG_DEBUG, "Video frame changed from size:%dx%d format:%s serial:%d to size:%dx%d format:%s serial:%d\n", last_w, last_h, (const char *)av_x_if_null(av_get_pix_fmt_name(last_format), "none"), last_serial, frame->width, frame->height, (const char *)av_x_if_null(av_get_pix_fmt_name(frame->format), "none"), is->viddec.pkt_serial); avfilter_graph_free(&graph); graph = avfilter_graph_alloc(); if ((ret = configure_video_filters(ffp, graph, is, ffp->vfilters_list ? ffp->vfilters_list[is->vfilter_idx] : NULL, frame)) < 0) { // FIXME: post error SDL_UnlockMutex(ffp->vf_mutex); goto the_end; } filt_in = is->in_video_filter; filt_out = is->out_video_filter; last_w = frame->width; last_h = frame->height; last_format = frame->format; last_serial = is->viddec.pkt_serial; last_vfilter_idx = is->vfilter_idx; frame_rate = av_buffersink_get_frame_rate(filt_out); SDL_UnlockMutex(ffp->vf_mutex); } ret = av_buffersrc_add_frame(filt_in, frame); if (ret < 0) goto the_end; while (ret >= 0) { is->frame_last_returned_time = av_gettime_relative() / 1000000.0; ret = av_buffersink_get_frame_flags(filt_out, frame, 0); if (ret < 0) { if (ret == AVERROR_EOF) is->viddec.finished = is->viddec.pkt_serial; ret = 0; break; } is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time; if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0) is->frame_last_filter_delay = 0; tb = av_buffersink_get_time_base(filt_out); #endif duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); ret = queue_picture(ffp, frame, pts, duration, av_frame_get_pkt_pos(frame), is->viddec.pkt_serial); av_frame_unref(frame); #if CONFIG_AVFILTER } #endif if (ret < 0) goto the_end; } the_end: #if CONFIG_AVFILTER avfilter_graph_free(&graph); #endif av_frame_free(&frame); return 0; }
queue_picture 会间接调用 func_create_overlay_l创建 SDL_VoutOverlay
static int queue_picture(FFPlayer *ffp, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial) { VideoState *is = ffp->is; Frame *vp; int video_accurate_seek_fail = 0; int64_t video_seek_pos = 0; if (ffp->enable_accurate_seek && is->video_accurate_seek_req && !is->seek_req) { if (!isnan(pts)) { video_seek_pos = is->seek_pos; if (pts * 1000 * 1000 < is->seek_pos) {
//当前帧的pts 小于要定位到的 pos,丢掉这一帧 if (is->drop_vframe_count == 0) { av_log(NULL, AV_LOG_INFO, "video accurate_seek start, is->seek_pos=%lld, pts=%lf\n", is->seek_pos, pts); }
// 增加丢帧的数目 is->drop_vframe_count++; if (is->drop_vframe_count < MAX_KEY_FRAME_INTERVAL) { return 1; // drop some old frame when do accurate seek } else { av_log(NULL, AV_LOG_WARNING, "video accurate_seek is error, is->drop_vframe_count=%d\n", is->drop_vframe_count); video_accurate_seek_fail = 1; // if KEY_FRAME interval too big, disable accurate seek } } else { av_log(NULL, AV_LOG_INFO, "video accurate_seek is ok, is->drop_vframe_count =%d, is->seek_pos=%lld, pts=%lf\n", is->drop_vframe_count, is->seek_pos, pts); if (video_seek_pos == is->seek_pos) { is->drop_vframe_count = 0; SDL_LockMutex(is->accurate_seek_mutex); is->video_accurate_seek_req = 0;
// 通知进行音频 seek SDL_CondSignal(is->audio_accurate_seek_cond); if (video_seek_pos == is->seek_pos && is->audio_accurate_seek_req && !is->abort_request)
// 在 video_accurate_seek_cond 上 等待音频seek完成 SDL_CondWait(is->video_accurate_seek_cond, is->accurate_seek_mutex); ffp_notify_msg2(ffp, FFP_MSG_ACCURATE_SEEK_COMPLETE, (int)(pts * 1000)); if (video_seek_pos != is->seek_pos && !is->abort_request) { is->video_accurate_seek_req = 1; SDL_UnlockMutex(is->accurate_seek_mutex); return 1; } SDL_UnlockMutex(is->accurate_seek_mutex); } } } else { video_accurate_seek_fail = 1; } if (video_accurate_seek_fail) { if (!isnan(pts)) { ffp_notify_msg2(ffp, FFP_MSG_ACCURATE_SEEK_COMPLETE, (int)(pts * 1000)); } ffp->enable_accurate_seek = 0; is->drop_vframe_count = 0; SDL_LockMutex(is->accurate_seek_mutex); is->video_accurate_seek_req = 0; SDL_CondSignal(is->audio_accurate_seek_cond); SDL_UnlockMutex(is->accurate_seek_mutex); } } #if defined(DEBUG_SYNC) printf("frame_type=%c pts=%0.3f\n", av_get_picture_type_char(src_frame->pict_type), pts); #endif //从 循环队列中取得一个Frame类型的指针,如果没有空位,会阻塞等待 if (!(vp = frame_queue_peek_writable(&is->pictq))) return -1; vp->sar = src_frame->sample_aspect_ratio; #ifdef FFP_MERGE vp->uploaded = 0; #endif /* alloc or resize hardware picture buffer */
// vp->allocated 是 SDL_VoutOverlay是否已分配的标志
// 在alloc_picture函数里设置这个标志. if (!vp->bmp || !vp->allocated || vp->width != src_frame->width || vp->height != src_frame->height || vp->format != src_frame->format) { if (vp->width != src_frame->width || vp->height != src_frame->height) ffp_notify_msg3(ffp, FFP_MSG_VIDEO_SIZE_CHANGED, src_frame->width, src_frame->height); vp->allocated = 0; vp->width = src_frame->width; vp->height = src_frame->height; vp->format = src_frame->format; /* the allocation must be done in the main thread to avoid locking problems. */
// 分配一个 SDL_VoutOverlay 对象
alloc_picture(ffp, src_frame->format); if (is->videoq.abort_request) return -1; } /* if the frame is not skipped, then display it */ if (vp->bmp) { /* get a pointer on the bitmap */ SDL_VoutLockYUVOverlay(vp->bmp); #ifdef FFP_MERGE #if CONFIG_AVFILTER // FIXME use direct rendering av_image_copy(data, linesize, (const uint8_t **)src_frame->data, src_frame->linesize, src_frame->format, vp->width, vp->height); #else // sws_getCachedContext(...); #endif #endif // FIXME: set swscale options if (SDL_VoutFillFrameYUVOverlay(vp->bmp, src_frame) < 0) { av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n"); exit(1); } /* update the bitmap content */ SDL_VoutUnlockYUVOverlay(vp->bmp); vp->pts = pts; vp->duration = duration; vp->pos = pos; vp->serial = serial; vp->sar = src_frame->sample_aspect_ratio; vp->bmp->sar_num = vp->sar.num; vp->bmp->sar_den = vp->sar.den; #ifdef FFP_MERGE av_frame_move_ref(vp->frame, src_frame); #endif
// 移动环形队列的写指针 frame_queue_push(&is->pictq); if (!is->viddec.first_frame_decoded) { ALOGD("Video: first frame decoded\n"); is->viddec.first_frame_decoded_time = SDL_GetTickHR(); is->viddec.first_frame_decoded = 1; } } return 0; }
/* allocate a picture (needs to do that in main thread to avoid potential locking problems */ static void alloc_picture(FFPlayer *ffp, int frame_format) { VideoState *is = ffp->is; Frame *vp; #ifdef FFP_MERGE int sdl_format; #endif vp = &is->pictq.queue[is->pictq.windex]; // 释放已有的SDL_VoutOverlay free_picture(vp); #ifdef FFP_MERGE video_open(is, vp); #endif
// 默认SDL_FCC_RV32
SDL_VoutSetOverlayFormat(ffp->vout, ffp->overlay_format); vp->bmp = SDL_Vout_CreateOverlay(vp->width, vp->height, frame_format, ffp->vout); #ifdef FFP_MERGE if (vp->format == AV_PIX_FMT_YUV420P) sdl_format = SDL_PIXELFORMAT_YV12; else sdl_format = SDL_PIXELFORMAT_ARGB8888; if (realloc_texture(&vp->bmp, sdl_format, vp->width, vp->height, SDL_BLENDMODE_NONE, 0) < 0) { #else /* RV16, RV32 contains only one plane */ if (!vp->bmp || (!vp->bmp->is_private && vp->bmp->pitches[0] < vp->width)) { #endif /* SDL allocates a buffer smaller than requested if the video * overlay hardware is unable to support the requested size. */ av_log(NULL, AV_LOG_FATAL, "Error: the video system does not support an image\n" "size of %dx%d pixels. Try using -lowres or -vf \"scale=w:h\"\n" "to reduce the image size.\n", vp->width, vp->height ); free_picture(vp); } SDL_LockMutex(is->pictq.mutex); vp->allocated = 1; SDL_CondSignal(is->pictq.cond); SDL_UnlockMutex(is->pictq.mutex); }
static int func_fill_frame(SDL_VoutOverlay *overlay, const AVFrame *frame) { assert(overlay); SDL_VoutOverlay_Opaque *opaque = overlay->opaque; AVFrame swscale_dst_pic = { { 0 } }; av_frame_unref(opaque->linked_frame); int need_swap_uv = 0; int use_linked_frame = 0; enum AVPixelFormat dst_format = AV_PIX_FMT_NONE; switch (overlay->format) { case SDL_FCC_YV12: need_swap_uv = 1; // no break; case SDL_FCC_I420: if (frame->format == AV_PIX_FMT_YUV420P || frame->format == AV_PIX_FMT_YUVJ420P) { // ALOGE("direct draw frame"); use_linked_frame = 1; dst_format = frame->format; } else { // ALOGE("copy draw frame"); dst_format = AV_PIX_FMT_YUV420P; } break; case SDL_FCC_I444P10LE: if (frame->format == AV_PIX_FMT_YUV444P10LE) { // ALOGE("direct draw frame"); use_linked_frame = 1; dst_format = frame->format; } else { // ALOGE("copy draw frame"); dst_format = AV_PIX_FMT_YUV444P10LE; } break; case SDL_FCC_RV32: dst_format = AV_PIX_FMT_0BGR32; break; case SDL_FCC_RV24: dst_format = AV_PIX_FMT_RGB24; break; case SDL_FCC_RV16: dst_format = AV_PIX_FMT_RGB565; break; default: ALOGE("SDL_VoutFFmpeg_ConvertPicture: unexpected overlay format %s(%d)", (char*)&overlay->format, overlay->format); return -1; } // setup frame if (use_linked_frame) { // linked frame av_frame_ref(opaque->linked_frame, frame); overlay_fill(overlay, opaque->linked_frame, opaque->planes); if (need_swap_uv) FFSWAP(Uint8*, overlay->pixels[1], overlay->pixels[2]); } else { // managed frame AVFrame* managed_frame = opaque_obtain_managed_frame_buffer(opaque); if (!managed_frame) { ALOGE("OOM in opaque_obtain_managed_frame_buffer"); return -1; } overlay_fill(overlay, opaque->managed_frame, opaque->planes); // setup frame managed for (int i = 0; i < overlay->planes; ++i) { swscale_dst_pic.data[i] = overlay->pixels[i]; swscale_dst_pic.linesize[i] = overlay->pitches[i]; } if (need_swap_uv) FFSWAP(Uint8*, swscale_dst_pic.data[1], swscale_dst_pic.data[2]); } // swscale / direct draw /* ALOGE("ijk_image_convert w=%d, h=%d, df=%d, dd=%d, dl=%d, sf=%d, sd=%d, sl=%d", (int)frame->width, (int)frame->height, (int)dst_format, (int)swscale_dst_pic.data[0], (int)swscale_dst_pic.linesize[0], (int)frame->format, (int)(const uint8_t**) frame->data, (int)frame->linesize); */ if (use_linked_frame) { // do nothing }
// use libyuv to convert yuv to rgb
else if (ijk_image_convert(frame->width, frame->height, dst_format, swscale_dst_pic.data, swscale_dst_pic.linesize, frame->format, (const uint8_t**) frame->data, frame->linesize)) { opaque->img_convert_ctx = sws_getCachedContext(opaque->img_convert_ctx, frame->width, frame->height, frame->format, frame->width, frame->height, dst_format, opaque->sws_flags, NULL, NULL, NULL); if (opaque->img_convert_ctx == NULL) { ALOGE("sws_getCachedContext failed"); return -1; } sws_scale(opaque->img_convert_ctx, (const uint8_t**) frame->data, frame->linesize, 0, frame->height, swscale_dst_pic.data, swscale_dst_pic.linesize); if (!opaque->no_neon_warned) { opaque->no_neon_warned = 1; ALOGE("non-neon image convert %s -> %s", av_get_pix_fmt_name(frame->format), av_get_pix_fmt_name(dst_format)); } } // TODO: 9 draw black if overlay is larger than screen return 0; }