http://blog.163.com/smilexiao_11015461/blog/static/212205218201452510149544/
最近在研究Rk3066的硬解,为了怕事后忘记,特记录下来。
有软解了为什么还选择硬解,主要因为软件太耗cpu,硬解刚好补这个缺陷;
一、先看spice server这边对视频的操作,spice server是把视频流打包成mjpeg,然后通过网络传到spice client;
Spice server:
在Red_worker.c-》red_marshall_stream_data》mjpeg_encoder_start_frame(agent->mjpeg_encoder, image->u.bitmap.format,width, height,&dcc->send_data.stream_outbuf,&outbuf_size,frame_mm_time);这里把frame进行mjpeg压缩,然后red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);发送到spice client;
Spice client:
Channel-display.c-》[ SPICE_MSG_DISPLAY_STREAM_DATA ]= display_handle_stream_data,所有视频数据都是在display_handle_stream_data进行处理的,然后调用display_stream_schedule(st),再到g_source_set_callback(timeout_source, (GSourceFunc)display_stream_render, (guint)st->id, NULL),回调到display_stream_render,在这里面先进行解码,然后在render。
case SPICE_VIDEO_CODEC_TYPE_MJPEG:
stream_mjpeg_data(st);
break;
}
我们这里主要讲解stream_mjpeg_data(st)这里面的工作;我们提供了两种解码方案:软解和硬解;
软解流程:
jpeg_read_header();//要对st->mjpeg_cinfo的成员进行赋值,网上有例子
jpeg_start_decompress();//开始解码
jpeg_read_scanlines();//一行行读取
还有memcpy();对解码后数据拷贝
jpeg_finish_decompress(&st->mjpeg_cinfo);//完成解码
硬解流程:
通过vpu实现硬解:
vpu_api_init(&mVpu_api, OMX_ON2_VIDEO_CodingMJPEG);//初始mjpeg解码器
decHandle = mVpu_api.get_class_On2Decoder();//获取解码器handler
mVpu_api.init_class_On2Decoder(decHandle);//初始
mVpu_api.dec_oneframe_class_On2Decoder(//开始硬解,src是数据,poutbut是硬解后的数据
decHandle, pOutBuf, &output_len, src, &src_size);
mVpu_api.deinit_class_On2Decoder(decHandle);//释放
mVpu_api.destroy_class_On2Decoder(decHandle);
由于mjpeg流默认格式是yuv420sp,屏幕render的格式ARGB32的,所以要对解码后的数据进行格式转换;网上有yuv420sp_to_rgba的算法实现,拷贝下来就行了;这里要注意的是:解码后的数据可能被放大了,所以传到yuv420sp_to_rgba的数据要是放大的数据,frame->displaywidth,frame->height,最后就是数据拷贝了,这地方最容易出错;
if(width == frame->DisplayWidth){
memcpy(dest,tempdest,width*height*4);
}else{ uint8_t* destp = dest;
uint8_t* srcp = (uint8_t*)tempdest;
for(int i=0;i memcpy(destp, srcp, width*4); destp += width * 4; srcp += frame->DisplayWidth * 4; } } 可悲的是,最后发现硬解的效果和软件的效果一样,cpu都要占用50%以上; 二、经过测试发现,spice client在不播视频的,只播放音频cpu都占用很高,于是又跟踪了channel-playback.c的代码:发现是音频解码库有问题libcelt051.a 在channel-playback.c里面channel_set_handlers()》[ SPICE_MSG_PLAYBACK_DATA ] = playback_handle_data,在playback_handle_data把数据丢到一个线程池处理g_thread_pool_push(g_audio_pool, (gpointer)in, NULL);g_audio_pool是在spice_playback_channel_init建立的,g_audio_pool = g_thread_pool_new(audio_decode, (gpointer)channel, 1, TRUE, NULL);所有的工作都是在audio_decode里面进行的; 在spice_playback_channel_init里面,spice_playback_channel_reset_capabilitiesspice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_PLAYBACK_CAP_CELT_0_5_1);client设置这个后,server每次下发的音频数据都会先通过libcelt051.a这个库进行数据压缩,然后下发。 在spice server:snd_worker.c里面有snd_set_playback_compression(),这里面就会判断client 是否有SPICE_PLAYBACK_CAP_CELT_0_5_1的能力,有就压缩数据,没有就发送源数据(这个比较大,更好服务器的流量); 在spice client:channel-playback.c:的audio_decode里面有对压缩方式判断,如果是SPICE_AUDIO_DATA_MODE_RAW,就直接给下层播放,如果是SPICE_AUDIO_DATA_MODE_CELT_0_5_1,就先celt051_decode,在给下层播放;