qt+vlc实现解码h264/h265裸码流播放

张开发
2026/4/10 2:31:11 15 分钟阅读

分享文章

qt+vlc实现解码h264/h265裸码流播放
一 概述本文章实现了对h264/h265裸码流的解码播放功能,主要是一个基于VLC实现的H.264/H.265裸流解码播放类。主要功能包括1)通过OpenStream接口打开流并指定显示窗口2)使用InputStream接口输入裸流数据3)通过CloseStream关闭流。核心实现采用VLC的回调机制(vlc_open_cb/vlc_read_cb/vlc_close_cb)处理流数据支持自动检测H.264/H.265编码格式。类中还包含了数据缓冲管理、线程安全控制以及调试日志功能通过配置选项优化了网络缓存和编解码器选择策略确保稳定播放不同编码格式的视频流。二 实现接口1. bool OpenStream(WId winId); // 打开流传入窗口句柄2. void CloseStream(); // 关闭流3. void InputStream(const char* data, int len, string streamType); // 输入裸流数据三 vlc解码播放类1.vlcstreamdecoder.h#ifndef VLCSTREAMDECODER_H #define VLCSTREAMDECODER_H #include QObject #include vlc/vlc.h #include QByteArray #include QMutex #include mutex #include QMutexLocker #include QtWidgets/QWidget #include basic.h class VLCStreamDecoder : public QObject { Q_OBJECT public: explicit VLCStreamDecoder(QObject *parent nullptr); ~VLCStreamDecoder(); bool OpenStream(WId winId); // 打开流传入窗口句柄 void CloseStream(); // 关闭流 void InputStream(const char* data, int len, string streamType); // 输入裸流数据 void InputStreamEx(const char* data,int len); void configureForBothCodecs(); private: bool h26xWriteOpen(const char* filename); void h26xWriteFrame(unsigned char* data, int len); void h26xWriteClose(); private: // VLC 回调修复后签名 static int vlc_open_cb(void* opaque, void** datap, uint64_t* sizep); static void vlc_close_cb(void* opaque); static ssize_t vlc_read_cb(void* opaque, unsigned char* buf, size_t len); libvlc_instance_t* m_vlcInst; libvlc_media_t* m_vlcMedia; libvlc_media_player_t* m_vlcPlayer; std::vectoruint8_t m_buffer; std::mutex m_mutex; QMutex m_packetMutex; std::listQByteArray m_packetList; FILE* m_h26x_file nullptr; bool m_isPlaying false; }; #endif // VLCSTREAMDECODER_H2.vlcstreamdecoder.cpp#include vlcstreamdecoder.h #include QDebug #include iostream using namespace std; VLCStreamDecoder::VLCStreamDecoder(QObject *parent) : QObject(parent) , m_vlcInst(nullptr) , m_vlcMedia(nullptr) , m_vlcPlayer(nullptr) { const char* vlc_args[] { -I, dummy, --no-audio, // 关闭音频如果你不需要播放声音保留需要声音就删掉 --no-video-title-show, // 不显示标题 --quiet, --live-caching0, --network-caching0, --verbose1, //日志级别 --file-logging, --logfilevlc_log.txt //日志文件 }; m_vlcInst libvlc_new(sizeof(vlc_args)/sizeof(vlc_args[0]), vlc_args); #ifdef WRITE_FILE h26xWriteOpen(camera.26x); #endif } VLCStreamDecoder::~VLCStreamDecoder() { CloseStream(); if (m_vlcInst) { libvlc_release(m_vlcInst); m_vlcInst nullptr; } } bool VLCStreamDecoder::OpenStream(WId winId) { if (!m_vlcInst){ qDebug() m_vlcInst is null; return false; } // 修复使用正确的回调函数 m_vlcMedia libvlc_media_new_callbacks( m_vlcInst, vlc_open_cb, vlc_read_cb, nullptr, vlc_close_cb, this ); if (!m_vlcMedia){ qDebug() m_vlcMedia is null; return false; } // 尝试使用通用解复用器 #if 0 libvlc_media_add_option(m_vlcMedia, :demuxh264); libvlc_media_add_option(m_vlcMedia, :codech264); libvlc_media_add_option(m_vlcMedia, :demuxhevc); libvlc_media_add_option(m_vlcMedia, :codechevc); #endif // configureForBothCodecs(); // 设置媒体到播放器 m_vlcPlayer libvlc_media_player_new_from_media(m_vlcMedia); if (!m_vlcPlayer) { qDebug() m_vlcPlayer is null; return false; } // 绑定窗口句柄 #ifdef WIN32 libvlc_media_player_set_hwnd(m_vlcPlayer, (void*)winId); #else libvlc_media_player_set_xwindow(m_vlcPlayer, (uint32_t)winId); #endif qDebug() VLC 打开流成功窗口句柄 winId; return true; } void VLCStreamDecoder::CloseStream() { if (m_vlcPlayer) { libvlc_media_player_stop(m_vlcPlayer); libvlc_media_player_release(m_vlcPlayer); m_vlcPlayer nullptr; } if (m_vlcMedia) { libvlc_media_release(m_vlcMedia); m_vlcMedia nullptr; } #ifdef WRITE_FILE h26xWriteClose(); #endif qDebug() VLC 流已关闭; } // 喂流接口 void VLCStreamDecoder::InputStream(const char *data, int len,string streamType) { // // 1. 超强防御判断 // if (!data || len 0) return; if (!m_vlcPlayer) return; #if 1 if(!m_isPlaying){ if(streamType h264){ libvlc_media_add_option(m_vlcMedia, :demuxh264); libvlc_media_add_option(m_vlcMedia, :codech264); }else if(streamType h265){ libvlc_media_add_option(m_vlcMedia, :demuxhevc); libvlc_media_add_option(m_vlcMedia, :codechevc); } qDebug() 设置当前播放器支持解码类型为: streamType.data(); libvlc_media_player_set_media(m_vlcPlayer,m_vlcMedia); libvlc_media_player_play(m_vlcPlayer); m_isPlaying true; } #endif #ifdef WRITE_FILE h26xWriteFrame((unsigned char*)data,len); #endif std::lock_guardstd::mutex lock(m_mutex); const uint8_t *p (const uint8_t*)data; m_buffer.insert(m_buffer.end(), p, p len); } void VLCStreamDecoder::InputStreamEx(const char *data, int len) { if(!data || len0) return; if (len 30) { QByteArray ba((const char*)data, 30); qDebug() 数据十六进制 ba.toHex( ); } QByteArray ba(data,len); { QMutexLocker lck(m_packetMutex); if(m_packetList.size()40) return; m_packetList.push_back(ba); } } void VLCStreamDecoder::configureForBothCodecs() { if (!m_vlcMedia) return; // 不指定特定的解复用器让VLC自动检测 // 这样VLC可以根据数据内容自动选择h264或h265解复用器 // libvlc_media_add_option(m_vlcMedia, :demuxh264); // 不要强制指定 // 使用通用的原始视频解复用器作为备选 libvlc_media_add_option(m_vlcMedia, :demuxh26x); // 网络缓存设置对于流数据很重要 libvlc_media_add_option(m_vlcMedia, :network-caching300); // 时钟同步设置 libvlc_media_add_option(m_vlcMedia, :clock-jitter0); libvlc_media_add_option(m_vlcMedia, :clock-synchro0); // 不强制指定解码器让VLC自动选择可用的解码器 // 这样VLC可以根据媒体内容自动选择h264或h265解码器 // libvlc_media_add_option(m_vlcMedia, :codech264); // 不要强制指定 // 增加缓冲区大小 libvlc_media_add_option(m_vlcMedia, :input-buffer-size16384); // 禁用硬件加速以避免兼容性问题 libvlc_media_add_option(m_vlcMedia, :avcodec-hwnone); // 添加错误处理选项 libvlc_media_add_option(m_vlcMedia, :ignore-config); libvlc_media_add_option(m_vlcMedia, :no-audio); // 专注于视频解码 // 添加编解码器自动检测选项 libvlc_media_add_option(m_vlcMedia, :codecany); // 允许任何可用的解码器 // 启用格式自动检测 libvlc_media_add_option(m_vlcMedia, :autodetect-fmts); qDebug() Configured for both H.264 and H.265 codecs; } bool VLCStreamDecoder::h26xWriteOpen(const char *filename) { if (m_h26x_file ! NULL) { fclose(m_h26x_file); } m_h26x_file fopen(filename, wb); // 二进制写入 if (m_h26x_file NULL) { perror(fopen failed); return false; } qDebug([H26x] 已打开文件: %s\n, filename); return true; } void VLCStreamDecoder::h26xWriteFrame(unsigned char *data, int len) { if (m_h26x_file NULL || data NULL || len 0) { return; } // 直接写入一帧 fwrite(data, 1, len, m_h26x_file); // 立即刷新到磁盘防止掉电丢失 fflush(m_h26x_file); // printf([H26x] 写入一帧: %d 字节\n, len); } void VLCStreamDecoder::h26xWriteClose() { if (m_h26x_file ! NULL) { fclose(m_h26x_file); m_h26x_file NULL; qDebug([H26x] 文件已关闭\n); } } // // VLC 回调实现修复版 // int VLCStreamDecoder::vlc_open_cb(void *opaque, void **datap, uint64_t *sizep) { *datap opaque; *sizep 0; // 未知大小 return 0; } void VLCStreamDecoder::vlc_close_cb(void *opaque) { } ssize_t VLCStreamDecoder::vlc_read_cb(void *opaque, unsigned char *buf, size_t len) { VLCStreamDecoder* self (VLCStreamDecoder*)opaque; std::lock_guardstd::mutex lock(self-m_mutex); if (self-m_buffer.empty()){ buf[0] 0; return 1; } unsigned int copyLen std::min(len, (unsigned long)self-m_buffer.size()); memcpy(buf, self-m_buffer.data(), copyLen); #if 0 qDebug(vlc_read_cb: asked for %zu bytes, have %zu, return %zu\n, len, self-m_buffer.size(), copyLen); #endif self-m_buffer.erase(self-m_buffer.begin(), self-m_buffer.begin() copyLen); return copyLen; }

更多文章