惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Jina AI
Jina AI
云风的 BLOG
云风的 BLOG
人人都是产品经理
人人都是产品经理
T
The Blog of Author Tim Ferriss
阮一峰的网络日志
阮一峰的网络日志
罗磊的独立博客
J
Java Code Geeks
博客园 - 聂微东
B
Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
WordPress大学
WordPress大学
腾讯CDC
L
LangChain Blog
Apple Machine Learning Research
Apple Machine Learning Research
Microsoft Azure Blog
Microsoft Azure Blog
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
美团技术团队
博客园 - Franky
Google DeepMind News
Google DeepMind News
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
The Cloudflare Blog

博客园 - 飘杨......

为ZLMediaKit在windows10编译srtp Windows10安装部署ZLMediaKit SurfaceView+EGL+OpenGL ES+ffmpeg播放视频并解决黑屏不显示画面的问题 SurfaceView+ANativeWindow+ffmpeg播放视频并解决花屏问题 SDL播放音频工具类封装 Qt QWidget中dropEvent(QDropEvent* e)拖拽放开不接收事件/不执行,且拖拽到目标后鼠标显示禁用符号 ffmpeg视频转码遇到H264黑屏问题 ffmpeg根据输入的参数截取视频 ffmpeg将视频流录制下来并保存到本地 ffmpeg解封装mp4并解码渲染(SDL2) Qt+ffmpeg+SDL硬件解码并渲染 ffmpeg+sdl播放H264/H265文件 ffmpeg SwsContext像素格式转换BGRA转YUV420P H264/H265 NALU的nalu_unit_type解析 ffmpeg码率控制模式详解 ffmpeg-H264/H265常用编解码预设参数 Qt+FFmpeg+SDL 实现多路多格式原始视频播放工具:从像素处理到实时渲染 Qt+SDL 实战:多格式视频播放工具开发 —— 支持 RGBA/ARGB/RGB24/YUV420P 的完整实现 SDL将YUV视频渲染到Qt窗口播放
FFmpeg实战:录制Windows屏幕并用FFmpeg压缩成H.264/H.265并保存
飘杨...... · 2025-05-21 · via 博客园 - 飘杨......
void GenerateH26XWindow::closeEvent(QCloseEvent* event) {
    event->accept();  // 接受关闭事件(允许关闭)
    if (isClickedRecord) {
        TonyCaptureScreen::Get()->Stop();
        isSaveing = false;
        if (renderThread && renderThread->joinable()) {
            renderThread->join();
        }
        if (writer) {
            writer->Close();
            writer = nullptr;
        }
        if (encoder) {
            encoder->Release();
            delete encoder;
            encoder = nullptr;
        }

    }
    qDebug() << "窗口已关闭";
}
#include "YEncoder.h"
#include <QDebug>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
}

/// <summary>
/// 输出错误日志
/// </summary>
/// <param name="err"></param>
static void PrintErr(int err)
{
    char buf[1024] = { 0 };
    av_strerror(err, buf, sizeof(buf) - 1);
    qDebug() << buf;
}

//////////////////////////////////////////
/// 创建编码上下文
/// @para codec_id 编码器ID号,对应ffmpeg
/// @return 编码上下文 ,失败返回nullptr
AVCodecContext* YEncoder::Create(int codec_id)
{
    //1 找到编码器
    auto codec = avcodec_find_encoder((AVCodecID)codec_id);
    if (!codec)
    {
        qDebug() << "avcodec_find_encoder failed!" << codec_id << endl;
        return nullptr;
    }
    //创建上下文
    auto c = avcodec_alloc_context3(codec);
    if (!c)
    {
        qDebug() << "avcodec_alloc_context3 failed!" << codec_id << endl;
        return nullptr;
    }
    //设置参数默认值
    c->time_base = { 1,25 };
    c->pix_fmt = AV_PIX_FMT_YUV420P;
    //c->pix_fmt = AV_PIX_FMT_RGBA;
    c->thread_count = 16;
    return c;
}

//////////////////////////////////////////
/// 设置对象的编码器上下文 上下文传递到对象中,资源由YEncoder维护
/// 加锁 线程安全
/// @para c 编码器上下文 如果mContext不为nullptr,则先清理资源
void YEncoder::set_c(AVCodecContext* c)
{
    std::unique_lock<std::mutex>lock(mMutex);
    if (mContext)
    {
        avcodec_free_context(&mContext);
    }
    this->mContext = c;
}

