入冬前车辆安全检查:如何在native C显示YUV420格式图像?
来源:百度文库 编辑:偶看新闻 时间:2024/05/11 18:19:10
- 悠嘻猴
yinhe|设置|提醒|短消息|好友 |任务|退出
积分: 73 , e望: 2 点 , e币: 35 元 , e脉: 0 条 , 用户组: 敏而好学 Ⅱ
- 帖子
- 收藏
- 分享
- 切换到宽版
- .
- android技术
- android问答
- 优亿专题
- 优亿人才
- 优亿市场eoeMarket软件商店
- 开发者服务
- 开发者社区BBS
[多媒体] 如何在native C显示YUV420格式图像? [复制链接]
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
我目前通过转换为RGB565格式,通过本地C代码输出的。
问题是,格式转换耗时太长,导致视频很慢。
android支持YUV420的,请问如何实现?
请叙述实现思路,蕞好有可参考的代码?
收藏1 分享0 好评0 差评0 我工作,我高兴!
回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
点评回复 引用
举报 返回顶部
Lolingew
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-12-28
- 最后登录
- 2011-4-6
- 在线时间
- 32小时
业精于勤 Ⅲ
- UID
- 121409
- 精华
- 0
- 帖子
- 22
- e望
- 0 点
- e币
- 66 元
- 在线时间
- 32小时
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
但即使这样,Android2.2中,在surfaceflinger层,是支持YUV420的,camera输出大都是YUV啊。
不知道怎样才能实现。 我工作,我高兴!
点评回复 引用
举报 返回顶部
yntnfu
- 发短消息
- 加为好友
- 阅读权限
- 30
- 注册时间
- 2010-8-27
- 最后登录
- 2011-3-31
- 在线时间
- 235小时
励精图治 Ⅳ
- UID
- 88217
- 精华
- 0
- 帖子
- 115
- e望
- 2 点
- e币
- 310 元
- 在线时间
- 235小时
点评回复 引用
举报 返回顶部
qggmm
- 发短消息
- 加为好友
- 阅读权限
- 200
- 注册时间
- 2010-9-14
- 最后登录
- 2011-4-14
- 在线时间
- 255小时
帮帮团
- UID
- 92371
- 精华
- 0
- 帖子
- 400
- e望
- 0 点
- e币
- 594 元
- 在线时间
- 255小时
顶顶顶 支持eoe
点评回复 引用
举报 返回顶部
tq09931
- 发短消息
- 加为好友
- 阅读权限
- 10
- 注册时间
- 2010-3-27
- 最后登录
- 2011-3-10
- 在线时间
- 21小时
敏而好学 Ⅱ
- UID
- 24523
- 精华
- 0
- 帖子
- 15
- e望
- 0 点
- e币
- 37 元
- 在线时间
- 21小时
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
研究结果,我的三星平台,显示系统支持的是YUV420SP格式,这一类的Framebuffer显示代码一般是于平台有关,是厂商编写的。
谷歌的android,一般的显示格式支持只有RGB565了。
而ffmpeg解码出来,是YUV420P格式。
虽然也是YUV,但是一个是打包格式,一个是平面格式。
所以YUV420P只能是转换为RGB565格式了,差别在于如何高效转换。 我工作,我高兴!
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
#define kBufferCount 2
/*============================================================*/
/**************************************************************************************************/
/*********************************************************************************************/
namespace android {
class Test {
public:
static const sp
return s->getISurface();
}
};
};
sp
sp
sp
sp
ISurface::BufferHeap bufferheap;
size_t framebuffers[2];
u_int framebufferindex = 0;
/**************************************************************************************************/
/*********************************************************************************************/
int VideoDriver_register(void)
{
LOGI("%s[%d]", __FUNCTION__, __LINE__);
// set up the thread-pool
sp
ProcessState::self()->startThreadPool();
LOGI("%s[%d]", __FUNCTION__, __LINE__);
// create a client to surfaceflinger
client = new SurfaceComposerClient();
client->setOrientation(0, ISurfaceComposer::eOrientationDefault, 0);
// create pushbuffer surface
LOGV("Created SurfaceComposerClient\n");
surface = client->createSurface(getpid(), 0, 1024, 600, PIXEL_FORMAT_UNKNOWN, ISurfaceComposer::ePushBuffers);
LOGV("Created SurfaceControl\n");
client->openTransaction();
surface->setLayer(100000);
client->closeTransaction();
// get to the isurface
LOGI("get to the isurface");
isurface = Test::getISurface(surface);
LOGI("isurface = %p\n", isurface.get());
return 0;
}
/**************************************************************************************************/
/*********************************************************************************************/
int VideoDriver_sregister(int mWidth, int mHeight)
{
LOGI("%s[%d]", __FUNCTION__, __LINE__);
// YUV420 frames are 1.5 bytes/pixel
heap = new MemoryHeapBase(((mWidth * mHeight * 3) / 2) * kBufferCount);
if (heap->heapID() < 0)
{
LOGE("Error creating frame buffer heap");
client->setOrientation(0, ISurfaceComposer::eOrientationDefault, 0);
return -1;
}
bufferheap = ISurface::BufferHeap(mWidth, mHeight,
mWidth, mHeight, PIXEL_FORMAT_CUSTOM_YCbCr_420_SP, heap); //PIXEL_FORMAT_YCbCr_420_P
isurface->registerBuffers(bufferheap);
for (int i = 0; i < 2; i++)
{
framebuffers[i] = i * ((mWidth * mHeight * 3) / 2);
LOGV("Frame buffer [%d]: address 0x%08x, offset 0x%08x\n", i, bufferheap.heap->base(), framebuffers[i]);
}
return 0;
}
/**************************************************************************************************/
/*********************************************************************************************/
void VideoDriver_unregister(void)
{
LOGI("%s[%d]", __FUNCTION__, __LINE__);
client->setOrientation(0, ISurfaceComposer::eOrientationDefault, 0);
isurface->unregisterBuffers();
}
/**************************************************************************************************/
/*********************************************************************************************/
void VideoDriver_setOrientation(void)
{
client->setOrientation(0, ISurfaceComposer::eOrientationDefault, 0);
}
/**************************************************************************************************/
/*********************************************************************************************/
void VideoDriver_mfc_display(unsigned int YPhyAddr, unsigned int CPhyAddr)
{
memcpy(static_cast
memcpy(static_cast
isurface->postBuffer(framebuffers[framebufferindex]);
if (++framebufferindex == kBufferCount) framebufferindex = 0;
}
/**********************************************************************************************************************/
/******************************************---end---*******************************************************************/
/**********************************************************************************************************************/
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
《普通应用使用Surface的方法》
Android的NDK提供了Surface类,可供应用向屏幕绘图,但使用这个类,有很多机关,
如果不注意,代码就不会正常工作。
下面是我们使用的时候遇到的问题与解决方法。
1,在C++代码里要使用Surface类,必须在APK的工程文件Android.mk里增加权限
uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
如果没有这句,则创建Surface会失败。
2,创建Surface
//sp client;
client = new SurfaceComposerClient();
// 然后向 Surfaceflinger 申请一个 Surface , surface 类型为 PushBuffers
DisplayInfo dispInfo;
client->getDisplayInfo(0,&dispInfo);
int ok = client->initCheck();
// 创建Surface,颜色为32位;ePushBuffers表示buffer由我们提供
surfaceControl = client->createSurface(getpid(), 0, 800, 600,
PIXEL_FORMAT_RGBA_8888, ISurfaceComposer::ePushBuffers);
if (surfaceControl == NULL) {
return JNI_FALSE;
}
// 设置位置,大小以及Z-order
client->openTransaction();
surfaceControl->setPosition(0, 0);
surfaceControl->setSize(800, 600);
surfaceControl->setLayer(99999);
surfaceControl->show();
client->closeTransaction();
3,获取ISurface对象
// 这一句需要权限,要在surface.h里加入frends class
isurface = Test::getISurface(surfaceControl);
这里使用了一个技巧,具体参见“欺骗编译器取得ISurface对象的方法”
4,注册buffer
static const char* pmem = "/dev/pmem";
// 这一句需要root权限
heap = new MemoryHeapBase(pmem, 800 * 600 * 4);
if (heap->heapID() < 0) {
return JNI_FALSE;
}
sp pmemHeap = new MemoryHeapPmem(heap, 0);
if (pmemHeap->getHeapID() >= 0) {
// pmemHeap->slap();
// heap.clear();
// pmemHeap.clear();
return JNI_FALSE;
}
mBufferHeap = ISurface::BufferHeap(800, 600,
800, 600, PIXEL_FORMAT_BGRA_8888, pmemHeap);
int ret = isurface->registerBuffers(mBufferHeap);
这里,因为要打开/dev/pmem设备文件,因此需要有权限,方法很简单,就是root手机,
然后使用命令
chmod a+rw /dev/pmem
来增加权限。
用完后记得复原,否则可能有安全风险。
在代码里修改权限参见“调用Su命令修改设备文件权限的Java代码”。
5,得到buffer指针
char * bp = static_cast(mBufferHeap.heap->base());
6,向buffer写位图数据,注意要是32位的
具体写的代码请参见“向Surface写位图数据的方法”
7,提交修改,通知Surface Flinger更新屏幕
isurface->postBuffer(0);
这样屏幕上就可以看到显示了。
我工作,我高兴!
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
对于多媒体视频,越来越倾向于高清,高分辨率了。
软件解码已经力不从心,尽量利用平台的硬件解码,而且同一平台的显示系统和多媒体解码器的输出格式是兼容的。
软件解码只能解决格式支持与否的问题,反正已经是速度慢了,多一步格式转换也无所谓了! 我工作,我高兴!
点评回复 引用
举报 返回顶部
zhouxiangang
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-9-7
- 最后登录
- 2011-4-15
- 在线时间
- 67小时
业精于勤 Ⅲ
- UID
- 90690
- 精华
- 0
- 帖子
- 5
- e望
- 0 点
- e币
- 59 元
- 在线时间
- 67小时
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
package com.lxglxt.mengPlayer;
import android.content.Context;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.MediaController;
import android.widget.MediaController.MediaPlayerControl;
public class mengSurfaceView extends SurfaceView implements SurfaceHolder.Callback
{
private static final String TAG = "mengSurfaceView";
private static final String mylibname = "mengJniNative";
public native int doVideoRegister(Surface surf);
public native void doVideoUnregister();
public native int doPlayer_setDataSource(String pathname);
public native int doPlayer_prepare();
public native int doPlayer_start();
public native void doPlayer_exit();
public native int doPlayer_main(String path);
public native void doPlayer_demoVideo();
public native void doPlayer_setFill(int flag);
public SurfaceHolder mholder;
public Surface mSurface;
private Context mContext;
private MediaController mMediaController;
private Thread mThread;
static {
try{
Log.d(TAG, "now load lib " + mylibname);
System.loadLibrary(mylibname);
}catch(UnsatisfiedLinkError e){
Log.d(TAG, "Couldn't load lib " + mylibname + e.getMessage());
}
}
public mengSurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub
mholder = this.getHolder();//获取holder
mholder.addCallback(this);
mContext = context;
attachMediaController();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
Log.d(TAG,"surfaceChanged");
mSurface = holder.getSurface();
doVideoRegister(mSurface);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.d(TAG,"surfaceCreated");
//here test draw
/* Canvas canvas = mholder.lockCanvas(null);//获取画布
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
canvas.drawRect(new RectF(0,0,320,240), mPaint);
mholder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
*/
// new Thread(new MyThread()).start();
mThread = new Thread(new MyThread());
mThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.d(TAG,"surfaceDestroyed");
doVideoUnregister();
doPlayer_exit();
// mThread.destroy();
}
public boolean onTouchEvent(android.view.MotionEvent event) {
Log.d(TAG,"onTouchEvent");
if(!mMediaController.isShowing()) {
mMediaController.show(3000);
}
return true;
}
private void attachMediaController() {
mMediaController = new MediaController(mContext);
View anchorView = this.getParent() instanceof View ?
(View)this.getParent() : this;
mMediaController.setMediaPlayer(mMediaPlayerControl);
mMediaController.setAnchorView(anchorView);
mMediaController.setEnabled(true);
}
MediaPlayerControl mMediaPlayerControl = new MediaPlayerControl() {
@Override
public boolean canPause() {
// TODO Auto-generated method stub
Log.d(TAG,"canPause, now is null");
return false;
}
@Override
public boolean canSeekBackward() {
// TODO Auto-generated method stub
Log.d(TAG,"canSeekBackward, now is null");
return false;
}
@Override
public boolean canSeekForward() {
// TODO Auto-generated method stub
Log.d(TAG,"canSeekForward, now is null");
return false;
}
@Override
public int getBufferPercentage() {
// TODO Auto-generated method stub
Log.d(TAG,"getBufferPercentage, now is null");
return 0;
}
@Override
public int getCurrentPosition() {
// TODO Auto-generated method stub
Log.d(TAG,"getCurrentPosition, now is null");
return 0;
}
@Override
public int getDuration() {
// TODO Auto-generated method stub
Log.d(TAG,"getDuration, now is null");
return 0;
}
@Override
public boolean isPlaying() {
// TODO Auto-generated method stub
Log.d(TAG,"isPlaying, now is null");
return false;
}
@Override
public void pause() {
// TODO Auto-generated method stub
Log.d(TAG,"pause, now is null");
}
@Override
public void seekTo(int pos) {
// TODO Auto-generated method stub
Log.d(TAG,"seekTo, now is null");
}
@Override
public void start() {
// TODO Auto-generated method stub
Log.d(TAG,"start, now is null");
}
};
class MyThread implements Runnable{
@Override
public void run() {
Log.d(TAG,"MyThread");
try {
Thread.sleep(400);
doPlayer_setFill(1);
// doPlayer_demoVideo();
doPlayer_prepare();
doPlayer_start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// doDrawSurface();
}
}
public void setVideoPath(String filePath) {
doPlayer_setDataSource(filePath);
}
}
我工作,我高兴!
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
java部分,在surfaceChanged中,把句柄传递给了C代码。就完成了所有工作,剩下的都是C部分的工作了。 我工作,我高兴!
点评回复 引用
举报 返回顶部
lxglxt
- 发短消息
- 加为好友
- 阅读权限
- 20
- 注册时间
- 2010-4-26
- 最后登录
- 2011-4-14
- 在线时间
- 62小时
业精于勤 Ⅲ
- UID
- 28462
- 精华
- 0
- 帖子
- 28
- e望
- 0 点
- e币
- 89 元
- 在线时间
- 62小时
VideoDriver_getPixels();
VideoDriver_lockSurface();
VideoDriver_unLockSurface();
其中VideoDriver_getPixels得到显示buffer的指针,调用avpicture_fill()将frame和这个buffer联系起来。
这样,对buffer的操作就是直接操作显示buffer了。
每一frame之前要lockSurface。
调用sws_scale()格式转换直接将输出送到了显示buffer里面了。
完成frame后,unLockSurface就可以了。
int MediaPlayer_prepareVideo(void)
{
int i;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
LOGI("prepareVideo");
// Find the first video stream
gmVideoStreamIndex=-1;
for(i=0; i
{
if(gpMovieFile->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO && gmVideoStreamIndex<0)
{
gmVideoStreamIndex=i;
}
}
if(gmVideoStreamIndex==-1)
{
LOGE("Didn't find a video stream");
return -1; // Didn't find a video stream
}
// Get a pointer to the codec context for the video stream
pCodecCtx = gpMovieFile->streams[gmVideoStreamIndex]->codec;
// Find the decoder for the video stream
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
LOGE("Codec not found a video decoder");
return -1;
}
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
{
LOGE("uld not open video codec");
return -1;
}
if(pCodecCtx->time_base.num>1000 && pCodecCtx->time_base.den==1)
{
pCodecCtx->time_base.den=1000;
}
video_frame_delay = 1000000/ av_q2d(gpMovieFile->streams[gmVideoStreamIndex]->r_frame_rate);
LOGI("video_frame_delay %lf", video_frame_delay);
#ifdef SUPORT_VIDEO_OUT
gmVideoWidth = pCodecCtx->width;
gmVideoHeight = pCodecCtx->height;
gmDuration = gpMovieFile->duration;
gpConvertCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);
if (gpConvertCtx == NULL)
{
LOGE("SwsContext is NULL");
return -1;
}
void* pixels;
if (VideoDriver_getPixels(pCodecCtx->width, pCodecCtx->height, &pixels) != 0)
{
LOGE("VideoDriver_getPixels error");
return -1;
}
gpFrame = avcodec_alloc_frame();
if (gpFrame == NULL) {
LOGE("gpFrame alloc error");
return -1;
}
// Assign appropriate parts of buffer to image planes in pFrameRGB
// Note that pFrameRGB is an AVFrame, but AVFrame is a superset
// of AVPicture
avpicture_fill((AVPicture *) gpFrame, (uint8_t *) pixels, PIX_FMT_RGB565, pCodecCtx->width, pCodecCtx->height);
#endif
return 0;
}
/**************************************************************************************************/
/*********************************************************************************************/
void MediaPlayer_Display(AVFrame* frame)
{
AVCodecContext *pCodecCtx;
int ret;
int mw, mh, ms;
int sw, sh;
void* pbuf;
if (frame == NULL)
{
LOGI("%s input error", __FUNCTION__);
return;
}
// Get a pointer to the codec context for the video stream
pCodecCtx = gpMovieFile->streams[gmVideoStreamIndex]->codec;
LOGE("pix_fmt=%d", pCodecCtx->pix_fmt);
mw = pCodecCtx->width;
mh = pCodecCtx->height;
ret = VideoDriver_lockSurface();
if(ret < 0)
{
return;
}
pbuf = VideoDriver_getBits();
ms = VideoDriver_getStride();
sw = VideoDriver_getWidth();
sh = VideoDriver_getHeight();
avpicture_fill((AVPicture *)gpFrame, (uint8_t *)pbuf, PIX_FMT_RGB565, sw, sh);
// LOGE("data0=%d, line0=%d", (int)gpFrame->data[0], (int)gpFrame->linesize[0]);
disp_time();
if (gpConvertCtx == NULL)
{
if(gbScreenFill == false)
{
LOGI("do ScreenNormal");
gpConvertCtx = sws_getContext(mw, mh, pCodecCtx->pix_fmt, mw, mh, PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);
}
else
{
LOGI("do ScreenFill");
gpConvertCtx = sws_getContext(mw, mh, pCodecCtx->pix_fmt, sw, sh, PIX_FMT_RGB565, SWS_POINT, NULL, NULL, NULL);
}
}
// LOGD("sws_scale");
ret = sws_scale(gpConvertCtx, frame->data, frame->linesize, 0, mh, gpFrame->data, gpFrame->linesize);
disp_time();
VideoDriver_unLockSurface();
}
点评回复 引用
举报 返回顶部
‹ 上一主题|下一主题 ›
返回列表
如何显示c:盘
在C语言中如何显示程序运行结果
C语言如何在XP的DOS环境下,安装什么中文系统,才能显示汉字
在DOS下显示"C:\SAN5\"的字样,求教如何打开游戏
如何在选择Windows98菜单后进入DOS,显示C:\
如何在c#.net下,Windows窗体中使用控件显示一个数组?
请教,C#.NET中如何打开文本文件并在对话中显示出来??
我在清华同方急救中心备份c盘时,显示读取文件错误,是怎么回事,如何解决?
C#:如何在lable中动态显示正在提取的记录
JSP可以调用class里的native方法吗?如何调用?
我想与native speaker用英语聊天,请问我怎么才能在网上找到他们?
.net(C#)中用div编写的下拉菜单,如何让下拉菜单显示时在最上层。
如何在C盘上多放点空间~
native place是什么?
Yamaha DS1x Native Audio
TC Native Essentials 2.02
TC NATIVE ESSENTIALS2.02
C语言写:父进程创建两个子进程每进程显示在屏幕上显示自己的进程ID,考虑如何控制程序的输出顺序
如何在桌面显示回收站
在网上如何显示股票行情
在网上如何显示股票行情.....
如何在WMP9显示歌词
如何在桌面显示文字?
硬盘损坏 只能显示C盘大小 如何修复