#include "camerathread.h"
#include <iostream>
#include <QDebug>
CameraThread::CameraThread()
{
}
CameraThread::~CameraThread()
{
if (thread_)
{
Stop();
}
}
int CameraThread::Init(const char *url)
{
int ret = 0;
url_ = url;
avdevice_register_all(); // 注册所有设备
h264_pkt = av_packet_alloc();//存读取和编码后的H264数据
captured_packet = av_packet_alloc();
qDebug()<<"CameraThread::Init Success!";
return 0;
}
int CameraThread::Start()
{
start_pts = 0;
int ret = 0;
/******************************************打开摄像头设备********************************************/
const AVInputFormat* m_inputFormat = av_find_input_format("dshow");
inputContext = avformat_alloc_context();
AVDictionary *options = nullptr;
// av_dict_set(&options, "pixel_format", "yuyv422", 0); // 设置为 YUV420P
// av_dict_set(&options, "video_size", "640x480", 0); // 设置分辨率为 640x480 默认1920*1080
// av_dict_set_int(&options, "rtbufsize", 3041280 * 50, 0);//默认大小3041280
// av_dict_set(&options, "rtbufsize", "10M", 0); // 设置为 10MB
ret = avformat_open_input(&inputContext, url_.c_str(), m_inputFormat, &options);
if (ret < 0){
qDebug()<<"avformat_open_input failed, ret: "<<ret;
return -1;
}
// 获取摄像头流信息
ret = avformat_find_stream_info(inputContext, nullptr);
if (ret < 0){
qDebug()<<"Could not retrieve input stream information";
return -1;
}
int videoStreamId = -1;
videoStreamId = av_find_best_stream(inputContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL);
inputStream = inputContext->streams[videoStreamId];
codecParameters = inputStream->codecpar;
// 获取摄像头的实际分辨率和帧率
int actual_width = codecParameters->width;
int actual_height = codecParameters->height;
AVRational actual_frame_rate = inputStream->r_frame_rate;
qDebug()<< "Camera Resolution: " << actual_width << "x" << actual_height;
qDebug()<< "Camera Frame Rate: " << actual_frame_rate.num << "/" << actual_frame_rate.den << " fps";
/******************************************创建解码器********************************************/
// 查找解码器
auto codec_id = inputStream->codecpar->codec_id;
const AVCodec* decoder = avcodec_find_decoder(codec_id);
if (!decoder) {
qDebug()<<"Unsupported codec !";
return -1;
}
// 创建解码器上下文
decoderContext = avcodec_alloc_context3(decoder);
ret = avcodec_parameters_to_context(decoderContext, codecParameters);
if (ret<0) {
qDebug()<<"Failed to copy codec parameters to decoder context.";
return -1;
}
qDebug() << " output pix_fmt=" << av_get_pix_fmt_name((AVPixelFormat)codecParameters->format) <<" "<< codecParameters->format;
ret = avcodec_open2(decoderContext, decoder, nullptr);
if (ret<0) {
qDebug()<<"Failed to open decoder.";
return -1;
}
/******************************************创建编码器********************************************/
// 查找 H.264 编码器
const AVCodec* pEncoderH264 = avcodec_find_encoder(AV_CODEC_ID_H264);
if (pEncoderH264 == NULL) {
qDebug() << "Unsupported encodec.";
return -1;
}
//视频编码器上下文
encoderContext = avcodec_alloc_context3(pEncoderH264);
encoderContext->time_base.num = inputStream->time_base.num;
encoderContext->time_base.den = inputStream->time_base.den;
encoderContext->has_b_frames = 0;
// encoderContext->gop_size = 50;
encoderContext->codec_id = pEncoderH264->id;
encoderContext->pix_fmt = (AVPixelFormat)inputStream->codecpar->format;
qDebug()<<"111111111111111111111111111111: "<<encoderContext->pix_fmt;
encoderContext->width = inputStream->codecpar->width;
encoderContext->height = inputStream->codecpar->height;
// encoderContext->bit_rate = 0;
encoderContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
encoderContext->framerate = inputStream->avg_frame_rate;//编码帧率按照采集帧率来
// encoderContext->bit_rate = 5000000; //数值越小文件越小
//编码器不用等待缓冲区填满,接收到数据即开始编码
av_opt_set(encoderContext->priv_data, "tune", "zerolatency", 0);
// av_opt_set(encoderContext->priv_data, "crf", "23", 0); //数值越大越模糊,存储文件越小
qDebug() << "encoder h246 information:";
qDebug() << " encode fps=" << (encoderContext->framerate.num / encoderContext->framerate.den) ;
qDebug() << " w=" << encoderContext->width << ", h=" << encoderContext->height;
qDebug() << " input pix format=" << av_get_pix_fmt_name(encoderContext->pix_fmt) <<" "<< encoderContext->pix_fmt ;
ret = avcodec_open2(encoderContext, pEncoderH264, NULL);
if (ret < 0) {
qDebug() <<"avcodec_open2";
return -1;
}
/******************************************创建视频输出文件********************************************/
const AVOutputFormat* outputFormat = av_guess_format("mp4", NULL, NULL);
outputContext = avformat_alloc_context();
outputContext->oformat = const_cast<AVOutputFormat*>(outputFormat);
// 创建输出流
outputStream = avformat_new_stream(outputContext, encoderContext->codec);
if (!outputStream){
qDebug() <<"Failed to create output stream";
return -1;
}
avcodec_parameters_from_context(outputStream->codecpar, encoderContext);
outputStream->time_base = av_inv_q(inputStream->r_frame_rate);
// 创建输出 MP4 文件
mp4_count++;
m_mp4_file_name = m_mp4_file_name_header + std::to_string(mp4_count) + ".mp4";
ret = avio_open(&outputContext->pb, m_mp4_file_name.c_str(), AVIO_FLAG_WRITE);
if(ret <0)
{
qDebug() <<"Failed to create output file.";
return -1;
}
ret = avformat_write_header(outputContext, NULL);
if(ret <0)
{
qDebug() <<"Failed to write header";
return -1;
}
/******************************************开线程****************************************************/
thread_ = new std::thread(&CameraThread::Run, this);
if (!thread_)
{
qDebug()<<"new std::thread(&CameraThread::Run, this) failed";
return -1;
}
return 0;
}
int CameraThread::Stop()
{
Thread::Stop();
// 写入文件尾
av_write_trailer(outputContext);
abort_ = 0;
start_pts = 0;
// 释放资源
avcodec_free_context(&encoderContext);
avformat_close_input(&inputContext);
if (!(outputContext->oformat->flags & AVFMT_NOFILE))
{
avio_closep(&outputContext->pb);
}
avformat_free_context(outputContext);
qDebug() << "Recording finished and saved to output.mp4." ;
return 0;
}
void CameraThread::Run()
{
qDebug() << "Run into CameraThread::Run()!";
int ret = 0;
SwsContext *sws_ctx = nullptr;
while (!abort_)
{
// 读取视频帧
ret = av_read_frame(inputContext, captured_packet);
if (ret < 0)
{
qDebug() << "av_read_frame error";
continue; // 如果读取失败,跳过到下一帧
}
// start capture pts from 0
// 初始化 start_pts
if (start_pts == 0)
{
start_pts = captured_packet->pts; // 仅在第一次读取时设置
}
captured_packet->pts -= start_pts;
qDebug()<<"captured_packet->pts: "<<captured_packet->pts;
av_log(nullptr, AV_LOG_INFO, "packet size is %d\n", captured_packet->size);
// 解码 MJPEG 数据
ret = avcodec_send_packet(decoderCo