bool YEncoder::SetOpt(const char* key, const char* val)
{
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return false;
    auto re = av_opt_set(mContext->priv_data, key, val, 0);
    if (re != 0)
    {
        qDebug() << "av_opt_set failed!";
        PrintErr(re);
    }
    return true;
}

bool YEncoder::SetOpt(const char* key, int val)
{
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return false;
    auto re = av_opt_set_int(mContext->priv_data, key, val, 0);
    if (re != 0)
    {
        qDebug() << "av_opt_set failed!";
        PrintErr(re);
    }
    return true;
}

//////////////////////////////////////////////////////////////
/// 打开编码器 线程安全
bool YEncoder::Open()
{
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return false;
    auto re = avcodec_open2(mContext, NULL, NULL);
    if (re != 0)
    {
        PrintErr(re);
        return false;
    }
    return true;
}

//////////////////////////////////////////////////////////////
/// 编码数据 线程安全 每次新创建AVPacket
/// @para frame 空间由用户维护
/// @return 失败范围nullptr 返回的AVPacket用户需要通过av_packet_free 清理
AVPacket* YEncoder::Encode(const AVFrame* frame)
{
    if (!frame)return nullptr;
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return nullptr;

    //发送到编码线程
    auto re = avcodec_send_frame(mContext, frame);
    if (re != 0)return nullptr;
    auto pkt = av_packet_alloc();
    //接收编码线程数据
    re = avcodec_receive_packet(mContext, pkt);
    if (re == 0)
    {
        return pkt;
    }
    av_packet_free(&pkt);
    if (re == AVERROR(EAGAIN) || re == AVERROR_EOF)
    {
        return nullptr;
    }
    if (re < 0)
    {
        PrintErr(re);
    }
    return nullptr;

}

//////////////////////////////////////////////////////////////
//返回所有编码缓存中AVPacket
std::vector<AVPacket*> YEncoder::End()
{
    std::vector<AVPacket*> res;
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return res;
    auto re = avcodec_send_frame(mContext, NULL); //发送NULL 获取缓冲
    if (re != 0)return res;
    while (re >= 0)
    {
        auto pkt = av_packet_alloc();
        re = avcodec_receive_packet(mContext, pkt);
        if (re != 0)
        {
            av_packet_free(&pkt);
            break;
        }
        res.push_back(pkt);
    }
    return res;
}

///////////////////////////////////////////////////////////////
//根据AVCodecContext 创建一个AVFrame,需要调用者释放av_frame_free
AVFrame* YEncoder::CreateFrame()
{
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return nullptr;
    auto frame = av_frame_alloc();
    frame->width = mContext->width;
    frame->height = mContext->height;
    frame->format = mContext->pix_fmt;
    auto re = av_frame_get_buffer(frame, 0);
    if (re != 0)
    {
        av_frame_free(&frame);
        PrintErr(re);
        return nullptr;
    }
    return frame;
}

//像素格式转换
void YEncoder::InitSwsCtx()
{
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return;
    swsCtx = sws_getCachedContext(swsCtx,
        mContext->width, mContext->height, AV_PIX_FMT_BGRA,//输入的宽高及像素格式
        mContext->width, mContext->height,//输出的宽高
        AV_PIX_FMT_YUV420P,//输出的像素格式
        SWS_BILINEAR,//双线性插值算法
        0, 0, 0
    );
    if (!swsCtx)
    {
        qDebug() << "sws_getCachedContext failed!";
    }
}

void YEncoder::scale(uint8_t* inData, AVFrame* frame)
{
    std::unique_lock<std::mutex>lock(mMutex);
    if (!mContext)return;

    const uint8_t* srcData[AV_NUM_DATA_POINTERS];
    srcData[0] = inData;
    int srcLines[AV_NUM_DATA_POINTERS];
    srcLines[0] = mContext->width * 4;
    int ret = sws_scale(swsCtx
        , srcData, srcLines, 0, mContext->height,
        frame->data, frame->linesize
    );
    if (ret <= 0)
    {
        PrintErr(ret);
    }
}
void YEncoder::Release()
{
    if (swsCtx)
    {
        sws_freeContext(swsCtx);
        swsCtx = nullptr;
    }
}

YEncoder::YEncoder()
{

}

YEncoder::~YEncoder()
{
}