首页 > 编程知识 正文

原生解码和ijk解码,potplayer视频解码器

时间:2023-05-05 22:18:49 阅读:115408 作者:3706

该系列是以下视频渲染过程音频播放过程read线程过程音频解码过程视频解码过程同步到视频音频的开始过程和缓冲策略

本文通过video_thread在以下流程图中展示了过程分析的第五篇分析ijkPlayer的视频解码过程。

IJKFF_Pipenode结构在ff_ffpipenode.h和ff_ffpipenode.c中定义

ffpipenode表示视频解码器封装了软解和硬解。

//ff _ ffpipenode.htypedefstructijkff _ pipe node _ opaqueijkff _ pipe node _ opaque; typedefstructijkff _ pipenodeijkff _ pipe node; structijkff _ pipe node { SDL _ mutex * mutex; void *opaque; void(func_destroy ) ) IJKFF_Pipenode *node ); int(func_run_sync ) ) IJKFF_Pipenode *node ); int(func_flush ) ) IJKFF_Pipenode *node ); //optional}; ijk ff _ pipe node * ffpipenode _ alloc (size _ t opaque _ size ); voidffpipenode _ free (ijk ff _ pipe node * node; voidffpipenode _ free _ p (ijk ff _ pipe node * * node ); intffpipenode _ run _ sync (ijk ff _ pipe node * node ); intffpipenode _ flush (ijk ff _ pipe node * node; pipenode初始化过程

在stream_component_open中调用pipeline.ff pipeline _ open _ video _ decoder创建

video_thread调用流视频帧的解码操作是video_thread线程中的video_thread从packet_queue读取视频包,然后进行软/硬解码

//ff _ ffpipenode.htypedefstructijkff _ pipenodeijkff _ pipe node; structijkff _ pipe node { SDL _ mutex * mutex; void *opaque; void(func_destroy ) ) IJKFF_Pipenode *node ); int(func_run_sync ) ) IJKFF_Pipenode *node ); int(func_flush ) ) IJKFF_Pipenode *node ); //optional}; //ff _ ffplay.cstaticintstream _ component _ open (ff player * FFP,int stream_index ) decoder_init(is-viddindex IJKFF_Pipenode创建和初始化解码器ffpipenode包括硬/软解码器FFP-node _ vdecffpipeline _ open _ video _ decoder (FFP-pipeline video_thread,ffp,ff_video_dec ) }/ff _ ff pipeline.cij kff _ pipe node * ff return pipeline-func _ open _ vipeline //ff pipeline _ Android.cstaticijkff _ pipe node * func _ open _ video _ decoder (ijk ff _ pipeline * pipeline,FP eler if (FFP-mediacodec _ all _ videos|| FFP-mediacodec _ AVC|| FFP-mediacodec _ hevc|)

diacodec(ffp, pipeline, opaque->weak_vout); if (!node) { // 硬解创建失败走软解 node ffpipenode_create_video_decoder_from_ffplay(ffp); } return node;}// ff_ffplay.cstatic int video_thread(void *arg) { ret ffpipenode_run_sync(ffp->node_vdec); // 调用ffpipenode_run_sync return ret;}// ff_ffpipenode.cint ffpipenode_run_sync(IJKFF_Pipenode *node) { return node->func_run_sync(node);} 视频帧解码流程

在video_thread中调用ffpipenode_run_sync就区分开了视频解码分为软解和硬解这里迅速过一下之后再详细分析。 软解的实现是ffpipenode_ffplay_vdec.c 硬解的实现是ffpipenode_android_mediacodec_vdec.c

软件流程会调到ff_ffplay.c#ffp_video_thread中 硬件流程就在ffpipenode_android_mediacodec_vdec.c文件中 这两种方式解码后都会会调到ff_ffplay.c#queue_picture进行入队等待渲染。

/* * 软解流程ffpipenode_ffplay_vdec.c */ static int func_run_sync(IJKFF_Pipenode *node){ IJKFF_Pipenode_Opaque *opaque node->opaque; return ffp_video_thread(opaque->ffp);}// ff_ffplay.cstatic int ffplay_video_thread(void *arg) { AVFrame *frame av_frame_alloc(); for(;;){ ret get_video_frame(ffp, frame); // avcodec_receive_frame软解码获取一帧 queue_picture(ffp, frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial); }}/* * 硬解ffpipenode_android_mediacodec_vdec.c */static int func_run_sync(IJKFF_Pipenode *node){ int got_frame 0; while (!q->abort_request) { drain_output_buffer(env, node, timeUs, &dequeue_count, frame, &got_frame); if (got_frame) { // 通过头文件调用到queue_picture ffp_queue_picture(ffp, frame, pts, duration, av_frame_get_pkt_pos(frame), is->viddec.pkt_serial); } }}// 入队 ff_ffplay.cstatic intqueue_picture(FFPlayer *ffp, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial) { Frame *vp; vp frame_queue_peek_writable(&is->pictq); // 获取一个可写节点 if (!vp->bmp){ alloc_picture(ffp, src_frame->format); // 创建bmp vp->allocated 0; vp->width src_frame->width; vp->height src_frame->height; vp->format src_frame->format; } if (vp->bmp) { SDL_VoutLockYUVOverlay(vp->bmp); // 锁 SDL_VoutFillFrameYUVOverlay(vp->bmp, src_frame); // 调用func_fill_frame把帧画面“绘制”到最终的显示图层上 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; frame_queue_push(&is->pictq); // 对节点操作结束后调用frame_queue_push告知FrameQueue“存入”该节点 } } 视频帧渲染流程

stream_open方法中创建了video_refresh_thread在该线程里从frame_queue读取视频帧进行音视频同步后进行渲染。 此处忽略音视频同步直接讲渲染流程。

CSDN站内私信我领取最新最全C音视频学习提升资料内容包括C/CLinux 服务器开发FFmpeg webRTC rtmp hls rtsp ffplay srs

static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat) { // 创建video_refresh_thread单独线程进行视频渲染 SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, ff_vout);}#define REFRESH_RATE 0.01static int video_refresh_thread(void *arg) { FFPlayer *ffp arg; VideoState *is ffp->is; double remaining_time 0.0; while (!is->abort_request) { if (remaining_time > 0.0) { // video_refresh里进行音视频同步更改remaining_time的值在此休息相应的时间 av_usleep((int) (int64_t) (remaining_time * 1000000.0)); } remaining_time REFRESH_RATE; if (is->show_mode ! SHOW_MODE_NONE && (!is->paused || is->force_refresh)) video_refresh(ffp, &remaining_time); } return 0;}static void video_refresh(FFPlayer *opaque, double *remaining_time) { if (!is->video_st) { return; } if (frame_queue_nb_remaining(&is->pictq) 0) { // 队列里没有帧do nothing } else { // ... 此处忽略音视频同步直接渲染下一帧 frame_queue_next(&is->pictq); // 移动到下一帧 is->force_refresh 1; } if (!ffp->display_disable && is->force_refresh && is->show_mode SHOW_MODE_VIDEO && is->pictq.rindex_shown) { video_display2(ffp); // 显示 }}static void video_display2(FFPlayer *ffp) { VideoState *is ffp->is; if (is->video_st) video_image_display2(ffp);}static void video_image_display2(FFPlayer *ffp) { Frame *vp frame_queue_peek_last(&is->pictq); // 就是要渲染的这一帧 if (vp->bmp) { // 进行渲染 SDL_VoutDisplayYUVOverlay(ffp->vout, vp->bmp); }}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。