📄 linux下的tv播放器.txt
字号:
&f_frameFinished, f_packet.data,
f_packet.size);
// Did we get a video frame?
if (f_frameFinished)
{
/*
* 这里不得不用中文说明一些东西:之所以要从原始格式转换成YUV420P,是因为这是一个最通用的格式,
* 无论你是要显示还是要做转换,还是进行压缩,这都是一个中转的格式,所以,这里一定要转换成YUV420P格式;
* 经过测试,这里读取出来的视频帧正是YUV420P格式,因此目前先去除这一步,以后在进行完善;
*/
/* Convert the image from its native format to YUV420P, and
* transmit its address to other threads to display or record;
*/
// img_convert ((AVPicture *) pm_frame_YUV420P, \
PIX_FMT_YUV420P, \
(AVPicture *) pm_frame, \
pm_codec_context->pix_fmt, \
pm_codec_context->width, \
pm_codec_context->height);
/*
* 从buffer中读取指定m_video_buffer_index的buffer节点中的帧,如果帧为空,则为其分配对应的空间,
* 然后将前面转换过的帧直接copy到这个临时帧pf_frameYUV420P上,然后保存此帧的指针地址到buffer中;
* 这里有必要说明的一点是:为什么要采取copy的方式,主要是从整体性能的角度考虑,
* 另外:这里的缓冲节点的读写也都是尽量使用指针读写,以避免大量的内存操作,而影响整体性能;
*/
if ( video_buffer_read(pm_tv->pm_video_buffer, \
m_video_buffer_index, &pf_frameYUV420P) == 0 ){
// read one buffer from video buffers queue;
if ( pf_frameYUV420P == NULL ){
// if the buffer node is NULL, then allocate memory to it;
// Determine required buffer size and allocate buffer
pf_frameYUV420P = avcodec_alloc_frame ();
// allocate picture memory
f_numBytes = avpicture_get_size (PIX_FMT_YUV420P, pm_codec_context->width, \
pm_codec_context->height);
pm_buffer = new uint8_t[f_numBytes];
// Assign appropriate parts of buffer to image planes in pm_frame_YUV420P
int rtbytes = avpicture_fill ((AVPicture *) pf_frameYUV420P, pm_buffer, \
PIX_FMT_YUV420P, \
pm_codec_context->width, pm_codec_context->height);
// then add pf_frameYUV420P into the buffer with index m_video_buffer_index;
video_buffer_write(pm_tv->pm_video_buffer, m_video_buffer_index, \
pf_frameYUV420P);
}// if null then allocate memory;
}
// lock();
thread_wrlock(pm_tv, m_video_buffer_index);
if ( pm_tv->m_deinterlace ){
// deinterlace the frame;
if (avpicture_deinterlace((AVPicture *)pf_frameYUV420P, \
(AVPicture *)pm_frame_YUV420P, PIX_FMT_YUV420P, \
pm_codec_context->width, pm_codec_context->height) < 0){
cerr << "avpicture_deinterlace error" << endl;
}
}
else{
// copy src frame to dst frame buffer;
img_copy((AVPicture *)pf_frameYUV420P, (AVPicture *)pm_frame_YUV420P, \
PIX_FMT_YUV420P, pm_codec_context->width, pm_codec_context->height);
}
thread_unlock(pm_tv, m_video_buffer_index);
// adjust the index of current frame become next in buffer;
m_video_buffer_index ++;
if ( m_video_buffer_index >= m_video_buffer_size ){
m_video_buffer_index = 0;
}
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet (&f_packet);
}
// Free the YUV image
av_free (pm_frame_YUV420P);
// Free the YUV frame
av_free (pm_frame);
}
注意:
这里有两点需要着重提一下:
一、是这里的视频帧队列中存储的视频帧格式均为YUV420P格式的,这样做有两点好处,第一,此格式的视频帧可以进行对其他格式的转换工作;第二,此格式如若能够直接显示,则能够减少从YUV到RGB的转换这一步,这是对提高电视的播放性能起到很关键的一步;
二、采用avpicture_deinterlace进行deinterlace处理,去除运动图象锯齿效果,提高视频显示效果;
视频帧缓冲队列
采用视频帧队列的最初目标或目的是为了提高在电视录制时的电视播放性能,电视播放是对视频帧队列的读取操作,视频帧的抓取则是对视频帧队列的写操作,而电 视录制则也是对视频帧队列的读操作,如果视频帧队列长度为1,也就等价于没有使用视频帧队列,此时,由于读写冲突,可能会出现卡帧的现象,为了避免出现这 种现象,才想到使用视频帧队列的;然而,天不遂人愿啊,目前情况下,视频帧队列长度为1时,录制效果是最好的,当增加长度为4以上时,录制的视频就会出现 重复录制某些视频帧的问题了;这个问题,我目前也还没有想到解决的方法;估计先要对录制原理进行了解过之后在进行改动,才能够解决的了;所以,我想,如果 能够控制好,先不要使用视频帧队列了,就采用一个视频帧作为缓冲即可,但要用加锁机制控制好读写冲突,否则也会出现意想不到的bug的。
显示视频帧线程
视频帧采集采用线程实现的原因也是为了能够提高GUI界面的响应速度,这一点我想大家也都是毋庸置疑的了;)
详细描述:
此线程主要完成从视频帧队列中读取视频帧,并显示出来的功能;
此线程同上也是使用类的实现方式,从QWidget继承而来,采用类的实现方式来实现线程操作;具体仍然是重点介绍run方法的实现方式;
Void LTvWindow::run(){
//
while(!m_tv_stopping){
// while tv don’t stop playing;
pm_frameYUV = get_frame_with_index (pm_tv, f_display_start_index);
while ( pm_frameYUV == NULL ){
//
pm_frameYUV = get_frame_with_index (pm_tv, f_display_start_index);
}
// 循环执行update的目的就是调用paintEvent函数显示视频帧;
update();
// ready to display next frame in the next buffer with index's increment;
f_display_start_index++;
if (f_display_start_index == pm_tv->m_video_buffer_size){
f_display_start_index = 0;
}
}// end while;
}
//
void LTvWindow::paintEvent (QPaintEvent * pv_event)
{
setWFlags(getWFlags() | Qt::WRepaintNoErase);
// 调用xv_display_one_frame显示视频帧;
xv_display_one_frame();
}
//
void LTvWindow::xv_display_one_frame(){
//
int f_src_x = 0;
int f_src_y = 0;
int f_dest_x = 0;
int f_dest_y = 0;
int f_rt_value = 0;
//
if ( m_fs_flag ){
// full screen;
if ( pm_frame_image != NULL ){
// Using Xv to display frames;
f_rt_value = XvPutImage(pm_display, m_port_id, this->winId(), \
*pm_gc, pm_frame_image, \
f_src_x, f_src_y, pm_tv->m_width, pm_tv->m_height, \
f_dest_x, f_dest_y, DSCREEN_FS_WIDTH, DSCREEN_FS_HEIGHT);
// fprintf(stderr, "xvputimage\n");
}
}else{
if ( pm_frame_image != NULL ){
// Using Xv to display frames;
f_rt_value = XvPutImage(pm_display, m_port_id, this->winId(), \
*pm_gc, pm_frame_image, \
f_src_x, f_src_y, pm_tv->m_width, pm_tv->m_height, \
f_dest_x, f_dest_y, pm_tv->m_width, pm_tv->m_height);
}
}
}
这里需要说明的地方比较多,最关键的也就是最后面的这个函数,显示视频帧有多种方法,我实现了两种,其中以上述方法最优,也就是采用Qt+Xv的方法;还 实现了一种是采用将视频帧转换为RGB视频帧之后,利用bitBle方法将QImage刷到屏幕上实现播放,这种方法,对于不经过缩放处理的的视频帧还可 以正常播放,一旦加上缩放操作,明显速度就跟不上了,视频帧刷新明显慢下来了;
上述方法就是采用的Qt+Xv来实习显示视频帧,并且能够实现视频帧全屏播放,而且速度很快;似乎还有更快的方法--XvShmPutImage,我没有试过,估计更快;)
对于ALSA版本号在1.0.9以下的,我们可以通过增加一个配置文件来达到使得alsa具有混音功能的目的;配置文件名是 /etc/asound.conf,文件内容为:
pcm.!default { type plug slave.pcm "dmixer" }pcm.dsp0 { type plug slave.pcm "dmixer" }pcm.dmixer { type dmix ipc_key 1024 slave { pcm "hw:0,0"# period_time 0# period_size 1024# buffer_size 8192 rate 44100 } bindings { 0 0 1 1 }}ctl.dmixer { type hw card 0 }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -