【MediaPlayer】基于libvlc+awtk的媒體播放器

基于libvlc+awtk的媒體播放器

  • libvlc
    • 下載地址
  • awtk
    • 下載地址
  • 代碼實現
    • libvlc相關邏輯接口
    • UI媒體接口
    • 實例化媒體播放器
    • 注意事項

libvlc

下載地址

  1. 可以到https://download.videolan.org/pub/videolan/vlc/去下載一個vlc版本,下載后其實是vlc的windows客戶端,里面包含sdk目錄,用于windows程序鏈接libvlc
  2. 需要的文件為:sdk/include、sdk/lib、libvlc.dll、libvlc_core.dll

awtk

下載地址

  1. AWTK是一套基于C語言的國產UI框架,目前開源在https://github.com/zlgopen/awtk,可自行下載后,按照文檔編譯即可,編譯方法也很簡單

代碼實現

以像素格式RGBA8888為例

libvlc相關邏輯接口

  1. 頭文件
#ifndef __TASK_MEDIA_PLAYER_H__
#define __TASK_MEDIA_PLAYER_H__#include <stdint.h>typedef int(*VideoFrameCb)(const unsigned char* data, const char* format, int w, int h, void* ctx);typedef int(*AudioFrameCb)(const unsigned char* data, const char* format, int sampleRate, void* ctx);typedef enum {MEDIA_STATUS_NONE,MEDIA_STATUS_OPENING,MEDIA_STATUS_PLAYING,MEDIA_STATUS_PAUSE,MEDIA_STATUS_STOP,MEDIA_STATUS_END,MEDIA_STATUS_ERROR,
}mediaStateEnum;/*
* @function: taskMediaPlayerCreateLocal
* @params:
*    filePath: 媒體文件路徑
* @brief:
*    創建本地文件的媒體播放器
*    成功返回播放器句柄,失敗返回NULL
*/
void* taskMediaPlayerCreateLocal(const char* filePath);/*
* @function: taskMediaPlayerCreateLocation
* @params:
*    path: 網絡串流媒體路徑
* @brief:
*    創建網絡串流的媒體播放器
*    成功返回播放器句柄,失敗返回NULL
*/
void* taskMediaPlayerCreateLocation(const char* path);/*
* @function: taskMediaPlayerStart
* @params:
*    handle: 播放器句柄
* @brief:
*    開啟播放
*/
int taskMediaPlayerStart(void** handle);/*
* @function: taskMediaPlayerIsPlaying
* @params:
*    handle: 播放器句柄
* @brief:
*    查詢是否處于播放狀態
*    1:處于播放狀態,0:不處于播放狀態
*/
int taskMediaPlayerIsPlaying(void** handle);/*
* @function: taskMediaPlayerPause
* @params:
*    handle: 播放器句柄
* @brief:
*    暫停播放
*/
int taskMediaPlayerPause(void** handle);/*
* @function: taskMediaPlayerResume
* @params:
*    handle: 播放器句柄
* @brief:
*    恢復播放
*/
int taskMediaPlayerResume(void** handle);/*
* @function: taskMediaPlayerStop
* @params:
*    handle: 播放器句柄
* @brief:
*    停止播放
*/
int taskMediaPlayerStop(void** handle);/*
* @function: taskMediaPlayerRelease
* @params:
*    handle: 播放器句柄
* @brief:
*    釋放播放器資源
*/
int taskMediaPlayerRelease(void** handle);/*
* @function: taskMediaPlayerTakeSnapshot
* @params:
*    handle: 播放器句柄
*    fileName: 需要保存的文件路徑
* @brief:
*    截圖,成功返回0,失敗返回-1
*/
int taskMediaPlayerTakeSnapshot(void** handle, const char* fileName);/*
* @function: taskMediaPlayerSetFormat
* @params:
*    handle: 播放器句柄
*    format: 編碼格式("RGBA/RV32/RV16"等等)
*    w: 輸出視頻寬度
*    h: 輸出視頻高度
* @brief:
*    設置編碼格式和輸出寬高
*/
int taskMediaPlayerSetFormat(void** handle, const char* format, int w, int h);/*
* @function: taskMediaPlayerSetCallback
* @params:
*    handle: 播放器句柄
*    videoCb: 視頻輸出回調
*    videoCtx: 視頻回調參數
*    audioCb: 音頻輸出回調
*    audioCtx: 音頻回調參數
* @brief:
*    設置音視頻輸出的回調,每一幀解碼都會調用回調,供上層應用播放和顯示
*/
int taskMediaPlayerSetCallback(void** handle, VideoFrameCb videoCb, void* videoCtx, AudioFrameCb audioCb, void* audioCtx);/*
* @function: taskMediaPlayerGetMediaSize
* @params:
*    handle: 播放器句柄
*    num: 視頻編號(通常是0)
*    width: 視頻寬度[out]
*    height: 視頻高度[out]
* @brief:
*    成功返回0,失敗返回-1
*/
int taskMediaPlayerGetMediaSize(void** handle, unsigned int num, unsigned int *width, unsigned int* height);/*
* @function: taskMediaPlayerSetMediaScale
* @params:
*    handle: 播放器句柄
*    scale: 視頻縮放倍數
* @brief:
*    成功返回0,失敗返回-1
*/
int taskMediaPlayerSetMediaScale(void** handle, float scale);/*
* @function: taskMediaPlayerGetMediaScale
* @params:
*    handle: 播放器句柄
* @brief:
*    成功返回視頻播放倍數,失敗返回0.0
*/
float taskMediaPlayerGetMediaScale(void** handle);/*
* @function: taskMediaPlayerGetMediaLength
* @params:
*    handle: 播放器句柄
* @brief:
*    成功返回媒體總時長(ms),失敗返回-1
*/
int64_t taskMediaPlayerGetMediaLength(void** handle);/*
* @function: taskMediaPlayerGetMediaTime
* @params:
*    handle: 播放器句柄
* @brief:
*    成功返回媒體當前播放時長(ms),失敗返回-1
*/
int64_t taskMediaPlayerGetMediaTime(void** handle);/*
* @function: taskMediaPlayerSetMediaTime
* @params:
*    handle: 播放器句柄
*    timeMs: 設置媒體當前播放時間偏移(ms)
* @brief:
*    成功返回0,失敗返回-1
*/
int taskMediaPlayerSetMediaTime(void** handle, int64_t timeMs);/*
* @function: taskMediaPlayerSetMediaVolume
* @params:
*    handle: 播放器句柄
*    volume: 音量值[0, 100]
* @brief:
*    成功返回0,失敗返回-1
*/
int taskMediaPlayerSetMediaVolume(void** handle, int volume);/*
* @function: taskMediaPlayerGetMediaVolume
* @params:
*    handle: 播放器句柄
* @brief:
*    成功返回音量值,失敗返回-1
*/
int taskMediaPlayerGetMediaVolume(void** handle);/*
* @function: taskMediaPlayerGetMediaPosition
* @params:
*    handle: 播放器句柄
* @brief:
*    獲取當前媒體播放進度位置,[0.0, 1.0]
*/
float taskMediaPlayerGetMediaPosition(void** handle);/*
* @function: taskMediaPlayerSetMediaPosition
* @params:
*    handle: 播放器句柄
*    pos: 進度
* @brief:
*    設置當前媒體播放進度位置,[0.0, 1.0]
*/
int taskMediaPlayerSetMediaPosition(void** handle, float pos);/*
* @function: taskMediaPlayerGetMediaRate
* @params:
*    handle: 播放器句柄
*    pos: 進度
* @brief:
*    返回當前媒體播放速率
*/
float taskMediaPlayerGetMediaRate(void** handle);/*
* @function: taskMediaPlayerGetMediaRate
* @params:
*    handle: 播放器句柄
*    rate: 播放速率
* @brief:
*    設置當前媒體播放速率
*/
int taskMediaPlayerSetMediaRate(void** handle, float rate);/*
* @function: taskMediaPlayerGetMediaState
* @params:
*    handle: 播放器句柄
* @brief:
*    獲取媒體播放狀態
*/
mediaStateEnum taskMediaPlayerGetMediaState(void** handle);/*
* @function: taskMediaPlayerGetUrlIsValid
* @params:
*    url: 播放的url
* @brief:
*    獲取url是否是有效的
*    1: 有效, 0: 無效
*/
int taskMediaPlayerGetUrlIsValid(const char* url);#endif // !__TASK_TOOLS_MEDIA_PLAYER_H__
  1. c 文件
#include <string.h>
#include "task_media_player.h"
#include "tools/log.h"
#include "tools/tools_typedef.h"
#include "tools/libc_str.h"#ifdef _WIN32  
#include <stddef.h> // 包含 ptrdiff_t 的定義  
typedef ptrdiff_t ssize_t;
#endif#include "vlc/vlc.h"#define TASK_MEDIA_PLAYER_TAG	"media_player"typedef struct {libvlc_instance_t * instance;libvlc_media_player_t *mediaPlayer;libvlc_media_t *media;unsigned char* pixel;char format[16];int w;int h;int frameSize;VideoFrameCb videoCb;void* videoCtx;AudioFrameCb audioCb;void* audioCtx;
}vlcMediaPlayer_t;static unsigned mediaVideoFormatSetup(void **opaque, char *chroma, unsigned *width, unsigned *height,unsigned *pitches, unsigned *lines) {vlcMediaPlayer_t** mediaPlayer = (vlcMediaPlayer_t**)opaque;if (mediaPlayer != NULL && *mediaPlayer != NULL) {strcpy(chroma, (*mediaPlayer)->format);*width = (*mediaPlayer)->w;*height = (*mediaPlayer)->h;if (strcmp(chroma, "RGBA") == 0) {*pitches = 4 * (*mediaPlayer)->w;*lines = (*mediaPlayer)->h;}return 1;}return 0;
}static void *mediaVideoLock(void *opaque, void **planes) {vlcMediaPlayer_t* mediaPlayer = (vlcMediaPlayer_t*)opaque;*planes = mediaPlayer->pixel;return NULL;
}static void mediaVideoUnlock(void *opaque, void *picture, void *const *planes) {vlcMediaPlayer_t* mediaPlayer = (vlcMediaPlayer_t*)opaque;if (mediaPlayer->videoCb != NULL) {mediaPlayer->videoCb((const unsigned char*)(*planes), mediaPlayer->format, mediaPlayer->w, mediaPlayer->h, mediaPlayer->videoCtx);}
}static void mediaVideoDisplay(void *opaque, void *picture) {}void* taskMediaPlayerCreateLocal(const char* filePath) {return_val_if_fail(filePath != NULL, NULL);vlcMediaPlayer_t* vlcMediaPlayer = calloc(1, sizeof(vlcMediaPlayer_t));if (vlcMediaPlayer != NULL) {vlcMediaPlayer->instance = libvlc_new(0, NULL);vlcMediaPlayer->media = libvlc_media_new_path(vlcMediaPlayer->instance, filePath);if (vlcMediaPlayer->media != NULL) {vlcMediaPlayer->mediaPlayer = libvlc_media_player_new_from_media(vlcMediaPlayer->media);}else {libvlc_release(vlcMediaPlayer->instance);free(vlcMediaPlayer);return NULL;}log_Debug(TASK_MEDIA_PLAYER_TAG, "%s:%p successfully!\n", __func__, vlcMediaPlayer);return vlcMediaPlayer;}log_Debug(TASK_MEDIA_PLAYER_TAG, "%s failed!\n", __func__);return NULL;
}void* taskMediaPlayerCreateLocation(const char* path) {return_val_if_fail(path != NULL, NULL);vlcMediaPlayer_t* vlcMediaPlayer = calloc(1, sizeof(vlcMediaPlayer_t));if (vlcMediaPlayer != NULL) {vlcMediaPlayer->instance = libvlc_new(0, NULL);vlcMediaPlayer->media = libvlc_media_new_location(vlcMediaPlayer->instance, path);if (vlcMediaPlayer->media != NULL) {vlcMediaPlayer->mediaPlayer = libvlc_media_player_new_from_media(vlcMediaPlayer->media);}else {libvlc_release(vlcMediaPlayer->instance);free(vlcMediaPlayer);return NULL;}log_Debug(TASK_MEDIA_PLAYER_TAG, "%s:%p, path:%s successfully!\n", __func__, vlcMediaPlayer, path);return vlcMediaPlayer;}log_Debug(TASK_MEDIA_PLAYER_TAG, "%s failed!\n", __func__);return NULL;
}int taskMediaPlayerStart(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {log_Debug(TASK_MEDIA_PLAYER_TAG, "%s:%p!\n", __func__, *vlcMediaPlayer);return libvlc_media_player_play((*vlcMediaPlayer)->mediaPlayer);}return -1;
}int taskMediaPlayerIsPlaying(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_media_player_is_playing((*vlcMediaPlayer)->mediaPlayer);}return 0;
}int taskMediaPlayerPause(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {libvlc_media_player_set_pause((*vlcMediaPlayer)->mediaPlayer, 1);log_Debug(TASK_MEDIA_PLAYER_TAG, "%s:%p!\n", __func__, *vlcMediaPlayer);return 0;}return -1;
}int taskMediaPlayerResume(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {libvlc_media_player_set_pause((*vlcMediaPlayer)->mediaPlayer, 0);log_Debug(TASK_MEDIA_PLAYER_TAG, "%s:%p!\n", __func__, *vlcMediaPlayer);return 0;}return -1;
}int taskMediaPlayerStop(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {libvlc_media_player_stop((*vlcMediaPlayer)->mediaPlayer);log_Debug(TASK_MEDIA_PLAYER_TAG, "%s:%p!\n", __func__, *vlcMediaPlayer);return 0;}return -1;
}int taskMediaPlayerRelease(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {if (taskMediaPlayerIsPlaying(handle)) {libvlc_media_player_stop((*vlcMediaPlayer)->mediaPlayer);}libvlc_media_player_release((*vlcMediaPlayer)->mediaPlayer);libvlc_media_release((*vlcMediaPlayer)->media);libvlc_release((*vlcMediaPlayer)->instance);log_Debug(TASK_MEDIA_PLAYER_TAG, "%s:%p successfully!\n", __func__, *vlcMediaPlayer);if ((*vlcMediaPlayer)->pixel != NULL) {free((*vlcMediaPlayer)->pixel);}free(*vlcMediaPlayer);*vlcMediaPlayer = NULL;return 0;}return -1;
}int taskMediaPlayerTakeSnapshot(void** handle, const char* fileName) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_video_take_snapshot((*vlcMediaPlayer)->mediaPlayer, 0, fileName, 0, 0);}return -1;
}int taskMediaPlayerSetFormat(void** handle, const char* format, int w, int h) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {int size = 0;if (strcmp(format, "RGBA") == 0) {size = sizeof(char) * w * h * 4;}(*vlcMediaPlayer)->w = w;(*vlcMediaPlayer)->h = h;strlcpy((*vlcMediaPlayer)->format, format, sizeof((*vlcMediaPlayer)->format));if (size != (*vlcMediaPlayer)->frameSize) {(*vlcMediaPlayer)->frameSize = size;if ((*vlcMediaPlayer)->pixel != NULL) {free((*vlcMediaPlayer)->pixel);}if ((*vlcMediaPlayer)->pixel == NULL) {(*vlcMediaPlayer)->pixel = calloc(1, (*vlcMediaPlayer)->frameSize + 1);}}//libvlc_video_set_format((*vlcMediaPlayer)->mediaPlayer, format, w, h, w*4);return 0;}return -1;
}int taskMediaPlayerSetCallback(void** handle, VideoFrameCb videoCb, void* videoCtx, AudioFrameCb audioCb, void* audioCtx) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {(*vlcMediaPlayer)->videoCb = videoCb;(*vlcMediaPlayer)->videoCtx = videoCtx;(*vlcMediaPlayer)->audioCb = audioCb;(*vlcMediaPlayer)->audioCtx = audioCtx;libvlc_video_set_format_callbacks((*vlcMediaPlayer)->mediaPlayer, mediaVideoFormatSetup, NULL);libvlc_video_set_callbacks((*vlcMediaPlayer)->mediaPlayer, mediaVideoLock, mediaVideoUnlock, mediaVideoDisplay, *vlcMediaPlayer);return 0;}return -1;
}int taskMediaPlayerGetMediaSize(void** handle, unsigned int num, unsigned int *width, unsigned int* height) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL && width != NULL && height != NULL) {return ((libvlc_video_get_size((*vlcMediaPlayer)->mediaPlayer, num, width, height) == 0) && *width > 0 && *height > 0) ? 0 : -1;}return -1;
}int taskMediaPlayerSetMediaScale(void** handle, float scale) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {libvlc_video_set_scale((*vlcMediaPlayer)->mediaPlayer, scale);return 0;}return -1;
}float taskMediaPlayerGetMediaScale(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_video_get_scale((*vlcMediaPlayer)->mediaPlayer);}return 0.0;
}int64_t taskMediaPlayerGetMediaLength(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_media_player_get_length((*vlcMediaPlayer)->mediaPlayer);}return -1;
}int64_t taskMediaPlayerGetMediaTime(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_media_player_get_time((*vlcMediaPlayer)->mediaPlayer);}return -1;
}int taskMediaPlayerSetMediaTime(void** handle, int64_t timeMs) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {libvlc_media_player_set_time((*vlcMediaPlayer)->mediaPlayer, timeMs);return 0;}return -1;
}int taskMediaPlayerSetMediaVolume(void** handle, int volume) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_audio_set_volume((*vlcMediaPlayer)->mediaPlayer, volume);}return -1;
}int taskMediaPlayerGetMediaVolume(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_audio_get_volume((*vlcMediaPlayer)->mediaPlayer);}return -1;
}float taskMediaPlayerGetMediaPosition(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_media_player_get_position((*vlcMediaPlayer)->mediaPlayer);}return 0.0;
}int taskMediaPlayerSetMediaPosition(void** handle, float pos) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {libvlc_media_player_set_position((*vlcMediaPlayer)->mediaPlayer, pos);return 0;}return -1;
}float taskMediaPlayerGetMediaRate(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_media_player_get_rate((*vlcMediaPlayer)->mediaPlayer);}return 0.0;
}int taskMediaPlayerSetMediaRate(void** handle, float rate) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {return libvlc_media_player_set_rate((*vlcMediaPlayer)->mediaPlayer, rate);}return -1;
}mediaStateEnum taskMediaPlayerGetMediaState(void** handle) {vlcMediaPlayer_t** vlcMediaPlayer = (vlcMediaPlayer_t**)handle;if (vlcMediaPlayer != NULL && *vlcMediaPlayer != NULL) {mediaStateEnum mediaState = MEDIA_STATUS_NONE;libvlc_state_t state = libvlc_media_player_get_state((*vlcMediaPlayer)->mediaPlayer);switch (state) {case libvlc_Opening:mediaState = MEDIA_STATUS_OPENING;break;case libvlc_Playing:mediaState = MEDIA_STATUS_PLAYING;break;case libvlc_Paused:mediaState = MEDIA_STATUS_PAUSE;break;case libvlc_Stopped:mediaState = MEDIA_STATUS_STOP;break;case libvlc_Ended:mediaState = MEDIA_STATUS_END;break;case libvlc_Error:mediaState = MEDIA_STATUS_ERROR;break;}return mediaState;}return MEDIA_STATUS_NONE;
}int taskMediaPlayerGetUrlIsValid(const char* url) {if(url == NULL) return 0;if(strStartWith(url, "file:///")|| strStartWith(url, "rtsp://")|| strStartWith(url, "http://")|| strStartWith(url, "mms://")|| strStartWith(url, "screen://")|| strStartWith(url, "dvd://")|| strStartWith(url, "vcd://")|| strStartWith(url, "cdda://")|| strStartWith(url, "udp://")) {return 1;}return 0;
}

UI媒體接口

一、UI媒體播放接口,使用AWTK自帶的mutable_image控件,AWTK控件的使用,可在AWTK控件的使用去查看官方文檔,以及AWTK源碼里doc/目錄的各種文檔

二、代碼封裝如下:

  1. 頭文件
#ifndef __UI_MEDIA_PLAYER_LIST_H__
#define __UI_MEDIA_PLAYER_LIST_H__#include <stdio.h>
#include "awtk.h"typedef enum {MEDIA_START,MEDIA_PAUSE,MEDIA_STOP,
}MEDIA_PLAY_ACTION;typedef enum {MEDIA_STATE_NONE,MEDIA_STATE_OPENING,MEDIA_STATE_PLAYING,MEDIA_STATE_PAUSE,MEDIA_STATE_STOP,MEDIA_STATE_END,MEDIA_STATE_ERROR,
}MEDIA_PLAY_STATE;typedef struct mediaPlayerList {/* public */int (*getUrlIsValid)(const char* url);ret_t(*setDrawRectParam)(widget_t* mutableImage, void* handle);void* (*getMediaHandle)(void* handle);int(*getPlayVideoState)(void* handle);ret_t (*getPlayVideoSize)(void* handle, int num, int *w, int*h);ret_t (*createPlayVideoBitmap)(void* handle, bitmap_format_t format, int w, int h);ret_t (*getPlayVideoFrame)(void* handle, bitmap_t* image);ret_t(*takeSnapShot)(void* handle, const char* filePath);int64_t(*getMediaLenth)(void* handle);int64_t(*getMediaTime)(void* handle);ret_t(*setMediaPosition)(void* handle, float position);float(*getMediaPosition)(void* handle);ret_t(*setMediaVolume)(void* handle, int volume);int(*getMediaVolume)(void* handle);ret_t(*setPlayVideoFormat)(void* handle, const char* format, int w, int h);ret_t(*start)(void* handle, const char* url);ret_t(*pause)(void* handle);ret_t(*stop)(void* handle);widget_t* mutableImage;/* private */bitmap_t* image;int playState;void* mediaHandle;int needClearImge; /* 結束時清除一幀mutableImage */
}mediaPlayerList_t;/*
* @function: uiMediaPlayerListCreate
* @params:
*    無
* @brief:
*    創建播放器
*/
mediaPlayerList_t* uiMediaPlayerListCreate(void);/*
* @function: uiMediaPlayerListDestroy
* @params:
*    mediaPlayList: 播放器句柄
* @brief:
*    銷毀播放器
*/
ret_t uiMediaPlayerListDestroy(mediaPlayerList_t** mediaPlayList);#endif // !__UI_MEDIA_PLAYER_LIST_H__
  1. c 文件
#include <stdio.h>
#include "ui_media_player_list.h"
#include "task/media_player/task_media_player.h"
#include "tools/log.h"#define UI_MEDIA_PLAYLIST_TAG	"ui_media_play_list.c"static ret_t uiMediaPlayerListStop(void* handle);static bitmap_t* uiMediaPlayerListCreateImage(void* ctx, bitmap_format_t format, bitmap_t* old_image) {bitmap_t* image = old_image;mediaPlayerList_t* playList = (mediaPlayerList_t*)ctx;return_value_if_fail(playList != NULL && playList->mutableImage != NULL, NULL);if (playList->getPlayVideoState(playList) == MEDIA_STATE_PLAYING) {uint32_t ww = playList->mutableImage->w;uint32_t wh = playList->mutableImage->h;uint32_t vw = playList->mutableImage->w;uint32_t vh = playList->mutableImage->h;if (playList->getPlayVideoSize(playList, 0, &vw, &vh) == RET_OK) {double scale_w = (double)ww / (double)vw;double scale_h = (double)wh / (double)vh;double scale = tk_min(scale_w, scale_h);uint32_t iw = (uint32_t)(vw * scale) / 32 * 32;uint32_t ih = (uint32_t)(vh * scale) / 32 * 32;playList->setPlayVideoFormat(playList, "RGBA", iw, ih);if (old_image != NULL) {if (old_image->w != iw || old_image->h != ih) {bitmap_destroy(old_image);old_image = NULL;}}if (old_image == NULL && iw > 0 && ih > 0) {image = bitmap_create_ex(iw, ih, 0, format);playList->createPlayVideoBitmap(playList, format, iw, ih);}}}return image;
}static ret_t uiMediaPlayerListPrepareImage(void* ctx, bitmap_t* image) {mediaPlayerList_t* playList = (mediaPlayerList_t*)ctx;if (playList != NULL && playList->needClearImge) {rect_t r = rect_init(0, 0, image->w, image->h);color_t color;color.rgba.r = 0;color.rgba.g = 0;color.rgba.b = 0;color.rgba.a = 0;playList->needClearImge = 0;return image_clear(image, &r, color);}else if (playList != NULL) {return playList->getPlayVideoFrame(playList, image);}return RET_FAIL;
}static bool_t uiMediaPlayerListNeedRedrawImage(void* ctx) {mediaPlayerList_t *playList = (mediaPlayerList_t*)ctx;if (playList != NULL) {return (playList->getPlayVideoState(playList) == MEDIA_STATE_PLAYING) || (playList->needClearImge == 1);}return FALSE;
}static ret_t uiMediaPlayerListSetDrawRectParam(widget_t* mutableImage, void* handle) {mediaPlayerList_t* playList = (mediaPlayerList_t*)handle;playList->mutableImage = mutableImage;mutable_image_set_create_image(mutableImage, uiMediaPlayerListCreateImage, handle);mutable_image_set_prepare_image(mutableImage, uiMediaPlayerListPrepareImage, handle);mutable_image_set_need_redraw(mutableImage, uiMediaPlayerListNeedRedrawImage, handle);
}static int uiMediaVideoCb(const unsigned char* data, const char* format, int w, int h, void* ctx) {mediaPlayerList_t *mediaPlayList = (mediaPlayerList_t*)ctx;if (mediaPlayList != NULL && mediaPlayList->image != NULL) {if (tk_str_eq(format, "RGBA")) {uint32_t i = 0;uint32_t bpp = 0;uint32_t size = 0;uint8_t* image_data = NULL;bitmap_format_t bitmapFormat = mediaPlayList->image->format;bpp = bitmap_get_bpp(mediaPlayList->image);size = mediaPlayList->image->h * bitmap_get_line_length(mediaPlayList->image);image_data = bitmap_lock_buffer_for_write(mediaPlayList->image);for (; i < size && i < w * h * 4; i += bpp, image_data += bpp) {if(bitmapFormat == BITMAP_FMT_RGBA8888) {image_data[0] = data[i];image_data[1] = data[i + 1];image_data[2] = data[i + 2];image_data[3] = data[i + 3];} else {image_data[0] = data[i + 2];image_data[1] = data[i + 1];image_data[2] = data[i];image_data[3] = data[i + 3];}}bitmap_unlock_buffer(mediaPlayList->image);}}return 0;
}static ret_t uiMediaPlayerListStart(void* handle, const char* url) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList == NULL) {log_Error(UI_MEDIA_PLAYLIST_TAG, "%s, error! playState=%d\n", __func__, mediaPlayList!=NULL?mediaPlayList->playState:-1);return RET_FAIL;}if (mediaPlayList->playState == MEDIA_STATE_PLAYING || mediaPlayList->playState == MEDIA_STATE_NONE) {uiMediaPlayerListStop(mediaPlayList);mediaPlayList->playState = MEDIA_STATE_NONE;}if (mediaPlayList->playState == MEDIA_STATE_PAUSE){mediaPlayList->playState = MEDIA_STATE_PLAYING;taskMediaPlayerResume(&mediaPlayList->mediaHandle);}else if(mediaPlayList->playState == MEDIA_STATE_NONE){ /* 播放 */mediaPlayList->mediaHandle = taskMediaPlayerCreateLocation(url);if (mediaPlayList->mediaHandle != NULL) {taskMediaPlayerSetCallback(&mediaPlayList->mediaHandle, uiMediaVideoCb, mediaPlayList, NULL, NULL);taskMediaPlayerStart(&mediaPlayList->mediaHandle);mediaPlayList->playState = MEDIA_STATE_PLAYING;}else {return RET_FAIL;}}return RET_OK;
}static ret_t uiMediaPlayerListPause(void* handle) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {mediaPlayList->playState = MEDIA_STATE_PAUSE;return taskMediaPlayerPause(&mediaPlayList->mediaHandle) == 0 ? RET_OK : RET_FAIL;}return RET_FAIL;
}static ret_t uiMediaPlayerListStop(void* handle) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;return_value_if_fail(mediaPlayList != NULL, RET_FAIL);if (mediaPlayList->mediaHandle != NULL) {taskMediaPlayerRelease(&mediaPlayList->mediaHandle);mediaPlayList->mediaHandle = NULL;}if (mediaPlayList->image != NULL) {rect_t r = rect_init(0, 0, mediaPlayList->image->w, mediaPlayList->image->h);color_t color;color.rgba.r = 0;color.rgba.g = 0;color.rgba.b = 0;color.rgba.a = 0;image_clear(mediaPlayList->image, &r, color);}mediaPlayList->playState = MEDIA_STATE_NONE;mediaPlayList->needClearImge = 1;return RET_OK;
}static int uiMediaPlayerListGetState(void* handle) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL) {return (MEDIA_PLAY_STATE)taskMediaPlayerGetMediaState(&mediaPlayList->mediaHandle);}return MEDIA_STATE_NONE;
}static ret_t uiMediaPlayerListSetFormat(void* handle, const char* format, int w, int h) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL) {taskMediaPlayerSetFormat(&mediaPlayList->mediaHandle, format, w, h);}return RET_OK;
}static ret_t uiMediaPlayerListCreateBitmap(void* handle, bitmap_format_t format, int w, int h) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList == NULL) {return RET_FAIL;}if (mediaPlayList->image != NULL) {bitmap_destroy(mediaPlayList->image);mediaPlayList->image = NULL;}if (mediaPlayList->image == NULL) {mediaPlayList->image = bitmap_create_ex(w, h, 0, format);}return RET_OK;
}static ret_t uiMediaPlayerListGetVideoSize(void* handle, int num, int* w, int *h) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL) {return taskMediaPlayerGetMediaSize(&mediaPlayList->mediaHandle, 0, w, h) == 0 ? RET_OK : RET_FAIL;}return RET_FAIL;
}static ret_t uiMediaPlayerListGetVideoFrame(void* handle, bitmap_t* image) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->playState == MEDIA_STATE_PLAYING && mediaPlayList->image != NULL) {rect_t r = rect_init(0, 0, image->w, image->h);return image_copy(image, mediaPlayList->image, &r, 0, 0);}return RET_FAIL;
}static ret_t uiMediaPlayerListTakeSnapShot(void* handle, const char* filePath) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {return taskMediaPlayerTakeSnapshot(&mediaPlayList->mediaHandle, filePath) == 0 ? RET_OK : RET_FAIL;}return RET_FAIL;
}static int64_t uiMediaPlayerListGetMediaLenth(void* handle) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {return taskMediaPlayerGetMediaLength(&mediaPlayList->mediaHandle) ;}return 0;
}static int64_t uiMediaPlayerListGetMediaTime(void* handle) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {return taskMediaPlayerGetMediaTime(&mediaPlayList->mediaHandle);}return 0;
}static float uiMediaPlayerListGetMediaPosition(void* handle) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {return taskMediaPlayerGetMediaPosition(&mediaPlayList->mediaHandle);}return 0.0;
}static ret_t uiMediaPlayerListSetMediaPosition(void* handle, float pos) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {return taskMediaPlayerSetMediaPosition(&mediaPlayList->mediaHandle, pos) == 0 ? RET_OK : RET_FAIL;}return RET_FAIL;
}static ret_t uiMediaPlayerListSetMediaVolume(void* handle, int volume) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {return taskMediaPlayerSetMediaVolume(&mediaPlayList->mediaHandle, volume) == 0 ? RET_OK : RET_FAIL;}return RET_FAIL;
}static int uiMediaPlayerListGetMediaVolume(void* handle) {mediaPlayerList_t* mediaPlayList = (mediaPlayerList_t*)handle;if (mediaPlayList != NULL && mediaPlayList->mediaHandle != NULL) {return taskMediaPlayerGetMediaVolume(&mediaPlayList->mediaHandle);}return -1;
}static int uiMediaPlayerListGetUrlIsValid(const char* url) {return_value_if_fail(url != NULL, 0);return taskMediaPlayerGetUrlIsValid(url);
}mediaPlayerList_t* uiMediaPlayerListCreate(void) {mediaPlayerList_t* mediaPlayList = calloc(1, sizeof(mediaPlayerList_t));if (mediaPlayList != NULL) {mediaPlayList->start = uiMediaPlayerListStart;mediaPlayList->pause = uiMediaPlayerListPause;mediaPlayList->stop = uiMediaPlayerListStop;mediaPlayList->createPlayVideoBitmap = uiMediaPlayerListCreateBitmap;mediaPlayList->getPlayVideoFrame = uiMediaPlayerListGetVideoFrame;mediaPlayList->getPlayVideoSize = uiMediaPlayerListGetVideoSize;mediaPlayList->getPlayVideoState = uiMediaPlayerListGetState;mediaPlayList->setPlayVideoFormat = uiMediaPlayerListSetFormat;mediaPlayList->takeSnapShot = uiMediaPlayerListTakeSnapShot;mediaPlayList->getMediaLenth = uiMediaPlayerListGetMediaLenth;mediaPlayList->getMediaTime = uiMediaPlayerListGetMediaTime;mediaPlayList->setMediaPosition = uiMediaPlayerListSetMediaPosition;mediaPlayList->getMediaPosition = uiMediaPlayerListGetMediaPosition;mediaPlayList->setMediaVolume = uiMediaPlayerListSetMediaVolume;mediaPlayList->getMediaVolume = uiMediaPlayerListGetMediaVolume;mediaPlayList->setDrawRectParam = uiMediaPlayerListSetDrawRectParam;mediaPlayList->getUrlIsValid = uiMediaPlayerListGetUrlIsValid;}return mediaPlayList;
}ret_t uiMediaPlayerListDestroy(mediaPlayerList_t** mediaPlayList) {if (mediaPlayList != NULL && *mediaPlayList != NULL) {if ((*mediaPlayList)->mediaHandle != NULL) {uiMediaPlayerListStop(mediaPlayList);}if ((*mediaPlayList)->image != NULL) {bitmap_destroy((*mediaPlayList)->image);(*mediaPlayList)->image = NULL;}free(*mediaPlayList);*mediaPlayList = NULL;}return RET_OK;
}

