SDL实现生产者消费者

环境:

阿里云主机

CPU 1     内存: 1GB    数据盘:  20G

操作系统: Ubuntu 12.04 64

 

1.       SDL安装

[root@iZ25ml7xjj1Z:/usr/lixian]$sudo aptitude install libsdl1.2debian

 

2.       生产者消费者源代码

/*************************************************

Name:        producerConsumer.cpp

Author:      Xian Li

Created:      2014-11-2

Compiler:     g++

Environment:  Aliyun host Ubuntu 12.04 64

*************************************************/

 

 

#include <stdio.h> 

#include <SDL/SDL.h> 

#include <SDL/SDL_thread.h> 

#include <unistd.h>

 

#define BUFFER_LEN 4   //缓冲区队列长度

 

SDL_mutex *mutex;       //互斥锁变量

SDL_cond *cond;             //条件变量

 

int buffer[BUFFER_LEN];  //缓冲区

 

int head = 0,tail = 0;     //队列头指针和尾指针

 

int productID = 1;      //产品ID

int productCount = 0;   //队列中产品计数

 

static int producerThread(void *ptr){ 

    while(1){ 

        if (SDL_LockMutex(mutex) == 0) {    //获取锁,进入临界区

         while(BUFFER_LEN==productCount) //如果缓冲区已满,释放锁,并等待缓冲区出现空闲空间

             {

                   SDL_CondWait(cond, mutex);

                printf("Buffer is full, producer is waiting......");

             }

 

             buffer[head] = productID;

            head = (head+1)%BUFFER_LEN;

            printf("Producer has produced a product %d\n", productID); 

             

                 productID++;

                 productCount++;

            SDL_CondSignal(cond); 

            SDL_UnlockMutex(mutex); 

            sleep(rand()%5); 

 

        } else { 

            fprintf(stderr, "Couldn't lock mutex\n"); 

        } 

         

    } 

  

    return 0; 

 

static int consumerThread(void *ptr){ 

    while(1){ 

        if (SDL_LockMutex(mutex) == 0)

         { 

            while(0==productCount){  //如果缓冲区为空,释放锁,并等待缓冲区非空

                  SDL_CondWait(cond, mutex); 

                             printf("\t\t Buffer is empty, consumer is waiting......\n");

            } 

            printf("\t\t Consumer consumed product %d\n", buffer[tail]); 

            tail = (tail+1)%BUFFER_LEN;

                 productCount--;

                 SDL_CondSignal(cond); 

            SDL_UnlockMutex(mutex); 

            sleep(rand()%5); 

 

        }

         else

         { 

            fprintf(stderr, "Couldn't lock mutex\n"); 

        } 

         

    } 

    return 0; 

 

int main(int argc, char *argv[]) 

 

    mutex = SDL_CreateMutex();    //申请锁变量

    if (!mutex) { 

        fprintf(stderr, "Couldn't create mutex\n"); 

        return -1; 

    } 

 

    cond = SDL_CreateCond();     //申请条件变量

    if (!cond) { 

        fprintf(stderr, "Couldn't create cond\n"); 

        return -1; 

    } 

 

    SDL_Thread *threadA; 

    SDL_Thread *threadB; 

    int         threadReturnValueA; 

    int         threadReturnValueB; 

 

 

    threadA = SDL_CreateThread(producerThread, (void *)NULL); 

    if (NULL == threadA) { 

        printf("\nSDL_CreateThread failed: %s\n", SDL_GetError()); 

    } 

     

    threadB = SDL_CreateThread(consumerThread, (void *)NULL); 

    if (NULL == threadB) { 

        printf("\nSDL_CreateThread failed: %s\n", SDL_GetError()); 

    } 

      

    SDL_WaitThread(threadA, &threadReturnValueA); 

    SDL_WaitThread(threadB, &threadReturnValueB); 

 

    return 0; 

 

3.       编译、连接,生成可执行文件。

[root@iZ25ml7xjj1Z:/usr/lixian]$g++ -o exc producerConsumer.cpp –lSDL

 

4.         运行,结果如下:
SDL_producer_consumer.png
<think>我们参考了用户提供的引用资料,并结合C++视频播放器开发的一般流程,总结出以下实现步骤: 1. **需求分析设计** - 确定播放器功能:文件播放、网络流播放、播放控制(播放/暂停/停止/音量/进度条)等 - 选择技术栈:如FFmpeg用于解码,SDL/VLC/OpenGL用于渲染,Qt用于界面等[^2][^3] - 设计架构:采用模块化设计,将解码、渲染、界面分离[^3] 2. **环境配置** - 安装FFmpeg库:处理视频解码 - 安装界面库:Qt/SDL等 - 安装音频库:如OpenAL/SDL_mixer 3. **核心模块实现** **3.1 视频解码模块(使用FFmpeg)** ```cpp // 初始化FFmpeg av_register_all(); AVFormatContext* formatCtx = avformat_alloc_context(); // 打开视频文件 if(avformat_open_input(&formatCtx, filename, NULL, NULL) != 0) { // 错误处理 } // 获取流信息 avformat_find_stream_info(formatCtx, NULL); // 查找视频流 int videoStream = -1; for(int i=0; i<formatCtx->nb_streams; i++) { if(formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } // 获取解码器 AVCodecContext* codecCtx = formatCtx->streams[videoStream]->codec; AVCodec* codec = avcodec_find_decoder(codecCtx->codec_id); // 打开解码器 avcodec_open2(codecCtx, codec, NULL); ``` **3.2 视频渲染模块** - 使用SDL渲染示例: ```cpp SDL_Init(SDL_INIT_VIDEO); SDL_Window* screen = SDL_CreateWindow("Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, codecCtx->width, codecCtx->height, 0); SDL_Renderer* renderer = SDL_CreateRenderer(screen, -1, 0); SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, codecCtx->width, codecCtx->height); ``` **3.3 音频处理模块** ```cpp // 查找音频流并初始化音频设备 SDL_AudioSpec wanted_spec, spec; wanted_spec.freq = codecCtx->sample_rate; wanted_spec.format = AUDIO_S16SYS; wanted_spec.channels = codecCtx->channels; wanted_spec.silence = 0; wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; wanted_spec.callback = audio_callback; // 音频回调函数 SDL_OpenAudio(&wanted_spec, &spec); ``` 4. **播放控制实现** ```cpp // 主播放循环 AVPacket packet; while(av_read_frame(formatCtx, &packet) >=0) { if(packet.stream_index == videoStream) { // 视频解码 AVFrame* frame = av_frame_alloc(); int frameFinished; avcodec_decode_video2(codecCtx, frame, &frameFinished, &packet); if(frameFinished) { // 渲染视频帧 SDL_UpdateYUVTexture(texture, NULL, frame->data[0], frame->linesize[0], frame->data[1], frame->linesize[1], frame->data[2], frame->linesize[2]); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } av_frame_free(&frame); } av_packet_unref(&packet); } ``` 5. **用户界面开发(使用Qt)** ```cpp // 创建主窗口 QMainWindow window; QVideoWidget *videoWidget = new QVideoWidget; QMediaPlayer *player = new QMediaPlayer; player->setVideoOutput(videoWidget); window.setCentralWidget(videoWidget); // 添加控制按钮 QToolBar *toolBar = new QToolBar; QAction *playAction = new QAction("Play", &window); QObject::connect(playAction, &QAction::triggered, player, &QMediaPlayer::play); toolBar->addAction(playAction); window.addToolBar(toolBar); ``` 6. **高级功能实现** - 网络流播放:支持RTSP[^2] ```cpp avformat_open_input(&pFormatCtx, "rtsp://example.com/stream", NULL, NULL); ``` - 多线程解码:使用生产者-消费者模型 - 硬件加速:通过FFmpeg启用硬件解码 ```cpp codecCtx->get_format = get_hw_format; // 设置硬件解码格式 ``` 7. **性能优化** - 帧缓冲池:减少内存分配开销 - 异步I/O:避免阻塞主线程 - 零拷贝渲染:直接使用GPU内存 8. **跨平台适配** - Windows:DirectShow/DirectDraw[^2] - Linux:V4L2/VAAPI - macOS:VideoToolbox 9. **测试调试** - 不同格式视频测试 - 压力测试:长时间播放测试 - 内存泄漏检测 10. **打包发布** - Windows:生成安装包 - Linux:打包deb/rpm - macOS:创建dmg §§相关问题§§ 1. FFmpeg解码的具体流程是怎样的? 2. 如何实现视频播放器的进度条控制? 3. 视频播放器开发中常见的性能问题有哪些?</think># C++视频播放器开发实现步骤 我将基于引用资料,详细介绍使用C++开发视频播放器的完整实现步骤。视频播放器开发涉及多个关键技术模块,需要综合运用多媒体处理和图形界面技术[^1]。 ## 一、开发准备 ### 1. 技术选型 - **核心解码库**:FFmpeg(跨平台音视频解码) - **渲染引擎**: - DirectDraw(Windows) - OpenGL/Vulkan(跨平台) - Qt多媒体模块(跨平台) - **界面框架**: - Qt(跨平台GUI) - SDL(简单媒体层) - **网络协议**:RTSP/RTP(实时流媒体传输)[^2] ### 2. 环境配置 ```bash # 安装FFmpeg开发库 sudo apt-get install ffmpeg libavcodec-dev libavformat-dev libswscale-dev # Qt开发环境 sudo apt-get install qtbase5-dev qtmultimedia5-dev ``` ## 二、核心模块实现步骤 ### 1. FFmpeg初始化视频解码 ```cpp #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> void initFFmpeg() { av_register_all(); avformat_network_init(); } bool openVideoFile(const char* filename) { AVFormatContext* formatContext = nullptr; if (avformat_open_input(&formatContext, filename, nullptr, nullptr) != 0) { return false; } if (avformat_find_stream_info(formatContext, nullptr) < 0) { return false; } // 查找视频流 int videoStream = -1; for (int i = 0; i < formatContext->nb_streams; i++) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; break; } } // 获取解码器 AVCodecParameters* codecParams = formatContext->streams[videoStream]->codecpar; AVCodec* codec = avcodec_find_decoder(codecParams->codec_id); AVCodecContext* codecContext = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codecContext, codecParams); // 打开解码器 if (avcodec_open2(codecContext, codec, nullptr) < 0) { return false; } // 存储解码上下文... return true; } ``` ### 2. 视频帧处理格式转换 ```cpp void decodeFrames(AVCodecContext* codecContext, AVFormatContext* formatContext) { AVPacket packet; AVFrame* frame = av_frame_alloc(); while (av_read_frame(formatContext, &packet) >= 0) { if (packet.stream_index == videoStreamIndex) { int response = avcodec_send_packet(codecContext, &packet); if (response < 0) break; while (response >= 0) { response = avcodec_receive_frame(codecContext, frame); if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) { break; } else if (response < 0) { // 错误处理 break; } // 转换为RGB格式 AVFrame* rgbFrame = convertToRGB(frame); // 渲染帧 renderFrame(rgbFrame); av_frame_unref(rgbFrame); } } av_packet_unref(&packet); } av_frame_free(&frame); } ``` ### 3. 视频渲染(DirectDraw示例) ```cpp #include <ddraw.h> class VideoRenderer { public: bool init(HWND hwnd, int width, int height) { // 初始化DirectDraw DirectDrawCreateEx(nullptr, (LPVOID*)&lpDD, IID_IDirectDraw7, nullptr); lpDD->SetCooperativeLevel(hwnd, DDSCL_NORMAL); // 创建主表面 DDSURFACEDESC2 ddsd; ZeroMemory(&ddsd, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_CAPS; ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; lpDD->CreateSurface(&ddsd, &lpPrimary, nullptr); // 创建离屏表面 ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; ddsd.dwWidth = width; ddsd.dwHeight = height; lpDD->CreateSurface(&ddsd, &lpBack, nullptr); return true; } void renderFrame(AVFrame* frame) { // 锁定表面 DDSURFACEDESC2 ddsd; ZeroMemory(&ddsd, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd); lpBack->Lock(nullptr, &ddsd, DDLOCK_WAIT, nullptr); // 复制像素数据 uint8_t* dest = (uint8_t*)ddsd.lpSurface; for (int y = 0; y < height; y++) { memcpy(dest, frame->data[0] + y * frame->linesize[0], width * 3); dest += ddsd.lPitch; } // 解锁表面 lpBack->Unlock(nullptr); // 复制到主表面 RECT destRect = {0, 0, width, height}; lpPrimary->Blt(&destRect, lpBack, nullptr, DDBLT_WAIT, nullptr); } private: LPDIRECTDRAW7 lpDD = nullptr; LPDIRECTDRAWSURFACE7 lpPrimary = nullptr; LPDIRECTDRAWSURFACE7 lpBack = nullptr; int width, height; }; ``` ### 4. Qt界面播放控制 ```cpp #include <QMediaPlayer> #include <QVideoWidget> #include <QPushButton> class VideoPlayer : public QWidget { public: VideoPlayer(QWidget* parent = nullptr) : QWidget(parent) { // 创建界面组件 QVBoxLayout* layout = new QVBoxLayout(this); videoWidget = new QVideoWidget; player = new QMediaPlayer; // 设置播放器 player->setVideoOutput(videoWidget); // 控制按钮 QPushButton* playButton = new QPushButton("播放"); QPushButton* pauseButton = new QPushButton("暂停"); QPushButton* stopButton = new QPushButton("停止"); // 连接信号槽 connect(playButton, &QPushButton::clicked, player, &QMediaPlayer::play); connect(pauseButton, &QPushButton::clicked, player, &QMediaPlayer::pause); connect(stopButton, &QPushButton::clicked, player, &QMediaPlayer::stop); // 布局 layout->addWidget(videoWidget); QHBoxLayout* controlLayout = new QHBoxLayout; controlLayout->addWidget(playButton); controlLayout->addWidget(pauseButton); controlLayout->addWidget(stopButton); layout->addLayout(controlLayout); } void openFile(const QString& filePath) { player->setMedia(QUrl::fromLocalFile(filePath)); } private: QMediaPlayer* player; QVideoWidget* videoWidget; }; ``` ### 5. RTSP流媒体支持[^2] ```cpp bool openRTSPStream(const char* url) { AVFormatContext* formatContext = avformat_alloc_context(); AVDictionary* options = nullptr; // 设置RTSP选项 av_dict_set(&options, "rtsp_transport", "tcp", 0); av_dict_set(&options, "stimeout", "5000000", 0); // 5秒超时 // 打开流 if (avformat_open_input(&formatContext, url, nullptr, &options) != 0) { return false; } // 获取流信息 if (avformat_find_stream_info(formatContext, nullptr) < 0) { return false; } // 后续处理文件播放类似... return true; } ``` ## 三、高级功能实现 ### 1. 多线程解码架构 ```mermaid graph TD A[主线程] --> B[UI事件处理] A --> C[播放控制] D[解码线程] --> E[读取数据包] D --> F[解码视频帧] D --> G[解码音频帧] H[渲染线程] --> I[视频渲染] H --> J[音频输出] C --> D F --> H G --> J ``` ### 2. 播放器架构设计[^3] ```cpp class IVideoDecoder { public: virtual bool open(const QString& source) = 0; virtual void close() = 0; virtual AVFrame* getNextFrame() = 0; virtual double getDuration() const = 0; virtual double getPosition() const = 0; virtual bool seek(double position) = 0; }; class VideoPlayerCore { public: VideoPlayerCore(IVideoDecoder* decoder) : decoder(decoder) {} void play() { playing = true; while (playing) { AVFrame* frame = decoder->getNextFrame(); if (frame) { renderer->renderFrame(frame); av_frame_unref(frame); } // 控制播放速率 QThread::msleep(1000 / frameRate); } } void pause() { playing = false; } void seek(double pos) { decoder->seek(pos); } private: IVideoDecoder* decoder; VideoRenderer* renderer; bool playing = false; double frameRate = 30.0; }; ``` ## 四、性能优化技巧 1. **硬件加速解码**: ```cpp // 启用硬件解码 codecContext->get_format = getHWFormat; // 创建硬件解码器上下文 AVBufferRef* hwDeviceCtx = nullptr; av_hwdevice_ctx_create(&hwDeviceCtx, AV_HWDEVICE_TYPE_CUDA, nullptr, nullptr, 0); codecContext->hw_device_ctx = av_buffer_ref(hwDeviceCtx); ``` 2. **帧缓冲池**: ```cpp class FramePool { public: AVFrame* getFrame() { if (pool.empty()) { return av_frame_alloc(); } AVFrame* frame = pool.back(); pool.pop_back(); return frame; } void returnFrame(AVFrame* frame) { av_frame_unref(frame); pool.push_back(frame); } private: std::vector<AVFrame*> pool; }; ``` 3. **零拷贝渲染**: ```cpp // 使用OpenGL纹理直接渲染 void uploadToTexture(AVFrame* frame) { glBindTexture(GL_TEXTURE_2D, textureID); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, frame->width, frame->height, GL_RGB, GL_UNSIGNED_BYTE, frame->data[0]); } ``` ## 五、测试调试 1. **内存泄漏检测**: ```bash valgrind --leak-check=full ./video_player ``` 2. **性能分析工具**: - Linux: `perf`, `gprof` - Windows: Visual Studio Profiler - macOS: Instruments 3. **跨平台测试矩阵**: | 平台 | 解码器 | 渲染器 | 测试状态 | |------|--------|--------|----------| | Windows | FFmpeg | DirectDraw | ✅ | | Linux | FFmpeg | OpenGL | ✅ | | macOS | FFmpeg | Metal | ✅ | | Android | MediaCodec | OpenGL ES | ⚠️ | ## 六、打包发布 1. **Windows**: - 使用Inno Setup创建安装包 - 包含必要的DLL(FFmpeg、Qt) 2. **Linux**: - 创建DEB/RPM包 - 使用AppImage打包独立应用 3. **macOS**: - 创建dmg安装包 - 使用macdeployqt打包 ## 七、常见问题解决 1. **音视频不同步**: - 使用PTS(Presentation Time Stamp)同步 - 实现音频时钟为主时钟 2. **播放卡顿**: - 优化解码线程优先级 - 使用硬件加速 - 降低分辨率 3. **内存泄漏**: - 确保每个av_alloc都有对应的av_free - 使用RAII管理资源 ## 八、相关资源推荐 1. **官方文档**: - [FFmpeg Documentation](https://ffmpeg.org/documentation.html) - [Qt Multimedia Documentation](https://doc.qt.io/qt-5/multimediaoverview.html) 2. **开源参考**: - VLC播放器源码 - MPV播放器源码 - QMediaPlayer实现 开发视频播放器是一个系统工程,需要深入理解多媒体处理流程、图形渲染技术和用户交互设计。建议从简单播放器开始,逐步添加功能模块[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值