当前位置: 首页 > news >正文

图片站wordpress有哪些网站做外贸的

图片站wordpress,有哪些网站做外贸的,android开发,设计网站作品在当今的多媒体世界中#xff0c;视频播放已成为不可或缺的一部分。从简单的媒体播放器到复杂的视频编辑软件#xff0c;视频解码和显示技术无处不在。本示例使用Qt和FFmpeg构建一个简单的视频播放器。利用ffmpeg解码视频#xff0c;通过QWidget渲染解码后的图像#xff0c…          在当今的多媒体世界中视频播放已成为不可或缺的一部分。从简单的媒体播放器到复杂的视频编辑软件视频解码和显示技术无处不在。本示例使用Qt和FFmpeg构建一个简单的视频播放器。利用ffmpeg解码视频通过QWidget渲染解码后的图像支持进度条跳转、进度条显示总时间显示视频基本信息显示。特点 采用软件解码CPU、只解码图像数据主要是演示了ffmpeg的基本使用流程如何通过ffmpeg完成视频解码转换图像像素格式最后完成图像渲染。视频解码采用独立子线程解码后将得到的图像数据通过信号槽发方式传递给UI界面进行渲染。 一、 环境介绍     1、QT版本: QT5.12.6 2、编译器:  MSVC2017 64 3、ffmpeg版本: 6.1.1 4、SDL2 音频播放所需 5、完整工程下载地址(下载即可编译运行): https://download.csdn.net/download/u012959478/89626950 二、实现功能 使用ffmpeg音视频库软解码实现视频播放器支持打开多种本地视频文件如mp4movavi等支持视频匀速播放采用QPainter进行图像显示支持自适应窗口缩放视频播放支持实时开始暂停继续播放采用模块化编程视频解码线程控制图像显示各功能分离低耦合多线程编程 三、实现思路   该视频播放器的主要运行三条线程需要两条队列 线程1(音视频数据分离)使用FFMPEG分解视频文件将视频数据存入到视频队列中将音频数据存入到音频队列中。 线程2(视频解码)从视频队列中获取一包视频数据通过FFMPEG解码该包视频数据解码后再将视频转换为RGB数据最后通过QT的画图显示将视频画面显示出来。 线程3(音频解码)实际该线程由SDL新建它是通过回调的方式来从音频队列中获取音频数据由SDL解码后再进行声音的播放。 四、示例代码   condmutex.h #ifndef CONDMUTEX_H #define CONDMUTEX_H#include SDL.hclass CondMutex { public:CondMutex();~CondMutex();void lock();void unlock();void signal();void broadcast();void wait();private:/** 互斥锁 */SDL_mutex *_mutex nullptr;/** 条件变量 */SDL_cond *_cond nullptr; };#endif // CONDMUTEX_Hcondmutex.cpp  #include condmutex.hCondMutex::CondMutex() {// 创建互斥锁_mutex SDL_CreateMutex();// 创建条件变量_cond SDL_CreateCond(); }CondMutex::~CondMutex() {SDL_DestroyMutex(_mutex);SDL_DestroyCond(_cond); }void CondMutex::lock() {SDL_LockMutex(_mutex); }void CondMutex::unlock() {SDL_UnlockMutex(_mutex); }void CondMutex::signal() {SDL_CondSignal(_cond); }void CondMutex::broadcast() {SDL_CondBroadcast(_cond); }void CondMutex::wait() {SDL_CondWait(_cond, _mutex); }videoslider.h  #ifndef VIDEOSLIDER_H #define VIDEOSLIDER_H#include QSliderclass VideoSlider : public QSlider {Q_OBJECT public:explicit VideoSlider(QWidget *parent nullptr);signals:void clicked(VideoSlider *slider);private:void mousePressEvent(QMouseEvent *ev) override; };#endif // VIDEOSLIDER_Hvideoslider.cpp  #include videoslider.h #include QMouseEvent #include QStyleVideoSlider::VideoSlider(QWidget *parent) : QSlider(parent) {}void VideoSlider::mousePressEvent(QMouseEvent *ev) {// 根据点击位置的x值计算出对应的valueint value QStyle::sliderValueFromPosition(minimum(),maximum(),ev-pos().x(),width());setValue(value);QSlider::mousePressEvent(ev);// 发出信号emit clicked(this); }videowidget.h #ifndef VIDEOWIDGET_H #define VIDEOWIDGET_H#include QWidget #include QImage #include videoplayer.h/*** 显示渲染视频*/ class VideoWidget : public QWidget {Q_OBJECT public:explicit VideoWidget(QWidget *parent nullptr);~VideoWidget();public slots:void onPlayerFrameDecoded(VideoPlayer *player, uint8_t *data, VideoPlayer::VideoSwsSpec spec);void onPlayerStateChanged(VideoPlayer *player);private:QImage *_image nullptr;QRect _rect;void paintEvent(QPaintEvent *event) override;void freeImage(); };#endif // VIDEOWIDGET_Hvideowidget.cpp  #include videowidget.h #include QPainterVideoWidget::VideoWidget(QWidget *parent) : QWidget(parent) {// 设置背景色setAttribute(Qt::WA_StyledBackground);setStyleSheet(background: black); }VideoWidget::~VideoWidget() {freeImage(); }void VideoWidget::onPlayerStateChanged(VideoPlayer *player) {if (player-getState() ! VideoPlayer::Stopped) return;freeImage();update(); }void VideoWidget::onPlayerFrameDecoded(VideoPlayer *player,uint8_t *data, VideoPlayer::VideoSwsSpec spec) {if (player-getState() VideoPlayer::Stopped) return;// 释放之前的图片freeImage();// 创建新的图片if (data ! nullptr) {_image new QImage((uchar *) data,spec.width, spec.height,QImage::Format_RGB888);// 计算最终的尺寸// 组件的尺寸int w width();int h height();// 计算rectint dx 0;int dy 0;int dw spec.width;int dh spec.height;// 计算目标尺寸if (dw w || dh h) { // 缩放if (dw * h w * dh) { // 视频的宽高比 播放器的宽高比dh w * dh / dw;dw w;} else {dw h * dw / dh;dh h;}}// 居中dx (w - dw) 1;dy (h - dh) 1;_rect QRect(dx, dy, dw, dh);}update();//触发paintEvent方法 }void VideoWidget::paintEvent(QPaintEvent *event) {if (!_image) return;// 将图片绘制到当前组件上QPainter(this).drawImage(_rect, *_image); }void VideoWidget::freeImage() {if (_image) {av_free(_image-bits());delete _image;_image nullptr;} }videoplayer.h #ifndef VIDEOPLAYER_H #define VIDEOPLAYER_H#include QObject #include QDebug #include list #include condmutex.hextern C { #include libavcodec/avcodec.h #include libavformat/avformat.h #include libavutil/avutil.h #include libswresample/swresample.h #include libswscale/swscale.h }#define ERROR_BUF \char errbuf[1024]; \av_strerror(ret, errbuf, sizeof (errbuf));#define CODE(func,code) \if (ret 0) { \ERROR_BUF; \qDebug() #func error errbuf; \code; \}#define END(func) CODE(func,fataError(); return;) #define RET(func) CODE(func, return ret;) #define CONTINUE(func) CODE(func, continue;) #define BREAK(func) CODE(func, break;)/*** 预处理视频数据不负责显示、渲染视频*/ class VideoPlayer : public QObject {Q_OBJECT public:// 状态typedef enum {Stopped 0,Playing,Paused} State;// 音量typedef enum {Min 0,Max 100} Volumn;// 视频frame参数typedef struct {int width;int height;AVPixelFormat pixFmt;int size;} VideoSwsSpec;explicit VideoPlayer(QObject *parent nullptr);~VideoPlayer();/** 播放 */void play();/** 暂停 */void pause();/** 停止 */void stop();/** 是否正在播放中 */bool isPlaying();/** 获取当前的状态 */State getState();/** 设置文件名 */void setFilename(QString filename);/** 获取总时长(单位是妙1秒1000毫秒1000000微妙)*/int getDuration();/** 当前的播放时刻单位是秒 */int getTime();/** 设置当前的播放时刻单位是秒 */void setTime(int seekTime);/** 设置音量 */void setVolumn(int volumn);int getVolumn();/** 设置静音 */void setMute(bool mute);bool isMute();signals:void stateChanged(VideoPlayer *player);void timeChanged(VideoPlayer *player);void initFinished(VideoPlayer *player);void playFailed(VideoPlayer *player);void frameDecoded(VideoPlayer *player,uint8_t *data,VideoSwsSpec spec);private:/******** 音频相关 ********/typedef struct {int sampleRate;AVSampleFormat sampleFmt;int chLayout;int chs;int bytesPerSampleFrame;} AudioSwrSpec;/** 解码上下文 */AVCodecContext *_aDecodeCtx nullptr;/** 流 */AVStream *_aStream nullptr;/** 存放音频包的列表 */std::listAVPacket _aPktList;/** 音频包列表的锁 */CondMutex _aMutex;/** 音频重采样上下文 */SwrContext *_aSwrCtx nullptr;/** 音频重采样输入\输出参数 */AudioSwrSpec _aSwrInSpec;AudioSwrSpec _aSwrOutSpec;/** 音频重采样输入\输出frame */AVFrame *_aSwrInFrame nullptr;AVFrame *_aSwrOutFrame nullptr;/** 音频重采样输出PCM的索引从哪个位置开始取出PCM数据填充到SDL的音频缓冲区 */int _aSwrOutIdx 0;/** 音频重采样输出PCM的大小 */int _aSwrOutSize 0;/** 音量 */int _volumn Max;/** 静音 */bool _mute false;/** 音频时钟当前音频包对应的时间值 */double _aTime 0;/** 是否有音频流 */bool _hasAudio false;/** 音频资源是否可以释放 */bool _aCanFree false;/** 外面设置的当前播放时刻用于完成seek功能 */int _aSeekTime -1;/** 初始化音频信息 */int initAudioInfo();/** 初始化SDL */int initSDL();/** 添加数据包到音频包列表中 */void addAudioPkt(AVPacket pkt);/** 清空音频包列表 */void clearAudioPktList();/** SDL填充缓冲区的回调函数 */static void sdlAudioCallbackFunc(void *userdata, Uint8 *stream, int len);/** SDL填充缓冲区的回调函数 */void sdlAudioCallback(Uint8 *stream, int len);/** 音频解码 */int decodeAudio();/** 初始化音频重采样 */int initSwr();/******** 视频相关 ********//** 解码上下文 */AVCodecContext *_vDecodeCtx nullptr;/** 流 */AVStream *_vStream nullptr;/** 像素格式转换的输入\输出frame */AVFrame *_vSwsInFrame nullptr, *_vSwsOutFrame nullptr;/** 像素格式转换的上下文 */SwsContext *_vSwsCtx nullptr;/** 像素格式转换的输出frame的参数 */VideoSwsSpec _vSwsOutSpec;/** 存放视频包的列表 */std::listAVPacket _vPktList;/** 视频包列表的锁 */CondMutex _vMutex;/** 视频时钟当前视频包对应的时间值 */double _vTime 0;/** 是否有视频流 */bool _hasVideo false;/** 视频资源是否可以释放 */bool _vCanFree false;/** 外面设置的当前播放时刻用于完成seek功能 */int _vSeekTime -1;/** 初始化视频信息 */int initVideoInfo();/** 初始化视频像素格式转换 */int initSws();/** 添加数据包到视频包列表中 */void addVideoPkt(AVPacket pkt);/** 清空视频包列表 */void clearVideoPktList();/** 解码视频 */void decodeVideo();/******** 其他 ********//** 当前的状态 */State _state Stopped;/** fmtCtx是否可以释放 */bool _fmtCtxCanFree false;/** 文件名 */QString _filename;// 解封装上下文AVFormatContext *_fmtCtx nullptr;/** 外面设置的当前播放时刻用于完成seek功能 */int _seekTime -1;/** 初始化解码器和解码上下文 */int initDecoder(AVCodecContext **decodeCtx,AVStream **stream,AVMediaType type);/** 改变状态 */void setState(State state);/** 读取文件数据 */void readFile();/** 释放资源 */void free();void freeAudio();void freeVideo();/** 严重错误 */void fataError(); };#endif // VIDEOPLAYER_Hvideoplayer.cpp #include videoplayer.h #include thread#define AUDIO_MAX_PKT_SIZE 1000 #define VIDEO_MAX_PKT_SIZE 500VideoPlayer::VideoPlayer(QObject *parent) : QObject(parent) {// 初始化Audio子系统if (SDL_Init(SDL_INIT_AUDIO)) {// 返回值不是0就代表失败qDebug() SDL_Init error SDL_GetError();emit playFailed(this);return;} }VideoPlayer::~VideoPlayer() {// 不再对外发送消息disconnect();stop();SDL_Quit(); }void VideoPlayer::play() {if (_state Playing) return;// 状态可能是暂停、停止、正常完毕if(_state Stopped){// 开始线程读取文件std::thread([this](){readFile();}).detach();// detach 等到readFile方法执行完这个线程就会销毁}else{setState(Playing);} }void VideoPlayer::pause() {if (_state ! Playing) return;// 状态可能是正在播放setState(Paused); }void VideoPlayer::stop() {if (_state Stopped) return;// 状态可能是正在播放、暂停、正常完毕// 改变状态_state Stopped;// 释放资源free();// 通知外界emit stateChanged(this); }bool VideoPlayer::isPlaying() {return _state Playing; }VideoPlayer::State VideoPlayer::getState() {return _state; }void VideoPlayer::setFilename(QString filename) {_filename filename; }int VideoPlayer::getDuration(){return _fmtCtx ? round(_fmtCtx-duration * av_q2d(AV_TIME_BASE_Q)) : 0; }int VideoPlayer::getTime(){return round(_aTime); }void VideoPlayer::setVolumn(int volumn){_volumn volumn; }void VideoPlayer::setTime(int seekTime){_seekTime seekTime; }int VideoPlayer::getVolumn(){return _volumn; }void VideoPlayer::setMute(bool mute) {_mute mute; }bool VideoPlayer::isMute() {return _mute; }void VideoPlayer::readFile(){ int ret 0;// 创建解封装上下文、打开文件ret avformat_open_input(_fmtCtx,_filename.toUtf8().data(),nullptr,nullptr);END(avformat_open_input);// 检索流信息ret avformat_find_stream_info(_fmtCtx,nullptr);END(avformat_find_stream_info);// 打印流信息到控制台av_dump_format(_fmtCtx,0,_filename.toUtf8().data(),0);fflush(stderr);// 初始化音频信息_hasAudio initAudioInfo() 0;// 初始化视频信息_hasVideo initVideoInfo() 0;if (!_hasAudio !_hasVideo) {emit playFailed(this);free();return;}// 到此为止初始化完毕emit initFinished(this);// 改变状态setState(Playing);// 音频解码子线程开始工作SDL_PauseAudio(0);// 开启新的线程去解码视频数据std::thread([this](){decodeVideo();}).detach();// 从输入文件中读取数据AVPacket pkt;while (_state ! Stopped) {// 处理seek操作if (_seekTime 0) {int streamIdx;if (_hasAudio) { // 优先使用音频流索引streamIdx _aStream-index;} else {streamIdx _vStream-index;}// 现实时间 - 时间戳AVRational timeBase _fmtCtx-streams[streamIdx]-time_base;int64_t ts _seekTime / av_q2d(timeBase);// ret av_seek_frame(_fmtCtx, streamIdx, ts, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME);ret avformat_seek_file(_fmtCtx, streamIdx, INT64_MIN, ts, INT64_MAX, 0);if(ret 0){// seek失败qDebug() seek失败 _seekTime ts streamIdx;_seekTime -1;}else{// seek成功qDebug() seek成功 _seekTime ts streamIdx;// 清空之前读取的数据包clearAudioPktList();clearVideoPktList();_vSeekTime _seekTime;_aSeekTime _seekTime;_seekTime -1;// 恢复时钟_aTime 0;_vTime 0;}}int vSize _vPktList.size();int aSize _aPktList.size();if (vSize VIDEO_MAX_PKT_SIZE || aSize AUDIO_MAX_PKT_SIZE) {SDL_Delay(1);continue;}ret av_read_frame(_fmtCtx, pkt);if (ret 0) {if (pkt.stream_index _aStream-index) { // 读取到的是音频数据addAudioPkt(pkt);} else if (pkt.stream_index _vStream-index) { // 读取到的是视频数据addVideoPkt(pkt);}else{// 如果不是音频、视频流直接释放av_packet_unref(pkt);}} else if (ret AVERROR_EOF) { // 读到了文件的尾部// break;// seek的时候不能用breakif(vSize 0 aSize 0){// 说明文件正常播放完毕_fmtCtxCanFree true;break;}} else {ERROR_BUF;qDebug() av_read_frame error errbuf;continue;}}if (_fmtCtxCanFree) { // 文件正常播放完毕stop();} else {// 标记一下_fmtCtx可以释放了_fmtCtxCanFree true;} }int VideoPlayer::initDecoder(AVCodecContext **decodeCtx,AVStream **stream,AVMediaType type) {// 根据type寻找最合适的流信息// 返回值是流索引int ret av_find_best_stream(_fmtCtx, type, -1, -1, nullptr, 0);RET(av_find_best_stream);// 检验流int streamIdx ret;*stream _fmtCtx-streams[streamIdx];if (!*stream) {qDebug() stream is empty;return -1;}// 为当前流找到合适的解码器const AVCodec *decoder avcodec_find_decoder((*stream)-codecpar-codec_id);if (!decoder) {qDebug() decoder not found (*stream)-codecpar-codec_id;return -1;}// 初始化解码上下文*decodeCtx avcodec_alloc_context3(decoder);if (!decodeCtx) {qDebug() avcodec_alloc_context3 error;return -1;}// 从流中拷贝参数到解码上下文中ret avcodec_parameters_to_context(*decodeCtx, (*stream)-codecpar);RET(avcodec_parameters_to_context);// 打开解码器ret avcodec_open2(*decodeCtx, decoder, nullptr);RET(avcodec_open2);return 0; }void VideoPlayer::setState(State state) {if (state _state) return;_state state;emit stateChanged(this); }void VideoPlayer::free(){while (_hasAudio !_aCanFree);while (_hasVideo !_vCanFree);while (!_fmtCtxCanFree);avformat_close_input(_fmtCtx);_fmtCtxCanFree false;_seekTime -1;freeAudio();freeVideo(); }void VideoPlayer::fataError(){setState(Stopped);free();emit playFailed(this); }videoplayer_audio.cpp #include videoplayer.h// 初始化音频信息 int VideoPlayer::initAudioInfo() {int ret initDecoder(_aDecodeCtx,_aStream,AVMEDIA_TYPE_AUDIO);RET(initDecoder);// 初始化音频重采样ret initSwr();RET(initSwr);// 初始化SDLret initSDL();RET(initSDL);return 0; }int VideoPlayer::initSwr() {// 重采样输入参数_aSwrInSpec.sampleFmt _aDecodeCtx-sample_fmt;_aSwrInSpec.sampleRate _aDecodeCtx-sample_rate;_aSwrInSpec.chLayout _aDecodeCtx-channel_layout;_aSwrInSpec.chs _aDecodeCtx-channels;// 重采样输出参数_aSwrOutSpec.sampleFmt AV_SAMPLE_FMT_S16;_aSwrOutSpec.sampleRate 44100;_aSwrOutSpec.chLayout AV_CH_LAYOUT_STEREO;_aSwrOutSpec.chs av_get_channel_layout_nb_channels(_aSwrOutSpec.chLayout);_aSwrOutSpec.bytesPerSampleFrame _aSwrOutSpec.chs * av_get_bytes_per_sample(_aSwrOutSpec.sampleFmt);// 创建重采样上下文_aSwrCtx swr_alloc_set_opts(nullptr,// 输出参数_aSwrOutSpec.chLayout,_aSwrOutSpec.sampleFmt,_aSwrOutSpec.sampleRate,// 输入参数_aSwrInSpec.chLayout,_aSwrInSpec.sampleFmt,_aSwrInSpec.sampleRate,0, nullptr);if (!_aSwrCtx) {qDebug() swr_alloc_set_opts error;return -1;}// 初始化重采样上下文int ret swr_init(_aSwrCtx);RET(swr_init);// 初始化重采样的输入frame_aSwrInFrame av_frame_alloc();if (!_aSwrInFrame) {qDebug() av_frame_alloc error;return -1;}// 初始化重采样的输出frame_aSwrOutFrame av_frame_alloc();if (!_aSwrOutFrame) {qDebug() av_frame_alloc error;return -1;}// 初始化重采样的输出frame的data[0]空间ret av_samples_alloc(_aSwrOutFrame-data,_aSwrOutFrame-linesize,_aSwrOutSpec.chs,4096, _aSwrOutSpec.sampleFmt, 1);RET(av_samples_alloc);return 0; }void VideoPlayer::freeAudio(){_aSwrOutIdx 0;_aSwrOutSize 0;_aTime 0;_aCanFree false;_aSeekTime -1;clearAudioPktList();avcodec_free_context(_aDecodeCtx);swr_free(_aSwrCtx);av_frame_free(_aSwrInFrame);if(_aSwrOutFrame){av_freep(_aSwrOutFrame-data[0]);// 因手动创建了data[0]的空间av_frame_free(_aSwrOutFrame);}// 停止播放SDL_PauseAudio(1);SDL_CloseAudio(); }void VideoPlayer::sdlAudioCallbackFunc(void *userdata, uint8_t *stream, int len){VideoPlayer *player (VideoPlayer *)userdata;player-sdlAudioCallback(stream,len); }int VideoPlayer::initSDL(){// 音频参数SDL_AudioSpec spec;// 采样率spec.freq _aSwrOutSpec.sampleRate;// 采样格式s16lespec.format AUDIO_S16LSB;// 声道数spec.channels _aSwrOutSpec.chs;// 音频缓冲区的样本数量这个值必须是2的幂spec.samples 512;// 回调spec.callback sdlAudioCallbackFunc;// 传递给回调的参数spec.userdata this;// 打开音频设备if (SDL_OpenAudio(spec, nullptr)) {qDebug() SDL_OpenAudio error SDL_GetError();return -1;}return 0; }void VideoPlayer::addAudioPkt(AVPacket pkt){_aMutex.lock();_aPktList.push_back(pkt);_aMutex.signal();_aMutex.unlock(); }void VideoPlayer::clearAudioPktList(){_aMutex.lock();for(AVPacket pkt : _aPktList){av_packet_unref(pkt);}_aPktList.clear();_aMutex.unlock(); }void VideoPlayer::sdlAudioCallback(Uint8 *stream, int len){// 清零静音SDL_memset(stream, 0, len);// lenSDL音频缓冲区剩余的大小还未填充的大小while (len 0) {if (_state Paused) break;if (_state Stopped) {_aCanFree true;break;}// 说明当前PCM的数据已经全部拷贝到SDL的音频缓冲区了// 需要解码下一个pkt获取新的PCM数据if (_aSwrOutIdx _aSwrOutSize) {// 全新PCM的大小_aSwrOutSize decodeAudio();// 索引清0_aSwrOutIdx 0;// 没有解码出PCM数据那就静音处理if (_aSwrOutSize 0) {// 假定PCM的大小_aSwrOutSize 1024;// 给PCM填充0静音memset(_aSwrOutFrame-data[0], 0, _aSwrOutSize);}}// 本次需要填充到stream中的PCM数据大小int fillLen _aSwrOutSize - _aSwrOutIdx;fillLen std::min(fillLen, len);// 获取当前音量int volumn _mute ? 0 : ((_volumn * 1.0 / Max) * SDL_MIX_MAXVOLUME);// 填充SDL缓冲区SDL_MixAudio(stream,_aSwrOutFrame-data[0] _aSwrOutIdx,fillLen, volumn);// 移动偏移量len - fillLen;stream fillLen;_aSwrOutIdx fillLen;} }/*** brief VideoPlayer::decodeAudio* return 解码出来的pcm大小*/ int VideoPlayer::decodeAudio(){// 加锁_aMutex.lock();if (_aPktList.empty() || _state Stopped) {_aMutex.unlock();return 0;}// 取出头部的数据包AVPacket pkt _aPktList.front();// 从头部中删除_aPktList.pop_front();// 解锁_aMutex.unlock();// 保存音频时钟if (pkt.pts ! AV_NOPTS_VALUE) {_aTime av_q2d(_aStream-time_base) *pkt.pts;// 通知外界播放时间点发生了改变emit timeChanged(this);}// 如果是视频不能在这个位置判断不能提前释放pkt不然会导致B帧、P帧解码失败画面撕裂// 发现音频的时间是早于seekTime的直接丢弃if (_aSeekTime 0) {if (_aTime _aSeekTime) {// 释放pktav_packet_unref(pkt);return 0;} else {_aSeekTime -1;}}// 发送压缩数据到解码器int ret avcodec_send_packet(_aDecodeCtx, pkt);// 释放pktav_packet_unref(pkt);RET(avcodec_send_packet);// 获取解码后的数据ret avcodec_receive_frame(_aDecodeCtx, _aSwrInFrame);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) {return 0;} else RET(avcodec_receive_frame);// 重采样输出的样本数int outSamples av_rescale_rnd(_aSwrOutSpec.sampleRate,_aSwrInFrame-nb_samples,_aSwrInSpec.sampleRate, AV_ROUND_UP);// 由于解码出来的PCM。跟SDL要求的PCM格式可能不一致需要进行重采样ret swr_convert(_aSwrCtx,_aSwrOutFrame-data,outSamples,(const uint8_t **) _aSwrInFrame-data,_aSwrInFrame-nb_samples);RET(swr_convert);return ret * _aSwrOutSpec.bytesPerSampleFrame; }videoplayer_video.cpp #include videoplayer.h #include thread extern C { #include libavutil/imgutils.h }// 初始化视频信息 int VideoPlayer::initVideoInfo() {int ret initDecoder(_vDecodeCtx,_vStream,AVMEDIA_TYPE_VIDEO);RET(initDecoder);// 初始化像素格式转换ret initSws();RET(initSws);return 0; }int VideoPlayer::initSws(){int inW _vDecodeCtx-width;int inH _vDecodeCtx-height;// 输出frame的参数_vSwsOutSpec.width inW 4 4;// 先除以16在乘以16保证是16的倍数_vSwsOutSpec.height inH 4 4;_vSwsOutSpec.pixFmt AV_PIX_FMT_RGB24;_vSwsOutSpec.size av_image_get_buffer_size(_vSwsOutSpec.pixFmt,_vSwsOutSpec.width,_vSwsOutSpec.height, 1);// 初始化像素格式转换的上下文_vSwsCtx sws_getContext(inW,inH,_vDecodeCtx-pix_fmt,_vSwsOutSpec.width,_vSwsOutSpec.height,_vSwsOutSpec.pixFmt,SWS_BILINEAR, nullptr, nullptr, nullptr);if (!_vSwsCtx) {qDebug() sws_getContext error;return -1;}// 初始化像素格式转换的输入frame_vSwsInFrame av_frame_alloc();if (!_vSwsInFrame) {qDebug() av_frame_alloc error;return -1;}// 初始化像素格式转换的输出frame_vSwsOutFrame av_frame_alloc();if (!_vSwsOutFrame) {qDebug() av_frame_alloc error;return -1;}// _vSwsOutFrame的data[0]指向的内存空间int ret av_image_alloc(_vSwsOutFrame-data,_vSwsOutFrame-linesize,_vSwsOutSpec.width,_vSwsOutSpec.height,_vSwsOutSpec.pixFmt,1);RET(av_image_alloc);return 0; }void VideoPlayer::addVideoPkt(AVPacket pkt){_vMutex.lock();_vPktList.push_back(pkt);_vMutex.signal();_vMutex.unlock(); }void VideoPlayer::clearVideoPktList(){_vMutex.lock();for(AVPacket pkt : _vPktList){av_packet_unref(pkt);}_vPktList.clear();_vMutex.unlock(); }void VideoPlayer::freeVideo(){clearVideoPktList();avcodec_free_context(_vDecodeCtx);av_frame_free(_vSwsInFrame);if (_vSwsOutFrame) {av_freep(_vSwsOutFrame-data[0]);av_frame_free(_vSwsOutFrame);}sws_freeContext(_vSwsCtx);_vSwsCtx nullptr;_vStream nullptr;_vTime 0;_vCanFree false;_vSeekTime -1; }void VideoPlayer::decodeVideo(){while (true) {// 如果是暂停并且没有Seek操作if (_state Paused _vSeekTime -1) {continue;}if (_state Stopped) {_vCanFree true;break;}_vMutex.lock();if(_vPktList.empty()){_vMutex.unlock();continue;}// 取出头部的视频包AVPacket pkt _vPktList.front();_vPktList.pop_front();_vMutex.unlock();// 视频时钟if (pkt.dts ! AV_NOPTS_VALUE) {_vTime av_q2d(_vStream-time_base) * pkt.dts;}// 发送压缩数据到解码器int ret avcodec_send_packet(_vDecodeCtx, pkt);// 释放pktav_packet_unref(pkt);CONTINUE(avcodec_send_packet);while (true) {ret avcodec_receive_frame(_vDecodeCtx, _vSwsInFrame);if (ret AVERROR(EAGAIN) || ret AVERROR_EOF) {break;} else BREAK(avcodec_receive_frame);// 一定要在解码成功后再进行下面的判断// 发现视频的时间是早于seekTime的直接丢弃if(_vSeekTime 0){if (_vTime _vSeekTime) {continue;// 丢掉} else {_vSeekTime -1;}}// 像素格式的转换sws_scale(_vSwsCtx,_vSwsInFrame-data, _vSwsInFrame-linesize,0, _vDecodeCtx-height,_vSwsOutFrame-data, _vSwsOutFrame-linesize);if(_hasAudio){// 有音频// 如果视频包过早被解码出来那就需要等待对应的音频时钟到达while (_vTime _aTime _state Playing) {SDL_Delay(1);}}uint8_t *data (uint8_t *)av_malloc(_vSwsOutSpec.size);memcpy(data, _vSwsOutFrame-data[0], _vSwsOutSpec.size);// 发出信号emit frameDecoded(this,data,_vSwsOutSpec);qDebug() 渲染了一帧 _vTime _aTime;}} }界面设计mainwindow.ui mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H#include QMainWindow #include videoplayer.h #include videoslider.hQT_BEGIN_NAMESPACE namespace Ui {class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr);~MainWindow();private slots:void onPlayerStateChanged(VideoPlayer *player);void onPlayerTimeChanged(VideoPlayer *player);void onPlayerInitFinished(VideoPlayer *player);void onPlayerPlayFailed(VideoPlayer *player);void onSliderClicked(VideoSlider *slider);void on_stopBtn_clicked();void on_openFileBtn_clicked();void on_currentSlider_valueChanged(int value);void on_volumnSlider_valueChanged(int value);void on_playBtn_clicked();void on_muteBtn_clicked();private:Ui::MainWindow *ui;VideoPlayer *_player;QString getTimeText(int value); }; #endif // MAINWINDOW_Hmainwindow.cpp #include mainwindow.h #include ui_mainwindow.h #include QFileDialog #include QMessageBox#define FILEPATH ../test/MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui-setupUi(this);// 注册信号的参数类型保证能够发出信号qRegisterMetaTypeVideoPlayer::VideoSwsSpec(VideoSwsSpec);// 创建播放器_player new VideoPlayer();connect(_player, VideoPlayer::stateChanged,this, MainWindow::onPlayerStateChanged);connect(_player, VideoPlayer::timeChanged,this, MainWindow::onPlayerTimeChanged);connect(_player, VideoPlayer::initFinished,this, MainWindow::onPlayerInitFinished);connect(_player, VideoPlayer::playFailed,this, MainWindow::onPlayerPlayFailed);connect(_player, VideoPlayer::frameDecoded,ui-videoWidget, VideoWidget::onPlayerFrameDecoded);connect(_player, VideoPlayer::stateChanged,ui-videoWidget, VideoWidget::onPlayerStateChanged);// 监听时间滑块的点击connect(ui-currentSlider, VideoSlider::clicked,this, MainWindow::onSliderClicked);// 设置音量滑块的范围ui-volumnSlider-setRange(VideoPlayer::Volumn::Min,VideoPlayer::Volumn::Max);ui-volumnSlider-setValue(ui-volumnSlider-maximum() 2); }MainWindow::~MainWindow() {delete ui;delete _player; }void MainWindow::onSliderClicked(VideoSlider *slider) {_player-setTime(slider-value()); }void MainWindow::onPlayerPlayFailed(VideoPlayer *player) {QMessageBox::critical(nullptr,提示,播放失败); }void MainWindow::onPlayerTimeChanged(VideoPlayer *player) {ui-currentSlider-setValue(player-getTime()); }void MainWindow::onPlayerInitFinished(VideoPlayer *player) {int duration player-getDuration();qDebug() duration;// 设置一些slider的范围ui-currentSlider-setRange(0,duration);// 设置label的文字ui-durationLabel-setText(getTimeText(duration)); }/*** onPlayerStateChanged方法的发射虽然在子线程中执行(VideoPlayer::readFile())* 但是此方法是在主线程执行因为它的connect是在主线程执行的*/ void MainWindow::onPlayerStateChanged(VideoPlayer *player) {VideoPlayer::State state player-getState();if (state VideoPlayer::Playing) {ui-playBtn-setText(暂停);} else {ui-playBtn-setText(播放);}if (state VideoPlayer::Stopped) {ui-playBtn-setEnabled(false);ui-stopBtn-setEnabled(false);ui-currentSlider-setEnabled(false);ui-volumnSlider-setEnabled(false);ui-muteBtn-setEnabled(false);ui-durationLabel-setText(getTimeText(0));ui-currentSlider-setValue(0);// 显示打开文件的页面ui-playWidget-setCurrentWidget(ui-openFilePage);} else {ui-playBtn-setEnabled(true);ui-stopBtn-setEnabled(true);ui-currentSlider-setEnabled(true);ui-volumnSlider-setEnabled(true);ui-muteBtn-setEnabled(true);// 显示播放视频的页面ui-playWidget-setCurrentWidget(ui-videoPage);} }void MainWindow::on_stopBtn_clicked() {_player-stop(); }void MainWindow::on_openFileBtn_clicked() {QString filename QFileDialog::getOpenFileName(nullptr,选择多媒体文件,FILEPATH,多媒体文件 (*.mp4 *.avi *.mkv *.mp3 *.aac));qDebug() 打开文件 filename;if (filename.isEmpty()) return;// 开始播放打开的文件_player-setFilename(filename);_player-play(); }void MainWindow::on_currentSlider_valueChanged(int value) {ui-currentLabel-setText(getTimeText(value)); }void MainWindow::on_volumnSlider_valueChanged(int value) {ui-volumnLabel-setText(QString(%1).arg(value));_player-setVolumn(value); }void MainWindow::on_playBtn_clicked() {VideoPlayer::State state _player-getState();if (state VideoPlayer::Playing) {_player-pause();} else {_player-play();} }QString MainWindow::getTimeText(int value){QString h QString(0%1).arg(value / 3600).right(2);QString m QString(0%1).arg((value / 60) % 60).right(2);QString s QString(0%1).arg(value % 60).right(2);return QString(%1:%2:%3).arg(h).arg(m).arg(s); }void MainWindow::on_muteBtn_clicked() {if (_player-isMute()) {_player-setMute(false);ui-muteBtn-setText(静音);} else {_player-setMute(true);ui-muteBtn-setText(开音);} }通过以上的实现我们就可以得到一个简单的录音软件它可以利用QT实现录音使用ffmpeg进行音频重采样并使用fdk-aac进行编码。这个录音软件不仅简单易用可以帮助我们记录和存储语音信息是一个非常实用的工具。 五、运行效果 ​​​​​​​​ 谢谢您的阅读。希望本文能对您有所帮助并且给您带来了一些新的观点和思考。如果您有任何问题或意见请随时与我联系。再次感谢您的支持 六、相关文章 Windosw下Visual Studio2022编译FFmpeg(支持x264、x265、fdk-acc)-CSDN博客
http://www.hkea.cn/news/14390792/

相关文章:

  • 网站设计制作系统哪个好wordpress 图文
  • 东港区建设局网站seo专员是什么职位
  • 小语种网站怎么设计沈阳专业做网站
  • 手机资讯网站源码建设企业网站管理的重要性
  • 学院网站建设的目的WordPress邮箱验证 注册
  • 宿松做网站策划运营
  • 做一个15页的网站怎么做如何做好网络营销工作
  • 网站建设网站推广网站建设各模块功能简述
  • 沧州做家装的公司网站邯郸网络用语
  • 北京海淀建设局搜索引擎优化的流程
  • 常州模板网站建设喜欢做木工 网站
  • 建站专家商贸公司网站建设极致发烧
  • 无锡开发网站建设o2o有哪些电商平台
  • 哪个商城网站建设好中国建设银行上海分行信息网站
  • 摄影师做展示的网站中国猎头公司前十名
  • 成功的微网站广州网站建设推广易尚
  • wordpress音乐网站主题个人网站icp
  • 上海网站建设 paiky图片处理软件
  • 舟山市普陀区建设局网站国外免费服务器ip大全
  • wordpress 网站禁用全屏代码中国房地产未来走向
  • 信阳网站建设哪家好上海闵行区邮编
  • 网站说服力-营销型网站策划网站开发开票交税
  • 什么网站做展板的多wordpress 电子书主题
  • 安全可信网站网站建设九亭
  • 明星粉丝网站怎么做wordpress 网站白屏
  • 色弱做网站都匀网站开发的公司
  • 天津建设网站安全员考试成绩查询苏州市城乡和建设局网站
  • 站酷设计网站官网入口免费云存储wordpress
  • 上海哪家网站建设好平台公司组织架构
  • 鳌江哪里有做网站wordpress ftp主机