實例化媒體播放器

  1. main.c:
#include "awtk.h"
#include "assets.inc"ret_t application_init(void) {window_manager_set_max_fps(window_manager(), 120);widget_t* win = window_open("media_play_win");mediaPlayerList_t* playList = uiMediaPlayerListCreate();widget_t* mutable_image = widget_lookup(win, "video_mutable", TRUE);playList->setDrawRectParam(mutable_image, playList);playList->start(playList, "E:\\test.avi");return RET_OK;
}ret_t application_exit() {log_debug("application_exit\n");window_close(widget_child(window_manager(), "media_play_win"));return RET_OK;
}#define LCD_WIDTH 1024
#define LCD_HEIGHT 600
#define APP_TYPE APP_DESKTOP
#define APP_NAME "vlc_awtk_media_player"
#define APP_ENABLE_CONSOLE FALSE /*關閉命令行窗口*/#ifdef WITH_FS_RES
#define APP_DEFAULT_FONT "default_full"
#endif /*WITH_FS_RES*/#ifndef APP_RES_ROOT
#define APP_RES_ROOT "./res"
#endif /*APP_RES_ROOT*/#include "awtk_main.inc"

注意事項

  1. 目前只處理了video視頻幀,audio幀沒有處理,libvlc會自動調用windows接口,將視頻里面的聲音播放出來。
  2. 本文適用于已經對AWTK有所熟悉的人,如果有不熟悉AWTK的,需要自行了解一下。

