一 简介
本文章主要是使用CLion开发环境实现了一个c/c++媒体流拉流库,该库支持rtsp地址拉流,支持本地mp4文件拉流,支持USB 相机采集拉流。本库可以做为rtsp服务的依赖库,实现一个rtsp服务工具。
二 对外接口
1.初始化媒体流
Simple_API int Simple_APICALL SimpleStreamClient_Init(Simple_Handle *handle, int loglevel);
2.释放媒体流
Simple_API int Simple_APICALL SimpleStreamClient_Deinit(Simple_Handle handle);
3.设置数据回调
Simple_API int Simple_APICALL SimpleStreamClient_SetCallback(Simple_Handle handle, SimpleStreamClientCallBack callback);
4.打开媒体流
Simple_API int Simple_APICALL SimpleStreamClient_OpenStream(Simple_Handle handle, char *url, SIMPLE_RTP_CONNECT_TYPE connType, void *userPtr, int reconn, int timeout, int useExtraData);
5.获取SPS/PPS数据
Simple_API int Simple_APICALL SimpleStreamClient_GetVideoExtraData(Simple_Handle handle,
uint8_t* outData, int maxLen);
三 主要代码
SimpleTypes.h
cpp
#ifndef SIMPLESTREAMCLIENT_SIMPLETYPES_H
#define SIMPLESTREAMCLIENT_SIMPLETYPES_H
#ifdef _WIN32
#define Simple_API __declspec(dllexport)
#define Simple_APICALL __stdcall
#define WIN32_LEAN_AND_MEAN
#else
#define Simple_API __attribute__ ((visibility("default")))
#define Simple_APICALL __attribute__ ((visibility("default")))
#endif
#define Simple_Handle void*
typedef int Simple_I32;
typedef unsigned char Simple_U8;
typedef unsigned char Simple_UChar;
typedef unsigned short Simple_U16;
typedef unsigned int Simple_U32;
typedef unsigned char Simple_Bool;
enum
{
Simple_NoErr = 0,
Simple_RequestFailed = -1,
Simple_Unimplemented = -2,
Simple_RequestArrived = -3,
Simple_OutOfState = -4,
Simple_NotAModule = -5,
Simple_WrongVersion = -6,
Simple_IllegalService = -7,
Simple_BadIndex = -8,
Simple_ValueNotFound = -9,
Simple_BadArgument = -10,
Simple_ReadOnly = -11,
Simple_NotPreemptiveSafe = -12,
Simple_NotEnoughSpace = -13,
Simple_WouldBlock = -14,
Simple_NotConnected = -15,
Simple_FileNotFound = -16,
Simple_NoMoreData = -17,
Simple_AttrDoesntExist = -18,
Simple_AttrNameExists = -19,
Simple_InstanceAttrsNotAllowed = -20,
Simple_InvalidSocket = -21,
Simple_MallocError = -22,
Simple_ConnectError = -23,
Simple_SendError = -24
};
typedef int Simple_Error;
typedef enum __Simple_ACTIVATE_ERR_CODE_ENUM
{
Simple_ACTIVATE_INVALID_KEY = -1, /* 无效Key */
Simple_ACTIVATE_TIME_ERR = -2, /* 时间错误 */
Simple_ACTIVATE_PROCESS_NAME_LEN_ERR = -3, /* 进程名称长度不匹配 */
Simple_ACTIVATE_PROCESS_NAME_ERR = -4, /* 进程名称不匹配 */
Simple_ACTIVATE_VALIDITY_PERIOD_ERR= -5, /* 有效期校验不一致 */
Simple_ACTIVATE_PLATFORM_ERR = -6, /* 平台不匹配 */
Simple_ACTIVATE_COMPANY_ID_LEN_ERR= -7, /* 授权使用商不匹配 */
Simple_ACTIVATE_SUCCESS = 9999, /* 激活成功 */
}Simple_ACTIVATE_ERR_CODE_ENUM;
/* 视频编码 */
#define Simple_SDK_VIDEO_CODEC_H264 0x1C /* H264 */
#define Simple_SDK_VIDEO_CODEC_H265 0xAE /* H265 */
#define Simple_SDK_VIDEO_CODEC_MJPEG 0x08 /* MJPEG */
#define Simple_SDK_VIDEO_CODEC_MPEG4 0x0D /* MPEG4 */
/* 音频编码 */
#define Simple_SDK_AUDIO_CODEC_AAC 0x15002 /* AAC */
#define Simple_SDK_AUDIO_CODEC_G711U 0x10006 /* G711 ulaw*/
#define Simple_SDK_AUDIO_CODEC_G711A 0x10007 /* G711 alaw*/
#define Simple_SDK_AUDIO_CODEC_G726 0x1100B /* G726 */
#define Simple_SDK_EVENT_CODEC_ERROR 0x63657272 /* ERROR */
#define Simple_SDK_EVENT_CODEC_EXIT 0x65786974 /* EXIT */
/* 音视频帧标识 */
#define Simple_SDK_VIDEO_FRAME_FLAG 0x00000001 /* 视频帧标志 */
#define Simple_SDK_AUDIO_FRAME_FLAG 0x00000002 /* 音频帧标志 */
#define Simple_SDK_EVENT_FRAME_FLAG 0x00000004 /* 事件帧标志 */
#define Simple_SDK_RTP_FRAME_FLAG 0x00000008 /* RTP帧标志 */
#define Simple_SDK_SDP_FRAME_FLAG 0x00000010 /* SDP帧标志 */
#define Simple_SDK_MEDIA_INFO_FLAG 0x00000020 /* 媒体类型标志*/
#define Simple_SDK_SNAP_FRAME_FLAG 0x00000040 /* 图片标志*/
/* 视频关键字标识 */
#define Simple_SDK_VIDEO_FRAME_I 0x01 /* I帧 */
#define Simple_SDK_VIDEO_FRAME_P 0x02 /* P帧 */
#define Simple_SDK_VIDEO_FRAME_B 0x03 /* B帧 */
#define Simple_SDK_VIDEO_FRAME_J 0x04 /* JPEG */
/* 连接类型 */
typedef enum __Simple_RTP_CONNECT_TYPE
{
Simple_RTP_OVER_TCP = 0x01, /* RTP Over TCP */
Simple_RTP_OVER_UDP, /* RTP Over UDP */
Simple_RTP_OVER_MULTICAST /* RTP Over MULTICAST */
}SIMPLE_RTP_CONNECT_TYPE;
typedef struct __Simple_AV_Frame
{
Simple_U32 u32AVFrameFlag; /* 帧标志 视频 or 音频 */
Simple_U32 u32AVFrameLen; /* 帧的长度 */
Simple_U32 u32VFrameType; /* 视频的类型,I帧或P帧 */
Simple_U8 *pBuffer; /* 数据 */
Simple_U32 u32TimestampSec; /* 时间戳(秒)*/
Simple_U32 u32TimestampUsec; /* 时间戳(微秒) */
Simple_U32 u32PTS;
} Simple_AV_Frame;
/* 媒体信息 */
typedef struct __Simple_MEDIA_INFO_T
{
Simple_U32 u32VideoCodec; /* 视频编码类型 */
Simple_U32 u32VideoFps; /* 视频帧率 */
Simple_U32 u32AudioCodec; /* 音频编码类型 */
Simple_U32 u32AudioSamplerate; /* 音频采样率 */
Simple_U32 u32AudioChannel; /* 音频通道数 */
Simple_U32 u32AudioBitsPerSample; /* 音频采样精度 */
Simple_U32 u32VpsLength;
Simple_U32 u32SpsLength;
Simple_U32 u32PpsLength;
Simple_U32 u32SeiLength;
Simple_U8 u8Vps[256];
Simple_U8 u8Sps[256];
Simple_U8 u8Pps[128];
Simple_U8 u8Sei[128];
}SIMPLE_MEDIA_INFO_T;
/* 帧信息 */
typedef struct
{
unsigned int codec; /* 音视频格式 */
unsigned int type; /* 视频帧类型 */
unsigned char fps; /* 视频帧率 */
unsigned short width; /* 视频宽 */
unsigned short height; /* 视频高 */
unsigned int reserved1; /* 保留参数1 */
unsigned int reserved2; /* 保留参数2 */
unsigned int reserved3; /* 保留参数3 */
unsigned int sample_rate; /* 音频采样率 */
unsigned int channels; /* 音频声道数 */
unsigned int bits_per_sample; /* 音频采样精度 */
unsigned int length; /* 音视频帧大小 */
unsigned int timestamp_usec; /* 时间戳,微妙 */
unsigned int timestamp_sec; /* 时间戳 秒 */
unsigned int pts;
unsigned int dts;
float bitrate; /* 比特率 */
float losspacket; /* 丢包率 */
}SIMPLE_FRAME_INFO;
#endif //SIMPLESTREAMCLIENT_SIMPLETYPES_H
SimpleStreamClientAPI.h
cpp
#ifndef SIMPLESTREAMCLIENT_LIBRARY_H
#define SIMPLESTREAMCLIENT_LIBRARY_H
#include <cstdint>
#include "SimpleTypes.h"
typedef int (Simple_APICALL *SimpleStreamClientCallBack)(void *_channelPtr, int _frameType, void *pBuf, SIMPLE_FRAME_INFO* _frameInfo);
#ifdef __cplusplus
extern "C"
{
#endif
/* 创建SimpleStreamClient句柄 返回0表示成功 ,返回非0表示失败 ; loglevel : 0 - quiet 1 - debug*/
Simple_API int Simple_APICALL SimpleStreamClient_Init(Simple_Handle *handle, int loglevel);
/* 释放 SimpleStreamClient 参数为SimpleStreamClient句柄 */
Simple_API int Simple_APICALL SimpleStreamClient_Deinit(Simple_Handle handle);
/* 设置数据回调 */
Simple_API int Simple_APICALL SimpleStreamClient_SetCallback(Simple_Handle handle, SimpleStreamClientCallBack callback);
/* 打开网络流 */
Simple_API int Simple_APICALL SimpleStreamClient_OpenStream(Simple_Handle handle, char *url, SIMPLE_RTP_CONNECT_TYPE connType, void *userPtr, int reconn, int timeout, int useExtraData);
// ===================== 新增:获取 SPS/PPS 等 extradata 接口 =====================
/**
* @brief 获取视频 SPS/PPS 数据
* @param handle 流句柄
* @param outData 输出缓冲区
* @param maxLen 缓冲区最大长度
* @return 实际拷贝长度,<0 失败
*/
Simple_API int Simple_APICALL SimpleStreamClient_GetVideoExtraData(Simple_Handle handle,
uint8_t* outData, int maxLen);
#ifdef __cplusplus
}
#endif
#endif // SIMPLESTREAMCLIENT_LIBRARY_H
BaseType.h
cpp
#ifndef SIMPLESTREAMCLIENT_BASETYPE_H
#define SIMPLESTREAMCLIENT_BASETYPE_H
#include <atomic>
#include "SimpleTypes.h"
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/time.h>
#include <libavdevice/avdevice.h> // 设备采集(USB摄像头)
#ifdef __cplusplus
}
#endif
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
typedef int Easy_I32;
typedef unsigned char Easy_U8;
typedef unsigned char Easy_UChar;
typedef unsigned short Easy_U16;
typedef unsigned int Easy_U32;
typedef unsigned char Easy_Bool;
/* 媒体信息 */
typedef struct __EASY_RTSPSERVER_MEDIA_INFO_T
{
Easy_U32 videoCodec; //视频编码类型
Easy_U32 videoFps; //视频帧率
Easy_U32 videoQueueSize; //视频队列大小 如: 1024 * 1024
Easy_U32 audioCodec; //音频编码类型
Easy_U32 audioSampleRate; //音频采样率
Easy_U32 audioChannel; //音频通道数
Easy_U32 audioBitsPerSample; //音频采样精度
Easy_U32 audioQueueSize; //音频队列大小 如: 1024 * 128
Easy_U32 metadataCodec; //Metadata类型
Easy_U32 metadataQueueSize; //Metadata队列大小 如: 1024 * 512
Easy_U32 vpsLength; //视频vps帧长度
Easy_U32 spsLength; //视频sps帧长度
Easy_U32 ppsLength; //视频pps帧长度
Easy_U32 seiLength; //视频sei帧长度
Easy_U8 vps[256]; //视频vps帧内容
Easy_U8 sps[256]; //视频sps帧内容
Easy_U8 pps[128]; //视频sps帧内容
Easy_U8 sei[128]; //视频sei帧内容
}EASY_RTSPSERVER_MEDIA_INFO_T;
typedef struct __RTSP_CHANNEL_T
{
char url[256];
char resourcename[256];
char username[36];
char password[36];
EASY_RTSPSERVER_MEDIA_INFO_T mediaInfo;
SIMPLE_MEDIA_INFO_T s_mediaInfo;
Simple_Handle rtspClientHandle;//rtsp客户端句柄
void* channelHandle;
int status;
}RTSP_CHANNEL_T;
// 流状态
typedef enum
{
STREAM_IDLE,
STREAM_CONNECTING,
STREAM_RUNNING,
STREAM_STOPPING,
STREAM_ERROR
} StreamState;
// 流类型:区分 网络RTSP / USB摄像头
typedef enum
{
SRC_TYPE_NETWORK, // RTSP/RTMP 网络流:解封装取AVPacket
SRC_TYPE_USB_CAMERA, // USB摄像头:采集原始帧 + 编码为AVPacket
SRC_TYPE_LOCAL_FILE //本地文件
} StreamSrcType;
#define EXTRA_DATA_MAX_LEN 2048 // SPS/PPS 最大缓存长度
// 单路流上下文 (Simple_Handle 本质为此结构体指针)
typedef struct StreamContext
{
// 基础配置
int log_level;
char stream_url[256];
SIMPLE_RTP_CONNECT_TYPE conn_type;
void* user_ptr;
int auto_reconn;
int timeout_ms;
int use_extra_data;
// 回调函数
SimpleStreamClientCallBack pkt_cb;
// 流类型 & 状态
StreamSrcType src_type;
std::atomic<int> state;
std::atomic<int> thread_exit;
int thread_running;
// FFmpeg 上下文
AVFormatContext* fmt_ctx;
AVBSFContext* bsf_ctx;
int video_stream_idx;
int audio_stream_idx;
// ========== SPS/PPS 缓存 ==========
uint8_t video_extradata[EXTRA_DATA_MAX_LEN];
int video_extradata_len;
// -------- USB 摄像头专属:采集 + 编码 --------
AVCodecContext* enc_ctx; // 视频编码器
const AVCodec* encoder;
SwsContext *sws_ctx ;
int cam_width;
int cam_height;
enum AVPixelFormat cam_pix_fmt;
// 线程 & 锁
pthread_t work_thread;
pthread_mutex_t ctx_mutex;
// 帧信息结构体(透传给上层)
SIMPLE_FRAME_INFO frame_info;
} StreamContext;
#endif //SIMPLESTREAMCLIENT_BASETYPE_H
SimpleStreamClientAPI.cpp
cpp
#include "SimpleStreamClientAPI.h"
#include "BaseType.h"
#include <iostream>
#define MEDIA_INFO_BUF_VPS_SPS_MAX 256
#define MEDIA_INFO_BUF_PPS_SEI_MAX 128
// 转换返回码
typedef enum
{
CONV_RET_OK = 0,
CONV_RET_NULL_INPUT = -1,
CONV_RET_BUF_SHORT = -2,
CONV_RET_ALLOC_FAIL = -3,
} ConvRetCode;
// 内部线程入口函数声明
static void* StreamWorkThread(void* arg);
static StreamSrcType DetectStreamType(const char *url);
// 根据编码获取对应BSF名称
static const char* GetMp4ToAnnexBBsfName(enum AVCodecID codecId)
{
if (codecId == AV_CODEC_ID_H264)
return "h264_mp4toannexb";
if (codecId == AV_CODEC_ID_HEVC)
return "hevc_mp4toannexb";
return NULL;
}
/**
* @brief 初始化AVCC/HVCC转Annex-B比特流过滤器
* @param parIn 视频流codecpar(mp4解封装得到)
* @param bsfCtx [out] 输出BSF上下文
* @return 0成功 负数失败
*/
int InitMp4ToAnnexBBsf(const AVCodecParameters* parIn, AVBSFContext** bsfCtx)
{
*bsfCtx = NULL;
if (!parIn || parIn->codec_type != AVMEDIA_TYPE_VIDEO)
return -1;
const char* bsfName = GetMp4ToAnnexBBsfName(parIn->codec_id);
if (!bsfName)
return -2;
// 1. 获取过滤器
const AVBitStreamFilter* filter = av_bsf_get_by_name(bsfName);
if (!filter)
return -3;
// 2. 分配BSF上下文
int ret = av_bsf_alloc(filter, bsfCtx);
if (ret < 0)
return ret;
// 3. 拷贝编码参数(关键:携带extradata SPS/PPS/VPS)
ret = avcodec_parameters_copy((*bsfCtx)->par_in, parIn);
if (ret < 0)
{
av_bsf_free(bsfCtx);
return ret;
}
// 4. 初始化过滤器
ret = av_bsf_init(*bsfCtx);
if (ret < 0)
{
av_bsf_free(bsfCtx);
return ret;
}
return 0;
}
/**
* @brief 转换1帧MP4 AVCC/HVCC packet为Annex-B packet
* @param bsfCtx 已初始化的BSF上下文
* @param inPkt 输入mp4原始packet
* @param outPkt 输出Annex-B标准packet(调用者av_packet_unref释放)
* @return 0成功;AVERROR(EAGAIN)无输出包;负数失败
*/
int ConvertPacketToAnnexB(AVBSFContext* bsfCtx, const AVPacket* inPkt, AVPacket* outPkt)
{
av_packet_unref(outPkt);
// 送入过滤器
int ret = av_bsf_send_packet(bsfCtx, (AVPacket*)inPkt);
if (ret < 0)
return ret;
// 取出转换后的Annex-B包
ret = av_bsf_receive_packet(bsfCtx, outPkt);
return ret;
}
// 辅助函数:查找 NALU 起始码位置 & 返回 NALU 数据起始偏移
static int FindNalStartCode(const uint8_t* data, int totalLen, int startPos, int* nalSize)
{
if (!data || totalLen <= 0 || startPos >= totalLen || !nalSize)
return -1;
*nalSize = 0;
for (int i = startPos; i < totalLen ; ++i)
{
// 匹配 00 00 00 01 (4字节起始码)
if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x00 && data[i+3] == 0x01)
{
int nextStart = i + 4;
// 查找下一个起始码,确定当前NALU长度
int j = nextStart;
while (j < totalLen)
{
if (data[j] == 0x00 && data[j+1] == 0x00 && data[j+2] == 0x00 && data[j+3] == 0x01)
break;
j++;
}
*nalSize = j - nextStart;
return nextStart;
}
// 匹配 00 00 01 (3字节起始码)
else if (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x01)
{
int nextStart = i + 3;
int j = nextStart;
while (j < totalLen - 2)
{
if (data[j] == 0x00 && data[j+1] == 0x00 && data[j+2] == 0x01)
break;
j++;
}
*nalSize = j - nextStart;
return nextStart;
}
}
return -1; // 未找到起始码
}
/**
* @brief 解析mp4 extradata(AVCC/HVCC) 提取VPS/SPS/PPS
* @param codecId 编码类型 AV_CODEC_ID_H264 / AV_CODEC_ID_HEVC
* @param extraData 原始extradata二进制
* @param extraLen extradata长度
* @param outInfo 输出解析后的vps/sps/pps
* @return 0成功 <0失败
*/
int ParseAvcHvccExtraData(int codecId, const uint8_t* extraData, int extraLen, SIMPLE_MEDIA_INFO_T* outInfo)
{
if (!extraData || extraLen <= 0 || !outInfo)
return -1;
// 清空输出缓冲区
memset(outInfo->u8Vps, 0, MEDIA_INFO_BUF_VPS_SPS_MAX);
outInfo->u32VpsLength = 0;
memset(outInfo->u8Sps, 0, MEDIA_INFO_BUF_VPS_SPS_MAX);
outInfo->u32SpsLength = 0;
memset(outInfo->u8Pps, 0, MEDIA_INFO_BUF_PPS_SEI_MAX);
outInfo->u32PpsLength = 0;
int pos = 0;
if (codecId == AV_CODEC_ID_H264)
{
// ========= H264 AVCC 解析 =========
// 最小头部长度校验
if (extraLen < 7)
return -2;
pos = 4; // 跳过版本+profile+level+mask(01 4d 40 28 ff)
uint8_t spsCntFlag = extraData[++pos];
// SPS数量
uint8_t spsCount = spsCntFlag & 0x1F;
for (int i = 0; i < spsCount; i++)
{
if (pos + 2 > extraLen)
return -3;
uint16_t spsLen = (extraData[pos+1] << 8) | extraData[pos + 2];
pos += 2;
if (pos + spsLen > extraLen)
return -4;
// 拷贝SPS
int copyLen = (spsLen < MEDIA_INFO_BUF_VPS_SPS_MAX) ? spsLen : MEDIA_INFO_BUF_VPS_SPS_MAX;
memcpy(outInfo->u8Sps, extraData + pos, copyLen);
outInfo->u32SpsLength = copyLen;
pos += spsLen;
break; // 只取第一个SPS
}
// PPS
if (pos >= extraLen)
return -5;
uint8_t ppsCount = extraData[++pos];
for (int i = 0; i < ppsCount; i++)
{
if (pos + 2 > extraLen)
return -6;
uint16_t ppsLen = (extraData[pos+1] << 8) | extraData[pos + 2];
pos += 2;
if (pos + ppsLen > extraLen)
return -7;
int copyLen = (ppsLen < MEDIA_INFO_BUF_PPS_SEI_MAX) ? ppsLen : MEDIA_INFO_BUF_PPS_SEI_MAX;
memcpy(outInfo->u8Pps, extraData + pos, copyLen);
outInfo->u32PpsLength = copyLen;
pos += ppsLen;
break; // 只取第一个PPS
}
// H264无VPS,保持0
}
else if (codecId == AV_CODEC_ID_HEVC)
{
// ========= H265 HVCC 解析(扩展兼容)=========
if (extraLen < 23)
return -8;
pos = 22;
// VPS
uint8_t vpsCnt = extraData[pos++];
for (int i = 0; i < vpsCnt; i++)
{
uint16_t vpsLen = (extraData[pos] << 8) | extraData[pos + 1];
pos += 2;
int copyLen = (vpsLen < MEDIA_INFO_BUF_VPS_SPS_MAX) ? vpsLen : MEDIA_INFO_BUF_VPS_SPS_MAX;
memcpy(outInfo->u8Vps, extraData + pos, copyLen);
outInfo->u32VpsLength = copyLen;
pos += vpsLen;
break;
}
// SPS
uint8_t spsCnt = extraData[pos++];
for (int i = 0; i < spsCnt; i++)
{
uint16_t spsLen = (extraData[pos] << 8) | extraData[pos + 1];
pos += 2;
int copyLen = (spsLen < MEDIA_INFO_BUF_VPS_SPS_MAX) ? spsLen : MEDIA_INFO_BUF_VPS_SPS_MAX;
memcpy(outInfo->u8Sps, extraData + pos, copyLen);
outInfo->u32SpsLength = copyLen;
pos += spsLen;
break;
}
// PPS
uint8_t ppsCnt = extraData[pos++];
for (int i = 0; i < ppsCnt; i++)
{
uint16_t ppsLen = (extraData[pos] << 8) | extraData[pos + 1];
pos += 2;
int copyLen = (ppsLen < MEDIA_INFO_BUF_PPS_SEI_MAX) ? ppsLen : MEDIA_INFO_BUF_PPS_SEI_MAX;
memcpy(outInfo->u8Pps, extraData + pos, copyLen);
outInfo->u32PpsLength = copyLen;
pos += ppsLen;
break;
}
}
else
{
return -9; // 不支持的编码
}
return 0;
}
static void ExtractVideoParams(SIMPLE_MEDIA_INFO_T &s_media_info, AVStream *vs) {
memset(s_media_info.u8Vps, 0, MEDIA_INFO_BUF_VPS_SPS_MAX);
memset(s_media_info.u8Sps, 0, MEDIA_INFO_BUF_VPS_SPS_MAX);
memset(s_media_info.u8Pps, 0, MEDIA_INFO_BUF_PPS_SEI_MAX);
memset(s_media_info.u8Sei, 0, MEDIA_INFO_BUF_PPS_SEI_MAX);
s_media_info.u32VpsLength = 0;
s_media_info.u32SpsLength = 0;
s_media_info.u32PpsLength = 0;
s_media_info.u32SeiLength = 0;
int pos = 0;
int nalLen = 0;
// 分支1:H.264 编码 (AV_CODEC_ID_H264)
if (vs->codecpar->codec_id == AV_CODEC_ID_H264)
{
// H264 extradata: SPS NALU + PPS NALU
pos = FindNalStartCode(vs->codecpar->extradata, vs->codecpar->extradata_size, pos, &nalLen);
if (pos > 0 && nalLen > 0 && pos + nalLen <= vs->codecpar->extradata_size)
{
// 判断 SPS (NALU type = 0x67)
if ((vs->codecpar->extradata[pos] & 0x1F) == 0x07)
{
int copyLen = (nalLen < MEDIA_INFO_BUF_VPS_SPS_MAX) ? nalLen : MEDIA_INFO_BUF_VPS_SPS_MAX;
memcpy(s_media_info.u8Sps, vs->codecpar->extradata + pos, copyLen);
s_media_info.u32SpsLength = copyLen;
printf("sps:%d\n",copyLen);
for (auto i= 0;i<s_media_info.u32SpsLength;i++) {
printf("%02x ",s_media_info.u8Sps[i]);
}
printf("\n");
}
// 查找下一个 NALU (PPS)
pos += nalLen;
pos = FindNalStartCode(vs->codecpar->extradata, vs->codecpar->extradata_size, pos, &nalLen);
if (pos > 0 && nalLen > 0 && pos + nalLen <= vs->codecpar->extradata_size)
{
// 判断 PPS (NALU type = 0x68)
if ((vs->codecpar->extradata[pos] & 0x1F) == 0x08)
{
int copyLen = (nalLen < MEDIA_INFO_BUF_PPS_SEI_MAX) ? nalLen : MEDIA_INFO_BUF_PPS_SEI_MAX;
memcpy(s_media_info.u8Pps, vs->codecpar->extradata + pos, copyLen);
s_media_info.u32PpsLength = copyLen;
printf("pps:%d\n",copyLen);
for (auto i= 0;i<s_media_info.u32PpsLength;i++) {
printf("%02x ",s_media_info.u8Pps[i]);
}
printf("\n");
}
}
}
}
// 分支2:H.265/HEVC 编码 (AV_CODEC_ID_HEVC)
else if (vs->codecpar->codec_id == AV_CODEC_ID_HEVC) {
// H265 extradata: VPS NALU + SPS NALU + PPS NALU
// 1. 解析 VPS (NALU type = 0x40)
pos = FindNalStartCode(vs->codecpar->extradata, vs->codecpar->extradata_size, pos, &nalLen);
if (pos > 0 && nalLen > 0 && pos + nalLen <= vs->codecpar->extradata_size)
{
if ((vs->codecpar->extradata[pos]>>1 & 0x3F) == 0x20)
{
int copyLen = (nalLen < MEDIA_INFO_BUF_VPS_SPS_MAX) ? nalLen : MEDIA_INFO_BUF_VPS_SPS_MAX;
memcpy(s_media_info.u8Vps, vs->codecpar->extradata + pos, copyLen);
s_media_info.u32VpsLength = copyLen;
printf("vps:%d\n",copyLen);
for (auto i= 0;i<s_media_info.u32VpsLength;i++) {
printf("%02x ",s_media_info.u8Vps[i]);
}
printf("\n");
}else {
printf("no find vps\n");
}
// 2. 解析 SPS (NALU type = 0x42)
pos += nalLen;
pos = FindNalStartCode(vs->codecpar->extradata, vs->codecpar->extradata_size, pos, &nalLen);
if (pos > 0 && nalLen > 0 && pos + nalLen <= vs->codecpar->extradata_size)
{
if ((vs->codecpar->extradata[pos]>>1 & 0x3F) == 0x21)
{
int copyLen = (nalLen < MEDIA_INFO_BUF_VPS_SPS_MAX) ? nalLen : MEDIA_INFO_BUF_VPS_SPS_MAX;
memcpy(s_media_info.u8Sps, vs->codecpar->extradata + pos, copyLen);
s_media_info.u32SpsLength = copyLen;
printf("sps:%d\n",copyLen);
for (auto i= 0;i<s_media_info.u32SpsLength;i++) {
printf("%02x ",s_media_info.u8Sps[i]);
}
printf("\n");
}
// 3. 解析 PPS (NALU type = 0x44)
pos += nalLen;
pos = FindNalStartCode(vs->codecpar->extradata, vs->codecpar->extradata_size, pos, &nalLen);
if (pos > 0 && nalLen > 0 && pos + nalLen <= vs->codecpar->extradata_size)
{
if ((vs->codecpar->extradata[pos]>>1 & 0x3F) == 0x22)
{
int copyLen = (nalLen < MEDIA_INFO_BUF_PPS_SEI_MAX) ? nalLen : MEDIA_INFO_BUF_PPS_SEI_MAX;
memcpy(s_media_info.u8Pps, vs->codecpar->extradata + pos, copyLen);
s_media_info.u32PpsLength = copyLen;
printf("pps:%d\n",copyLen);
for (auto i= 0;i<s_media_info.u32PpsLength;i++) {
printf("%02x ",s_media_info.u8Pps[i]);
}
printf("\n");
}
}
}
}
}
}
int SimpleStreamClient_Init(void **handle, int loglevel)
{
if (!handle)
return -1;
StreamContext* ctx = (StreamContext*)av_mallocz(sizeof(StreamContext));
if (!ctx)
return -2;
// 基础初始化
ctx->log_level = loglevel;
ctx->pkt_cb = NULL;
ctx->user_ptr = NULL;
ctx->auto_reconn = 0;
ctx->timeout_ms = 0;
ctx->use_extra_data = 0;
ctx->state.store(STREAM_IDLE);
ctx->thread_exit.store(0);
ctx->thread_running = 0;
ctx->fmt_ctx = NULL;
ctx->video_stream_idx = -1;
ctx->audio_stream_idx = -1;
// 清空 SPS/PPS 缓存
memset(ctx->video_extradata, 0, EXTRA_DATA_MAX_LEN);
ctx->video_extradata_len = 0;
// USB 编码相关置空
ctx->enc_ctx = NULL;
ctx->encoder = NULL;
ctx->cam_width = 640;
ctx->cam_height = 480;
ctx->cam_pix_fmt = AV_PIX_FMT_YUV420P;
// 初始化锁
if (pthread_mutex_init(&ctx->ctx_mutex, NULL) != 0)
{
av_free(ctx);
return -3;
}
// FFmpeg 全局初始化(含设备)
avdevice_register_all();
av_log_set_level(loglevel ? AV_LOG_DEBUG : AV_LOG_QUIET);
*handle = (Simple_Handle)ctx;
return 0;
}
int SimpleStreamClient_Deinit(void *handle) {
if (!handle)
return -1;
StreamContext* ctx = (StreamContext*)handle;
// 通知线程退出
ctx->thread_exit.store(1);
ctx->state.store(STREAM_STOPPING);
if (ctx->thread_running)
{
pthread_join(ctx->work_thread, NULL);
ctx->thread_running = 0;
}
// 释放所有 FFmpeg 资源
pthread_mutex_lock(&ctx->ctx_mutex);
if (ctx->fmt_ctx)
{
avformat_close_input(&ctx->fmt_ctx);
ctx->fmt_ctx = NULL;
}
avcodec_free_context(&ctx->enc_ctx);
pthread_mutex_unlock(&ctx->ctx_mutex);
pthread_mutex_destroy(&ctx->ctx_mutex);
av_free(ctx);
return 0;
}
int SimpleStreamClient_SetCallback(void *handle, SimpleStreamClientCallBack callback) {
if (!handle || !callback)
return -1;
StreamContext* ctx = (StreamContext*)handle;
pthread_mutex_lock(&ctx->ctx_mutex);
ctx->pkt_cb = callback;
pthread_mutex_unlock(&ctx->ctx_mutex);
return 0;
}
int SimpleStreamClient_OpenStream(void *handle, char *url, SIMPLE_RTP_CONNECT_TYPE connType, void *userPtr, int reconn,
int timeout, int useExtraData) {
if (!handle || !url)
return -1;
StreamContext* ctx = (StreamContext*)handle;
pthread_mutex_lock(&ctx->ctx_mutex);
int state = ctx->state;
if (state != STREAM_IDLE && state != STREAM_ERROR)
{
pthread_mutex_unlock(&ctx->ctx_mutex);
return -2;
}
std::string stream_url = "";
if (strstr(url, "ch1")) {
stream_url = "rtsp://admin:admin12345@192.168.22.222:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1";
}else if (strstr(url, "ch2")) {
stream_url = "/home/packet/car1.mp4";
}else if (strstr(url,"ch3")) {
stream_url = "/home/packet/easy.mp4";
}else if (strstr(url,"ch4")) {
stream_url = "rtsp://admin5:admin12345@192.168.250.114:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif";
}else if (strstr(url,"ch5")) {
stream_url = "/home/packet/dzq_h265.mp4";
}else if (strstr(url,"camera")) {
stream_url = "camera";
}else {
stream_url = url;
}
// 赋值参数
if (stream_url == "") {
}else {
strncpy(ctx->stream_url, stream_url.data(), sizeof(ctx->stream_url) - 1);
}
ctx->conn_type = connType;
ctx->user_ptr = userPtr;
ctx->auto_reconn = reconn;
ctx->timeout_ms = timeout;
ctx->use_extra_data = useExtraData;
// 判断流类型
ctx->src_type = DetectStreamType(stream_url.data());
// 清空旧的 SPS/PPS
memset(ctx->video_extradata, 0, EXTRA_DATA_MAX_LEN);
ctx->video_extradata_len = 0;
ctx->state.store(STREAM_CONNECTING);
pthread_mutex_unlock(&ctx->ctx_mutex);
// 启动工作线程
if (pthread_create(&ctx->work_thread, NULL, StreamWorkThread, ctx) != 0)
{
ctx->state.store(STREAM_ERROR);
return -3;
}
ctx->thread_running = 1;
return 0;
}
// 内部:判断是网络流还是USB摄像头设备
static StreamSrcType DetectStreamType(const char *url)
{
if (!url) return SRC_TYPE_NETWORK;
// 简单规则:rtsp/rtmp/http 开头 = 网络流
if (strstr(url, "rtsp://") || strstr(url, "rtmp://") || strstr(url, "http://"))
{
return SRC_TYPE_NETWORK;
}else if (strstr(url,"camera")) {
return SRC_TYPE_USB_CAMERA;
}
// 其余当作 本地文件
return SRC_TYPE_LOCAL_FILE;
}
// 内部:缓存 SPS/PPS
static void CacheExtraData(StreamContext* ctx, const uint8_t* data, int len)
{
if (!ctx || !data || len <= 0) return;
int copyLen = (len < EXTRA_DATA_MAX_LEN) ? len : EXTRA_DATA_MAX_LEN;
memcpy(ctx->video_extradata, data, copyLen);
ctx->video_extradata_len = copyLen;
printf("video_extradata_len:%d\n",ctx->video_extradata_len);
}
// 工作线程主逻辑
static void* StreamWorkThread(void* arg)
{
StreamContext* ctx = (StreamContext*)arg;
if (!ctx) return NULL;
RTSP_CHANNEL_T *rtsp_channel = (RTSP_CHANNEL_T*)ctx->user_ptr;
AVPacket* pkt = av_packet_alloc();
AVPacket* outPkt = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
// 固定帧参数:宽高、像素格式(和采集、编码器严格对齐)
frame->width = ctx->cam_width;
frame->height = ctx->cam_height;
frame->format = ctx->cam_pix_fmt;
while (!ctx->thread_exit.load())
{
pthread_mutex_lock(&ctx->ctx_mutex);
ctx->fmt_ctx = avformat_alloc_context();
AVDictionary* opts = NULL;
av_dict_set(&opts, "protocol_whitelist", "tcp,udp,rtsp,file,rtp,http,https", 0);
#if 0
av_dict_set(&opts, "timeout", std::to_string(ctx->timeout_ms * 1000).data(), 0);
#else
if (ctx->timeout_ms > 0)
{
char stimeout_str[32] = {0};
snprintf(stimeout_str, sizeof(stimeout_str), "%d", ctx->timeout_ms * 1000);
av_dict_set(&opts, "stimeout", stimeout_str, 0);
}
#endif
av_dict_set(&opts, "rtsp_transport", "tcp", 0);
int ret = -1;
// ========== 分支1:RTSP/网络流 解封装读包 ==========
if (ctx->src_type == SRC_TYPE_NETWORK )
{
ret = avformat_open_input(&ctx->fmt_ctx, ctx->stream_url, NULL, &opts);
} else if (ctx->src_type == SRC_TYPE_LOCAL_FILE) {
ret = avformat_open_input(&ctx->fmt_ctx, ctx->stream_url, NULL, &opts);
}
// ========== 分支2:USB 摄像头 设备采集 ==========
else
{
// 预先分配AVFrame内部缓冲区
int retValue = av_frame_get_buffer(frame, 0);
if (retValue < 0)
{
av_log(NULL, AV_LOG_ERROR, "av_frame_get_buffer failed\n");
continue;
}else {
av_log(NULL, AV_LOG_INFO, "av_frame_get_buffer succ\n");
}
// 指定设备输入格式(linux:video4linux2, windows:dshow)
AVDictionary* dev_opts = NULL;
AVInputFormat *ifmt = av_find_input_format("video4linux2");
// Linux USB摄像头默认设备:/dev/video0
const char *videoDevice = "/dev/video0";
ret = avformat_open_input(&ctx->fmt_ctx, videoDevice, ifmt, &dev_opts);
// 初始化编码器 (H.264 示例)
if (ret >= 0 && !ctx->enc_ctx)
{
ctx->encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
if (ctx->encoder == nullptr) {
printf("avcodec_find_encoder failed\n");
}else {
printf("avcodec_find_encoder() call succ \n");
}
ctx->enc_ctx = avcodec_alloc_context3(ctx->encoder);
ctx->enc_ctx->width = ctx->cam_width;
ctx->enc_ctx->height = ctx->cam_height;
ctx->enc_ctx->pix_fmt = ctx->cam_pix_fmt;
ctx->enc_ctx->time_base = (AVRational){1, 25};
ctx->enc_ctx->gop_size = 25; // 每25帧一个I帧
// 禁止生成B帧,只输出 I帧 + P帧
ctx->enc_ctx->max_b_frames = 0;
// libx264 推荐CRF 23~28,数值越小越清晰
av_opt_set(ctx->enc_ctx->priv_data, "crf", "26", 0);
int res = avcodec_open2(ctx->enc_ctx, ctx->encoder, NULL);
if (res < 0) {
printf("avcodec_open2() call failed\n");
}else {
printf("avcodec_open2() call succ\n");
printf("extradata_size:%d\n",ctx->enc_ctx->extradata_size);
}
// 1. 创建输出流
AVStream* vstream = avformat_new_stream(ctx->fmt_ctx, ctx->enc_ctx->codec);
if (!vstream)
{
// 创建流失败
printf("avformat_new_stream() call failed\n");
continue;
}else {
printf("avformat_new_stream() call succ\n");
}
// 2. 把编码器上下文同步到流参数(fmt_ctx依靠此参数封装)
int copy_ret = avcodec_parameters_from_context(vstream->codecpar, ctx->enc_ctx);
if (copy_ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "copy codec param failed\n");
continue;
}else {
printf("avcodec_parameters_from_context() call succ ,codeid:%d\n",
vstream->codecpar->codec_id);
printf("ctx->fmt_ctx->nb_streams:%d \n",ctx->fmt_ctx->nb_streams);
}
// 3. 同步时基,封装pts/dts计算依赖
vstream->time_base = ctx->enc_ctx->time_base;
// 初始化SwsContext
ctx->sws_ctx = sws_getContext(
ctx->cam_width, ctx->cam_height, AV_PIX_FMT_YUYV422,
ctx->enc_ctx->width, ctx->enc_ctx->height, AV_PIX_FMT_YUV420P,
SWS_BILINEAR, NULL, NULL, NULL
);
//测试代码
{
for (int i = 0;i<ctx->fmt_ctx->nb_streams;i++) {
AVStream* vs = ctx->fmt_ctx->streams[i];
printf("-=- codeid:%d\n",vs->codecpar->codec_id);
}
}
}
}
av_dict_free(&opts);
if (ret < 0)
{
pthread_mutex_unlock(&ctx->ctx_mutex);
ctx->state.store(STREAM_ERROR);
av_usleep(500000);
if (!ctx->auto_reconn) break;
continue;
}
// 查找流信息
ret = avformat_find_stream_info(ctx->fmt_ctx, NULL);
if (ret < 0)
{
avformat_close_input(&ctx->fmt_ctx);
ctx->fmt_ctx = NULL;
pthread_mutex_unlock(&ctx->ctx_mutex);
ctx->state.store(STREAM_ERROR);
av_usleep(500000);
if (!ctx->auto_reconn) break;
continue;
}
// 找到音视频流
if (ctx->src_type == SRC_TYPE_USB_CAMERA) {
ctx->video_stream_idx = av_find_best_stream(ctx->fmt_ctx, AVMEDIA_TYPE_VIDEO, 1, -1, NULL, 0);
}else {
ctx->video_stream_idx = av_find_best_stream(ctx->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
}
ctx->audio_stream_idx = av_find_best_stream(ctx->fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
printf("video_stream_idx:%d audio_stream_idx:%d\n",ctx->video_stream_idx,ctx->audio_stream_idx);
// 缓存首次 SPS/PPS
if (ctx->video_stream_idx >= 0)
{
AVStream* vs = ctx->fmt_ctx->streams[ctx->video_stream_idx];
if (vs->codecpar->codec_id == AV_CODEC_ID_H264) {
rtsp_channel->s_mediaInfo.u32VideoCodec = 0x1C;
}else if (vs->codecpar->codec_id == AV_CODEC_ID_HEVC) {
rtsp_channel->s_mediaInfo.u32VideoCodec = 0xAE;
}else {
printf("==codec id :%d\n",vs->codecpar->codec_id);
}
rtsp_channel->s_mediaInfo.u32VideoFps = 25;
if (vs->codecpar->extradata && vs->codecpar->extradata_size > 0)
{
if (ctx->src_type == SRC_TYPE_NETWORK || ctx->src_type == SRC_TYPE_USB_CAMERA)
{
ExtractVideoParams(rtsp_channel->s_mediaInfo,vs);
}
else if (ctx->src_type == SRC_TYPE_LOCAL_FILE)
{
#if 0
int result = ParseAvcHvccExtraData(vs->codecpar->codec_id,
vs->codecpar->extradata, vs->codecpar->extradata_size,
&rtsp_channel->s_mediaInfo);
if (result == 0) {
printf("ParseAvcHvccExtraData() parse successful\n");
}else {
printf("ParseAvcHvccExtraData parse failed :%d\n",result);
}
#else
// 初始化转换过滤器
ret = InitMp4ToAnnexBBsf(vs->codecpar, &ctx->bsf_ctx);
if (ret < 0) {
printf("InitMp4ToAnnexBBsf() failed\n");
pthread_mutex_unlock(&ctx->ctx_mutex);
continue;
}
#endif
}
rtsp_channel->mediaInfo.videoCodec = rtsp_channel->s_mediaInfo.u32VideoCodec;
rtsp_channel->mediaInfo.videoFps = rtsp_channel->s_mediaInfo.u32VideoFps;
if (rtsp_channel->s_mediaInfo.u32SpsLength > 0) {
rtsp_channel->mediaInfo.spsLength = rtsp_channel->s_mediaInfo.u32SpsLength;
memcpy(rtsp_channel->mediaInfo.sps,rtsp_channel->s_mediaInfo.u8Sps,rtsp_channel->s_mediaInfo.u32SpsLength);
}
if (rtsp_channel->s_mediaInfo.u32PpsLength > 0) {
rtsp_channel->mediaInfo.ppsLength = rtsp_channel->s_mediaInfo.u32PpsLength;
memcpy(rtsp_channel->mediaInfo.pps,rtsp_channel->s_mediaInfo.u8Pps,rtsp_channel->s_mediaInfo.u32PpsLength);
}
if (rtsp_channel->s_mediaInfo.u32VpsLength > 0) {
rtsp_channel->mediaInfo.vpsLength = rtsp_channel->s_mediaInfo.u32VpsLength;
memcpy(rtsp_channel->mediaInfo.vps,rtsp_channel->s_mediaInfo.u8Vps,rtsp_channel->s_mediaInfo.u32VpsLength);
}
rtsp_channel->mediaInfo.seiLength = 0;
if (rtsp_channel->s_mediaInfo.u32SeiLength > 0) {
rtsp_channel->mediaInfo.seiLength = rtsp_channel->s_mediaInfo.u32SeiLength;
memcpy(rtsp_channel->mediaInfo.sei,rtsp_channel->s_mediaInfo.u8Sei,rtsp_channel->s_mediaInfo.u32SeiLength);
}
printf("extradata_size:%d\n",vs->codecpar->extradata_size);
for (int i = 0; i < vs->codecpar->extradata_size; i++) {
printf(" %02x", vs->codecpar->extradata[i]);
}
printf("\n");
CacheExtraData(ctx, vs->codecpar->extradata, vs->codecpar->extradata_size);
}else {
printf("extradata_size:%d\n",vs->codecpar->extradata_size);
}
}
if (ctx->audio_stream_idx >= 0) {
AVStream* as = ctx->fmt_ctx->streams[ctx->audio_stream_idx];
rtsp_channel->s_mediaInfo.u32AudioCodec = as->codecpar->codec_id;
rtsp_channel->s_mediaInfo.u32AudioChannel = as->codecpar->channels;
rtsp_channel->s_mediaInfo.u32AudioSamplerate = as->codecpar->sample_rate;
rtsp_channel->s_mediaInfo.u32AudioBitsPerSample = as->codecpar->bits_per_coded_sample;
rtsp_channel->mediaInfo.audioSampleRate = rtsp_channel->s_mediaInfo.u32AudioSamplerate;
rtsp_channel->mediaInfo.audioBitsPerSample = rtsp_channel->s_mediaInfo.u32AudioBitsPerSample;
rtsp_channel->mediaInfo.audioCodec = rtsp_channel->s_mediaInfo.u32AudioCodec;
rtsp_channel->mediaInfo.audioChannel = rtsp_channel->s_mediaInfo.u32AudioChannel;
}
ctx->state.store(STREAM_RUNNING);
pthread_mutex_unlock(&ctx->ctx_mutex);
// ========== 循环读包 ==========
if (ctx->src_type == SRC_TYPE_NETWORK)
{
// 网络流:直接读出 AVPacket 回调
while (!ctx->thread_exit.load() && av_read_frame(ctx->fmt_ctx, pkt) >= 0)
{
memset(&ctx->frame_info, 0, sizeof(SIMPLE_FRAME_INFO));
ctx->frame_info.pts = pkt->pts;
ctx->frame_info.dts = pkt->dts;
ctx->frame_info.length = pkt->size;
if (pkt->flags & AV_PKT_FLAG_KEY) {
ctx->frame_info.type = 0x01;
}
if (pkt->stream_index == ctx->video_stream_idx)
{
ctx->frame_info.codec = rtsp_channel->s_mediaInfo.u32VideoCodec;
if (ctx->pkt_cb)
{
ctx->pkt_cb(ctx->user_ptr, 1, pkt->data, &ctx->frame_info);
}
}
else if (pkt->stream_index == ctx->audio_stream_idx)
{
if (ctx->pkt_cb)
{
ctx->pkt_cb(ctx->user_ptr, 2, pkt->data, &ctx->frame_info);
}
}
av_packet_unref(pkt);
}
}
else if (ctx->src_type == SRC_TYPE_LOCAL_FILE) {
// 文件流:直接读出 AVPacket 回调
while (!ctx->thread_exit.load() && av_read_frame(ctx->fmt_ctx, pkt) >= 0)
{
memset(&ctx->frame_info, 0, sizeof(SIMPLE_FRAME_INFO));
if (pkt->stream_index == ctx->video_stream_idx)
{
ctx->frame_info.codec = rtsp_channel->s_mediaInfo.u32VideoCodec;
AVStream *av = ctx->fmt_ctx->streams[ctx->video_stream_idx];
ret = ConvertPacketToAnnexB(ctx->bsf_ctx, pkt, outPkt);
if (ret == 0) {
ctx->frame_info.pts = outPkt->pts;
ctx->frame_info.dts = outPkt->dts;
ctx->frame_info.length = outPkt->size;
if (outPkt->flags & AV_PKT_FLAG_KEY) {
ctx->frame_info.type = 0x01;
}
if (ctx->pkt_cb)
{
ctx->pkt_cb(ctx->user_ptr, 1, outPkt->data, &ctx->frame_info);
}
}else if (ret == AVERROR(EAGAIN))
{
// 当前包无输出,等待下一帧
}
av_usleep(rtsp_channel->s_mediaInfo.u32VideoFps*1000);
}
else if (pkt->stream_index == ctx->audio_stream_idx)
{
if (ctx->pkt_cb)
{
//ctx->pkt_cb(ctx->user_ptr, 2, pkt->data, &ctx->frame_info);
}
}
av_packet_unref(outPkt);
av_packet_unref(pkt);
}
}
else
{
// 线程初始化定义计数器
int64_t pts_counter = 0;
// USB摄像头:采集原始帧 -> 编码为 AVPacket -> 回调
//1.读取USB摄像头原始未压缩YUV AVPacket
while (!ctx->thread_exit.load() && av_read_frame(ctx->fmt_ctx, pkt) >= 0)
{
// 2. 将采集到的Packet数据填充到AVFrame
// 保证frame可写入
#if 1
if (av_frame_is_writable(frame) < 0)
{
av_log(nullptr,AV_LOG_ERROR,"frame is not writable\n");
if (int errnum = av_frame_make_writable(frame) < 0)
{
char buf[AV_ERROR_MAX_STRING_SIZE] = {0};
av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, errnum);
av_log(NULL, AV_LOG_ERROR, "av_frame_make_writable() call failed "
"error info:%s\n",buf);
av_packet_unref(pkt);
continue;
}
}
else
{
// av_log(nullptr,AV_LOG_INFO,"frame width:%d height:%d\n",frame->width,frame->height);
}
#endif
#if 0
uint8_t* src = pkt->data;
int src_off = 0;
// Y平面
for (int y = 0; y < frame->height; y++)
{
memcpy(frame->data[0] + y * frame->linesize[0], src + src_off, frame->width);
src_off += frame->width;
}
// U平面
int uv_h = frame->height / 2;
int uv_w = frame->width / 2;
for (int y = 0; y < uv_h; y++)
{
memcpy(frame->data[1] + y * frame->linesize[1], src + src_off, uv_w);
src_off += uv_w;
}
// V平面
for (int y = 0; y < uv_h; y++)
{
memcpy(frame->data[2] + y * frame->linesize[2], src + src_off, uv_w);
src_off += uv_w;
}
#else
uint8_t *src_data[1] = {pkt->data};
int src_linesize[1] = {pkt->size / ctx->cam_height};
sws_scale(ctx->sws_ctx, src_data, src_linesize, 0, ctx->cam_height, frame->data, frame->linesize);
# endif
// 强制连续递增PTS,消除时序警告
frame->pts = pts_counter++;
// 3. 送入编码器
int send_ret = avcodec_send_frame(ctx->enc_ctx, frame);
if (send_ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "send frame to encoder fail ret:%d\n", send_ret);
// av_log(nullptr,AV_LOG_ERROR, "frame width:%d height:%d\n", frame->width,frame->height);
av_packet_unref(pkt);
continue;
}else {
// av_log(nullptr,AV_LOG_INFO,"send frame to encoder success\n");
}
// 4. 循环取出编码后的H264/H265 Annex-B AVPacket
while (1)
{
av_packet_unref(outPkt);
int recv_ret = avcodec_receive_packet(ctx->enc_ctx, outPkt);
if (recv_ret == AVERROR(EAGAIN) || recv_ret == AVERROR_EOF)
{
break; // 当前无编码输出,等待下一帧
}
if (recv_ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "recv encode pkt fail ret:%d\n", recv_ret);
break;
}
// 填充帧信息
memset(&ctx->frame_info, 0, sizeof(SIMPLE_FRAME_INFO));
ctx->frame_info.pts = outPkt->pts;
ctx->frame_info.dts = outPkt->dts;
ctx->frame_info.length = outPkt->size;
ctx->frame_info.type = (outPkt->flags & AV_PKT_FLAG_KEY) ? 1 : 0;
ctx->frame_info.codec = rtsp_channel->s_mediaInfo.u32VideoCodec;
// 触发回调:补齐全部参数 (channelPtr, frameType, pBuf, dataLen, info)
if (ctx->pkt_cb)
{
ctx->pkt_cb(ctx->user_ptr, 1, outPkt->data, &ctx->frame_info);
}
}
av_packet_unref(pkt);
}
}
// 流断开,释放资源准备重连
pthread_mutex_lock(&ctx->ctx_mutex);
if (ctx->fmt_ctx)
{
avformat_close_input(&ctx->fmt_ctx);
ctx->fmt_ctx = NULL;
}
pthread_mutex_unlock(&ctx->ctx_mutex);
ctx->state.store(STREAM_ERROR);
if (!ctx->auto_reconn) break;
av_usleep(1000000);
}
printf("quit workThread\n");
av_packet_free(&pkt);
av_packet_free(&outPkt);
av_frame_free(&frame);
ctx->state.store(STREAM_IDLE);
return NULL;
}
int SimpleStreamClient_GetVideoExtraData(void *handle, uint8_t *outData, int maxLen) {
if (!handle || !outData || maxLen <= 0)
return -1;
StreamContext* ctx = (StreamContext*)handle;
pthread_mutex_lock(&ctx->ctx_mutex);
int copyLen = (ctx->video_extradata_len < maxLen) ? ctx->video_extradata_len : maxLen;
memcpy(outData, ctx->video_extradata, copyLen);
pthread_mutex_unlock(&ctx->ctx_mutex);
return copyLen;
}
四 已经验证功能
1.可以正常获取rtsp网络流数据
代码片断如下:
// 网络流:直接读出 AVPacket 回调 while (!ctx->thread_exit.load() && av_read_frame(ctx->fmt_ctx, pkt) >= 0) { memset(&ctx->frame_info, 0, sizeof(SIMPLE_FRAME_INFO)); ctx->frame_info.pts = pkt->pts; ctx->frame_info.dts = pkt->dts; ctx->frame_info.length = pkt->size; if (pkt->flags & AV_PKT_FLAG_KEY) { ctx->frame_info.type = 0x01; } if (pkt->stream_index == ctx->video_stream_idx) { ctx->frame_info.codec = rtsp_channel->s_mediaInfo.u32VideoCodec; if (ctx->pkt_cb) { ctx->pkt_cb(ctx->user_ptr, 1, pkt->data, &ctx->frame_info); } } else if (pkt->stream_index == ctx->audio_stream_idx) { if (ctx->pkt_cb) { ctx->pkt_cb(ctx->user_ptr, 2, pkt->data, &ctx->frame_info); } } av_packet_unref(pkt); }
2.可以正常获取mp4本地文件媒体流数据
代码片断如下:
// 文件流:直接读出 AVPacket 回调 while (!ctx->thread_exit.load() && av_read_frame(ctx->fmt_ctx, pkt) >= 0) { memset(&ctx->frame_info, 0, sizeof(SIMPLE_FRAME_INFO)); if (pkt->stream_index == ctx->video_stream_idx) { ctx->frame_info.codec = rtsp_channel->s_mediaInfo.u32VideoCodec; AVStream *av = ctx->fmt_ctx->streams[ctx->video_stream_idx]; ret = ConvertPacketToAnnexB(ctx->bsf_ctx, pkt, outPkt); if (ret == 0) { ctx->frame_info.pts = outPkt->pts; ctx->frame_info.dts = outPkt->dts; ctx->frame_info.length = outPkt->size; if (outPkt->flags & AV_PKT_FLAG_KEY) { ctx->frame_info.type = 0x01; } if (ctx->pkt_cb) { ctx->pkt_cb(ctx->user_ptr, 1, outPkt->data, &ctx->frame_info); } }else if (ret == AVERROR(EAGAIN)) { // 当前包无输出,等待下一帧 } av_usleep(rtsp_channel->s_mediaInfo.u32VideoFps*1000); } else if (pkt->stream_index == ctx->audio_stream_idx) { if (ctx->pkt_cb) { //ctx->pkt_cb(ctx->user_ptr, 2, pkt->data, &ctx->frame_info); } } av_packet_unref(outPkt); av_packet_unref(pkt); }
3.支持将mp4文件的AVCC格式数据包,转为Annex-B数据包
代码片断如下:
/** * @brief 转换1帧MP4 AVCC/HVCC packet为Annex-B packet * @param bsfCtx 已初始化的BSF上下文 * @param inPkt 输入mp4原始packet * @param outPkt 输出Annex-B标准packet(调用者av_packet_unref释放) * @return 0成功;AVERROR(EAGAIN)无输出包;负数失败 */ int ConvertPacketToAnnexB(AVBSFContext* bsfCtx, const AVPacket* inPkt, AVPacket* outPkt) { av_packet_unref(outPkt); // 送入过滤器 int ret = av_bsf_send_packet(bsfCtx, (AVPacket*)inPkt); if (ret < 0) return ret; // 取出转换后的Annex-B包 ret = av_bsf_receive_packet(bsfCtx, outPkt); return ret; }
4.支持获取USB 相机采集的视频流数据
代码片断如下:
// 线程初始化定义计数器 int64_t pts_counter = 0; // USB摄像头:采集原始帧 -> 编码为 AVPacket -> 回调 //1.读取USB摄像头原始未压缩YUV AVPacket while (!ctx->thread_exit.load() && av_read_frame(ctx->fmt_ctx, pkt) >= 0) { // 2. 将采集到的Packet数据填充到AVFrame // 保证frame可写入 #if 1 if (av_frame_is_writable(frame) < 0) { av_log(nullptr,AV_LOG_ERROR,"frame is not writable\n"); if (int errnum = av_frame_make_writable(frame) < 0) { char buf[AV_ERROR_MAX_STRING_SIZE] = {0}; av_make_error_string(buf, AV_ERROR_MAX_STRING_SIZE, errnum); av_log(NULL, AV_LOG_ERROR, "av_frame_make_writable() call failed " "error info:%s\n",buf); av_packet_unref(pkt); continue; } } else { // av_log(nullptr,AV_LOG_INFO,"frame width:%d height:%d\n",frame->width,frame->height); } #endif #if 0 uint8_t* src = pkt->data; int src_off = 0; // Y平面 for (int y = 0; y < frame->height; y++) { memcpy(frame->data[0] + y * frame->linesize[0], src + src_off, frame->width); src_off += frame->width; } // U平面 int uv_h = frame->height / 2; int uv_w = frame->width / 2; for (int y = 0; y < uv_h; y++) { memcpy(frame->data[1] + y * frame->linesize[1], src + src_off, uv_w); src_off += uv_w; } // V平面 for (int y = 0; y < uv_h; y++) { memcpy(frame->data[2] + y * frame->linesize[2], src + src_off, uv_w); src_off += uv_w; } #else uint8_t *src_data[1] = {pkt->data}; int src_linesize[1] = {pkt->size / ctx->cam_height}; sws_scale(ctx->sws_ctx, src_data, src_linesize, 0, ctx->cam_height, frame->data, frame->linesize); # endif // 强制连续递增PTS,消除时序警告 frame->pts = pts_counter++; // 3. 送入编码器 int send_ret = avcodec_send_frame(ctx->enc_ctx, frame); if (send_ret < 0) { av_log(NULL, AV_LOG_ERROR, "send frame to encoder fail ret:%d\n", send_ret); // av_log(nullptr,AV_LOG_ERROR, "frame width:%d height:%d\n", frame->width,frame->height); av_packet_unref(pkt); continue; }else { // av_log(nullptr,AV_LOG_INFO,"send frame to encoder success\n"); } // 4. 循环取出编码后的H264/H265 Annex-B AVPacket while (1) { av_packet_unref(outPkt); int recv_ret = avcodec_receive_packet(ctx->enc_ctx, outPkt); if (recv_ret == AVERROR(EAGAIN) || recv_ret == AVERROR_EOF) { break; // 当前无编码输出,等待下一帧 } if (recv_ret < 0) { av_log(NULL, AV_LOG_ERROR, "recv encode pkt fail ret:%d\n", recv_ret); break; } // 填充帧信息 memset(&ctx->frame_info, 0, sizeof(SIMPLE_FRAME_INFO)); ctx->frame_info.pts = outPkt->pts; ctx->frame_info.dts = outPkt->dts; ctx->frame_info.length = outPkt->size; ctx->frame_info.type = (outPkt->flags & AV_PKT_FLAG_KEY) ? 1 : 0; ctx->frame_info.codec = rtsp_channel->s_mediaInfo.u32VideoCodec; // 触发回调:补齐全部参数 (channelPtr, frameType, pBuf, dataLen, info) if (ctx->pkt_cb) { ctx->pkt_cb(ctx->user_ptr, 1, outPkt->data, &ctx->frame_info); } } av_packet_unref(pkt); }