当前位置: 首页 > news >正文

项目1:高分辨率(1920 * 1080)编码码流推送流媒体讲解

一.本章节内容:

本章节将详细介绍如何通过高分辨率队列处理每一帧数据,并利用FFMPEG推流器将数据传输至高码流分辨率流媒体服务器。相关代码实现位于rkmedia_assignment_manage.cpp和rkmedia_data_process.cpp文件中。

二.高分辨率编码码流推流的流程

高分辨率推流过程可分为以下6个步骤:

  1. 初始化RKMEDIA_FFMPEG_CONFIG结构体
  2. 调用init_rkmedia_ffmpeg_context设置1920×1080推流器
  3. 创建high_video_push_thread线程
  4. 从HIGH_VIDEO_QUEUE队列获取视频帧数据
  5. 计算并转换AVPacket的PTS时间基
  6. 使用FFMPEG API推送视频帧到流媒体服务器

接下来我们将详细讲解每个步骤的具体实现。

2.1. 初始化RKMEDIA_FFMPEG_CONFIG结构体

typedef struct
{AVStream *stream; AVCodecContext *enc;int64_t next_timestamp;int samples_count;AVPacket *packet;
} OutputStream;typedef struct
{int width;int height;unsigned int config_id;int protocol_type; //流媒体TYPEchar network_addr[NETWORK_ADDR_LENGTH];//流媒体地址enum AVCodecID video_codec; //视频编码器IDenum AVCodecID audio_codec; //音频编码器IDOutputStream video_stream; //VIDEO的STREAM配置OutputStream audio_stream; //AUDIO的STREAM配置AVFormatContext *oc; //是存储音视频封装格式中包含的信息的结构体,也是FFmpeg中统领全局的结构体,对文件的封装、编码操作从这里开始。} RKMEDIA_FFMPEG_CONFIG; //FFMPEG配置

我们来看看RKMEDIA_FFMPEG_CONFIG的成员变量

2.2.1. width:推流器的width,width和rv1126编码器的width一致

2.2.2. height:推流器的height,height和rv1126编码器的height一致

2.2.3. config_id:config_id,暂时没用到

2.2.4. protocol_type:流媒体的类型

2.2.5.network_addr:流媒体地址

2.2.6.video_codec:视频编码器ID
2.2.7.audio_codec:音频编码器ID

2.2.8.video_stream:自定义VIDEO的STREAM结构体配置

2.2.9.audio_stream:自定义AUDIO的STREAM结构体配置

  int ret;RKMEDIA_FFMPEG_CONFIG *ffmpeg_config = (RKMEDIA_FFMPEG_CONFIG *)malloc(sizeof(RKMEDIA_FFMPEG_CONFIG));if (ffmpeg_config == NULL){printf("malloc ffmpeg_config failed\n");}ffmpeg_config->width = 1920;ffmpeg_config->height = 1080;ffmpeg_config->config_id = 0;ffmpeg_config->protocol_type = protocol_type;ffmpeg_config->video_codec = AV_CODEC_ID_H264;ffmpeg_config->audio_codec = AV_CODEC_ID_AAC;

上面是高分辨率rkmedia_ffmpeg_config的设置

2.2. 调用init_rkmedia_ffmpeg_context来初始化1920 * 1080推流器

 //初始化ffmpeg输出模块init_rkmedia_ffmpeg_context(ffmpeg_config);

init_rkmedia_ffmpeg_context是初始化rkmedia_ffmpeg_config的设置,关于这个函数的内容在之前的文章已经说了。这里不做过多的介绍

2.3. 创建high_video_push_thread线程

    //创建HIGH_PUSH线程ret = pthread_create(&pid, NULL, high_video_push_thread, (void *)ffmpeg_config);if (ret != 0){printf("push_server_thread error\n");}

high_video_push_thread的核心功能是从HIGH_VIDEO_QUEUE队列中提取1920×1080分辨率的H264编码视频帧。该线程首先将每帧H264码流数据封装到AVPacket结构中,随后通过调用FFMPEG接口将这些视频数据推送至流媒体服务器。

2.4. 从HIGH_VIDEO_QUEUE获取每一帧H264数据码流并且赋值到AVPacket

// 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中
AVPacket *get_high_ffmpeg_video_avpacket(AVPacket *pkt)
{video_data_packet_t *video_data_packet = high_video_queue->getVideoPacketQueue(); // 从视频队列获取数据if (video_data_packet != NULL){/*重新分配给定的缓冲区1.  如果入参的 AVBufferRef 为空,直接调用 av_realloc 分配一个新的缓存区,并调用 av_buffer_create 返回一个新的 AVBufferRef 结构;2.  如果入参的缓存区长度和入参 size 相等,直接返回 0;3.  如果对应的 AVBuffer 设置了 BUFFER_FLAG_REALLOCATABLE 标志,或者不可写,再或者 AVBufferRef data 字段指向的数据地址和 AVBuffer 的 data 地址不同,递归调用 av_buffer_realloc 分配一个新
的 buffer,并将 data 拷贝过去;4.  不满足上面的条件,直接调用 av_realloc 重新分配缓存区。*//*首先用av_buffer_realloc分配每一个缓冲区数据。要注意的是AVPacket中缓冲区的buf是不能直接赋值的,如: memcpy(pkt->data, video_data_packet->buffer, video_data_packet->frame_size)否则程序就会出现core_dump情况。我们需要先把video_data_packet_t的视频数据(video_data_packet->buffer)先拷贝到pkt->buf->data,然后再把pkt->buf->data的数据赋值到pkt->data。*/int ret = av_buffer_realloc(&pkt->buf, video_data_packet->video_frame_size + 70);//分配一个新的缓存区if (ret < 0){return NULL;}pkt->size = video_data_packet->video_frame_size;                                        // rv1126的视频长度赋值到AVPacket Sizememcpy(pkt->buf->data, video_data_packet->buffer, video_data_packet->video_frame_size); // rv1126的视频数据赋值到AVPacket datapkt->data = pkt->buf->data;                                                             // 把pkt->buf->data赋值到pkt->datapkt->flags |= AV_PKT_FLAG_KEY;                                                          // 默认flags是AV_PKT_FLAG_KEY,强制标记当前帧为关键帧(I帧)if (video_data_packet != NULL){free(video_data_packet);video_data_packet = NULL;}return pkt;}else{return NULL;}
}int write_ffmpeg_avpacket(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{/*返回值类型 int:FFmpeg标准错误码(0成功,负值失败)。参数 AVFormatContext *fmt_ctx:FFmpeg格式上下文,管理输出文件格式(如MP4/TS)、IO操作及全局元数据。参数 const AVRational *time_base:编码器时间基(如{1, 90000}),用于时间戳单位转换。参数 AVStream *st:目标输出流(视频流),包含流索引、时间基、编码参数等。参数 AVPacket *pkt:待写入的视频数据包,需包含有效数据、时间戳及流索引。*//*将数据包的时间戳(PTS/DTS)从编码器时间基转换为流时间基*/av_packet_rescale_ts(pkt, *time_base, st->time_base);pkt->stream_index = st->index;//将数据包绑定到目标流。FFmpeg通过stream_index识别数据包所 属流(视频/音频/字幕等)//将数据包写入输出文件,并自动处理数据包交织(确保音视频帧按时间顺序排列)return av_interleaved_write_frame(fmt_ctx, pkt);
}int deal_high_video_avpacket(AVFormatContext *oc, OutputStream *ost)
{int ret;AVCodecContext *c = ost->enc;//指向编码器上下文的指针,用于获取编码参数(如时间基time_base)。AVPacket *video_packet = get_high_ffmpeg_video_avpacket(ost->packet); // 从RV1126视频编码数据赋值到FFMPEG的Video AVPacket中,调用前述函数将硬件数据转为FFmpeg格式。if (video_packet != NULL){video_packet->pts = ost->next_timestamp++; // VIDEO_PTS按照帧率进行累加,显示时间戳,单位由编码器时间基决定,这里其实在最早的FFMPEG初始化帧率已经设置过了}ret = write_ffmpeg_avpacket(oc, &c->time_base, ost->stream, video_packet); // 向复合流写入视频数据if (ret != 0){printf("write video avpacket error");return -1;}return 0;
}

这段代码实现了从HIGH_VIDEO_QUEUE队列获取1920*1080分辨率的H264视频帧数据,并将其封装到AVPacket中。相关功能已封装在deal_high_video_packet函数内。

函数的核心功能包括:

  1. 从队列获取视频帧数据
  2. 将数据填充到AVPacket结构体中

其中关键处理环节是video_data_packet到AVPacket的数据转换,主要涉及两个操作:

  • AVPacket缓冲区数据的填充
  • AVPacket数据长度的设置

2.4.1. 我们先来看看AVPacket缓冲区的赋值:

首先用av_buffer_realloc分配每一个缓冲区数据。要注意的是AVPacket中缓冲区的buf是不能直接赋值的,如: memcpy(pkt->data, video_data_packet->buffer, video_data_packet->frame_size)否则程序就会出现core_dump情况。我们需要先把video_data_packet_t的视频数据(video_data_packet->buffer)先拷贝到pkt->buf->data,然后再把pkt->buf->data的数据赋值到pkt->data。

 int ret = av_buffer_realloc(&pkt->buf, video_data_packet->video_frame_size + 70);//分配一个新的缓存区if (ret < 0){return NULL;}pkt->size = video_data_packet->video_frame_size;                                        // rv1126的视频长度赋值到AVPacket Sizememcpy(pkt->buf->data, video_data_packet->buffer, video_data_packet->video_frame_size); // rv1126的视频数据赋值到AVPacket datapkt->data = pkt->buf->data;                                                             // 把pkt->buf->data赋值到pkt->data

2.4.2.AVPacket关键帧标识符的赋值

添加了这个标识符后,每个AVPacket中都进行关键帧设置,这个标识符必须要加,否则播放器则无法正常解码出视频。

pkt->flags |= AV_PKT_FLAG_KEY;                                                          // 默认flags是AV_PKT_FLAG_KEY,强制标记当前帧为关键帧(I帧)

2.5.每一帧AVPacket计算PTS时间戳

根据AVPacket的数据去计算视频的PTS,若AVPacket的数据不为空。则让视频pts = ost->next_timestamp++(关于video的PTS计算,上一章节已经讲了)

 if (video_packet != NULL){video_packet->pts = ost->next_timestamp++; // VIDEO_PTS按照帧率进行累加,显示时间戳,单位由编码器时间基决定,这里其实在最早的FFMPEG初始化帧率已经设置过了}

把视频PTS进行时间基的转换,调用av_packet_rescale_ts把采集的视频时间基转换成复合流的时间基。

2.6. 把每一帧视频数据传输到流媒体服务器

完成时间基转换后,视频数据将被写入复合流文件。该操作通过调用av_interleaved_write_frame API实现(注:复合流文件可以是本地文件或流媒体地址)。


int write_ffmpeg_avpacket(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{/*返回值类型 int:FFmpeg标准错误码(0成功,负值失败)。参数 AVFormatContext *fmt_ctx:FFmpeg格式上下文,管理输出文件格式(如MP4/TS)、IO操作及全局元数据。参数 const AVRational *time_base:编码器时间基(如{1, 90000}),用于时间戳单位转换。参数 AVStream *st:目标输出流(视频流),包含流索引、时间基、编码参数等。参数 AVPacket *pkt:待写入的视频数据包,需包含有效数据、时间戳及流索引。 *//*将数据包的时间戳(PTS/DTS)从编码器时间基转换为流时间基*/av_packet_rescale_ts(pkt, *time_base, st->time_base);pkt->stream_index = st->index;//将数据包写入输出文件,并自动处理数据包交织(确保音视频帧按时间顺序排列)//将数据包写入输出文件,并自动处理数据包交织(确保音视频帧按时间顺序排列)/*第一个参数:AVFormatContext结构体指针	第二个参数:AVPacket结构体指针,在我们这个项目里面AVPacket存储RV1126的编码数据。返回值:成功==0,失败-22*/return av_interleaved_write_frame(fmt_ctx, pkt);
}

初始化完成后,即可通过输出模块向流媒体服务器推送数据流。在FFmpeg中,通常使用av_interleaved_write_frame函数进行推流操作。该函数的主要功能是将经过压缩的音视频数据(如AAC、MP3音频和H.264/H.265视频)以交错方式写入复合流文件。该复合流文件既可以是本地文件,也可以作为流媒体数据输出。需要特别注意的是,av_interleaved_write_frame函数会自动对AVPacket的PTS进行合法性校验,并执行缓存检查。

图解:

http://www.dtcms.com/a/482174.html

相关文章:

  • [嵌入式系统-109]:GPU与NPU的比较
  • 算法入门:专题攻克一---双指针4(三数之和,四数之和)强推好题,极其锻炼算法思维
  • 比较好的网页设计网站wordpress salient 8
  • 建设网站都需要哪些资料佛山做网站的公司
  • 198种组合算法+优化CNN卷积神经网络+SHAP分析+新数据预测+多输出!深度学习可解释分析,强烈安利,粉丝必备!
  • 深度学习基础模块
  • 仿muduo库的高并发服务器
  • DNS优选 2.8.2 | 优选最快DNS,访问受限网站,去网站广告
  • 网络编程就是做网站么枣庄网页制作公司
  • 【目标跟踪n雷达二维EKF】雷达对单目标跟踪,滤波(使用扩展卡尔曼)增强定位能力,二维,目标状态未知,雷达数量可调。给出MATLAB代码
  • 从鉴酱酒:传承文化,品味佳酿
  • 响应式网站开发图标wordpress 注册 登陆不了
  • 如何在 MySQL 中实现慢查询监控
  • Python 切片的核心概念
  • Linux用户空间/内核空间获取用户空间地址的页表
  • AB Download Manager(下载管理工具) 中文绿色版
  • 深圳建设网站公司排名网页制作作业网站
  • Python3 AI 编程助手
  • C# WPS操作PPT,全屏,缩率图,备注,跳转播放
  • 医药公司网站建设备案网站做戒酒通知书
  • 高效存储大List对象到Redis的解决方案,使用分片存储和压缩技术
  • 阿德莱德学习推理与导航!PEAP-LLM:基于大语言模型的参数高效动作规划
  • 科技赋能畜牧业|小吉快检 BL-08plus 推动行业数字化转型
  • Qt多线程渲染架构设计与实现思考
  • 亚马逊云科技 WAF 指南(十)用 Amazon Q Developer CLI 解决 DDoS 防护与 SEO 冲突问题
  • 网络营销是什么 能做什么seo项目经理
  • 咨询行业网站建设公司太仓市建设局网站
  • 自己开外销网站怎么做手机分销网站
  • 那个网站可以做ppt赚钱建设银行网站查询密码怎么开通
  • EI输入整形振动抑制方法介绍