No pains, no gains.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/75784.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/75784.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/75784.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

pulsar中的延遲隊列使用詳解

Apache Pulsar的延遲隊列支持任意時間精度的延遲消息投遞&#xff0c;適用于金融交易、定時提醒等高時效性場景。其核心設計通過堆外內存索引隊列與持久化分片存儲實現&#xff0c;兼顧靈活性與可擴展性。以下從實現原理、使用方式、優化策略及挑戰展開解析&#xff1a; 一、核…

單鏈表的實現 | 附學生信息管理系統的實現

目錄 1.前言&#xff1a; 2.單鏈表的相關概念&#xff1a; 2.1定義&#xff1a; 2.2形式&#xff1a; 2.3特點&#xff1a; 3.常見功能及代碼 &#xff1a; 3.1創建節點&#xff1a; 3.2頭插&#xff1a; 3.3尾插&#xff1a; 3.4頭刪&#xff1a; 3.5尾刪&#xff1a; 3.6插入…

java實用工具類Localstorage

public class LocalStorageUtil {//提供ThreadLocal對象,private static ThreadLocal threadLocalnew ThreadLocal();public static Object get(){return threadLocal.get();}public static void set(Object o){threadLocal.set(o);}public static void remove(){threadLocal.r…

LLM-大語言模型淺談

目錄 核心定義 典型代表 核心原理 用途 優勢與局限 未來發展方向 LLM&#xff08;Large Language Model&#xff09;大語言模型&#xff0c;指通過海量文本數據訓練 能夠理解和生成人類語言的深度學習模型。 核心定義 一種基于深度神經網絡&#xff08;如Transformer架…

【小兔鮮】day03 Home模塊與一級分類

【小兔鮮】day03 Home模塊與一級分類 1. Home-整體結構搭建和分類實現1.1 頁面結構 2. Home-banner輪播圖功能實現 1. Home-整體結構搭建和分類實現 1.1 頁面結構 分類實現 2. Home-banner輪播圖功能實現 輪播圖實現 在HomeBanner.vue中寫出輪播圖的結構 在apis目錄下新建h…

C++中的多態和模板

#include <iostream> #include <cstdlib> #include <ctime> #include <string>using namespace std;// 武器基類 class Weapon { public:virtual ~Weapon() {}virtual string getName() const 0; // 獲取武器名稱virtual int getAtk() const 0; …

Spring 概念

Spring 是一個功能強大、靈活且廣泛使用的 Java 企業級開發框架&#xff0c;它誕生于 2003 年&#xff0c;由 Rod Johnson 創建&#xff0c;初衷是簡化 Java EE 的開發過程。 一、Spring 是什么&#xff1f; 簡單來說&#xff1a; Spring 是一個輕量級的 Java 開發框架&#…

神經網絡之損失函數

引言&#xff1a;損失函數 &#xff08;Loss Function&#xff09;是機器學習和深度學習中非常重要的一個概念。用于衡量模型的預測值與真實值之間的差異&#xff0c;從而指導模型優化其參數以最小化這種差異。 一、損失函數作用 量化誤差&#xff1a;損失函數是將預測值和真實…

Java 基礎-32-枚舉-枚舉的應用場景

在Java編程中&#xff0c;枚舉&#xff08;Enum&#xff09;提供了一種強大的方式來定義一組固定的常量。它們不僅限于簡單的用途&#xff0c;還可以包含構造函數、方法和字段等高級功能&#xff0c;使其適用于多種不同的應用場景。本文將探討幾種常見的使用枚舉的場景&#xf…

【網絡安全】安全的網絡設計

網絡設計是網絡安全的基礎&#xff0c;一個好的網絡設計可以有效的防止攻擊者的入侵。在本篇文章中&#xff0c;我們將詳細介紹如何設計一個安全的網絡&#xff0c;包括網絡架構&#xff0c;網絡設備&#xff0c;網絡策略&#xff0c;以及如何處理網絡安全事件。 一、網絡架構…

網絡安全-等級保護(等保) 0. 前言

各位伙伴好&#xff1a; 招投標總結已過去一年了&#xff0c;時間飛逝&#xff0c;一直忙于工作&#xff0c;等保相關的內容斷斷續續整理了近半年的時間&#xff0c;但一直無暇完成博客內容。 等保已經是一個成熟的體系&#xff0c;現在已進入等保2.0時代&#xff0c;相關政策…

TLS協議詳解

TLS協議 一&#xff0c;TLS協議的組成 TLS協議架構模塊分為兩層&#xff1a;TLS記錄協議&#xff0c;TLS握手協議 ① TLS記錄協議&#xff1a; 是所有子協議的基層&#xff0c;規定了TLS收發數據的基本單位。所有子協議都需要通過記錄協議發出&#xff0c;多個記錄數據可以在…

ollama更新升級及警告解決

1. 概述 在大模型業務處理中&#xff0c;需要用到gemma3 和 qwen2.5-VL&#xff0c;當前服務器的ollama版本 0.3.11&#xff0c;無法滿足要求&#xff0c;需要更新升級。 2. 實施過程 參考官網升級要求&#xff1a; curl -fsSL https://ollama.com/install.sh | sh 不知道…

ubuntu 配置固定ip

在裝服務器系統的時候&#xff0c;DHCP自動獲取ip時&#xff0c;路由可能會重新分配ip&#xff0c;為避免產生影響&#xff0c;可以關閉DHCP將主機設置為靜態ip。 系統環境 Ubuntu 22.04-Desktop 配置方式 一、如果是裝的Ubuntu圖形化&#xff08;就是可以用鼠標操作點擊應用…

套接字編程函數recv和send ,以及設置reuseaddress

recv就是去套接字讀緩沖區讀數據 阻塞模式下&#xff1a;讀緩沖區沒數據那就阻塞等待&#xff0c;若等待被打斷返回-1設置errno為EINTR 非阻塞模式下&#xff1a;讀緩沖區沒數據那就返回-1&#xff0c;設置errno為EAGAIN或EWOULDBLOCK。 若連接斷開返回0&#xff0c;讀取成功…

《C++后端開發最全面試題-從入門到Offer》目錄

當今科技行業對C++開發者的需求持續高漲,從金融科技到游戲開發,從嵌入式系統到高性能計算,C++憑借其卓越的性能和靈活性始終占據著關鍵地位。然而,成為一名優秀的C++工程師并非易事,不僅需要扎實的語言基礎,還要掌握現代C++特性、設計模式、性能優化技巧以及各種工業級開…

設計模式簡述(十)責任鏈模式

責任鏈模式 描述基本使用使用 描述 如果一個請求要經過多個類似或相關處理器的處理。 可以考慮將這些處理器添加到一個鏈上&#xff0c;讓請求逐個經過這些處理器進行處理。 通常&#xff0c;在一個業務場景下會對整個責任鏈進行初始化&#xff0c;確定這個鏈上有哪些Handler…

初識數據結構——Java集合框架解析:List與ArrayList的完美結合

&#x1f4da; Java集合框架解析&#xff1a;List與ArrayList的完美結合 &#x1f31f; 前言&#xff1a;為什么我們需要List和ArrayList&#xff1f; 在日常開發中&#xff0c;我們經常需要處理一組數據。想象一下&#xff0c;如果你要管理一個班級的學生名單&#xff0c;或…

ReFormX:現代化的 React 表單解決方案 - 深度解析與最佳實踐

ReFormX文檔 表單開發一直是前端工作中最繁瑣卻又最常見的任務之一。從簡單的登錄表單到復雜的多步驟配置頁面&#xff0c;開發者往往需要編寫大量重復代碼&#xff0c;處理繁瑣的狀態管理、數據驗證和聯動邏輯。ReFormX 應運而生&#xff0c;它不僅是一個表單組件庫&#xff…

WinForm真入門(9)——RichTextBox控件詳解

WinForm中RichTextBox控件詳解&#xff1a;從基礎到高級應用 上一文中筆者重點介紹了TextBox控件的詳細用法&#xff0c;忘記的 請點擊WinForm真入門(8)——TextBox控件詳解&#xff0c;那么本文中的RichTextBox與TextBox有什么區別嗎&#xff0c;光看名字的話&#xff0c;多了…