近日,米尔电子发布MYC-LR3568核心板及开发板,核心板基于高性能、低功耗的国产芯片-瑞芯微RK3568。核心
发表于 08-02 08:01
•287次阅读
:风火轮科技的YY3568开发板。 YY3568主板基于 Rockchip RK3568 芯片平台,四核 64位 Cortex-A55 核,
发表于 03-07 08:41
•1528次阅读
YY3568主板基于 Rockchip RK3568 芯片平台,四核 64位 Cortex-A55 核,主频最高达 2GHz,集成双核心架构GPU以及高效能NPU,芯片性能优异。
发表于 03-06 18:17
•4260次阅读
这次非常有幸,能够得到深圳风火轮youyeetoo X1的体验机会,感谢电子发烧友和风火轮。
在申请youyeetoo X1之前,已经通过风火轮的官方WiKi做过了一下了解,官方的介绍简介给力
发表于 02-01 00:20
前言
很荣幸有机会参与到我们电子发烧友论坛平台的试用活动,也感谢深圳风火轮科技提供了这一次宝贵的机会!提供了youyeetoo X1 windows这么一款优秀开发板。闲话少说,我们快入正文;
官网
发表于 01-26 17:58
感谢电子发烧友和深圳风火轮科技的信任,本次参加youyeetoo X1 Windows 开发板的评测。
开发板特点
youyeetoo X1 是一款由深圳风火轮科技推出的x86架构单板
发表于 01-26 16:32
在2023年的尾声之际,风火轮科技如约而至,再次为我们带来惊喜。继前两款备受赞誉的开发板之后,他们再次推出了全新的第三款开发板--youyeetoo R1,为科技领域注入一股清新的活力。
发表于 01-02 14:08
•1136次阅读
YY3568开发板是 「风火轮科技」 基于Rockchip RK3568 芯片平台设计的开发板,四核 64Cortex-A55 核,主频最高
发表于 11-28 10:59
•1801次阅读
软件开发版本。而最新版的HomeAssistant是2023.11.2,要求Python要3.11以上版本,但是YY3568的Debian-10系统自带的Python为3.9,更新软件仓库后依然没有
发表于 11-14 11:49
,并成功在自己的开发笔记本,以及手机(Android-12)上运行,因此想测试一下能否在YY3568的Android-11系统上运行。
根据YY3568的wiki上的内容,使用adb方式进行apk安装
发表于 11-14 11:44
号外,号外。。。深圳风火轮科技又推出新品啦--youyeetoo X1。youyeetoo X1 是一款由深圳风火轮科技推出的x86架构单板电脑(SBC),可运行全功能版的windows和Linux
发表于 10-12 18:01
•1881次阅读
号外,号外。。。 深圳风火轮科技又推出新品啦--youyeetoo X1。 youyeetoo X1 是一款由深圳风火轮科技推出的x86架构单板电脑(SBC),可运行全功能版的windows
发表于 10-10 10:22
•838次阅读
在上一篇,我们已经成功使用FFMPEG通过命令行的方式进行了RTMP的直播,其实FFMPEG还可以通过API的方式进行编程,并且使用API的方式,那么拥有着更多的灵活性,这也是为什么要自编译ffmpeg,而不是直接使用APT命令安装的方式来运行它。我们最终的目标是使用RKMPP来将视频进行编码,然后通过FFMPEG发送到RTMP服务器上进行直播,那么方案有两种,一种就是修改FFMPEG源码进行编译,第二种方式就只能通过API编程来进行了。
那么这一篇的任务有两方面:
1.因为我们要使用ffmpeg推RTMP流,那么我们先不继承RKMPP,先从RTSP进行拉流,然后使用软解软编后进行推流,使用纯C编写。
2.在Linux下面的软件编译,gcc hello.c -o hello一般只用来编译hello world,但是在实际编译过程中我们常常需要引入其他的库来进行编译,所以需要增加一些参数。
废话就说到这里吧,上代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include \"libavcodec/avcodec.h\"
#include \"libavformat/avformat.h\"
#include \"libavfilter/avfilter.h\"
#include \"libavfilter/buffersink.h\"
#include \"libavfilter/buffersrc.h\"
#include \"libavutil/avutil.h\"
#include \"libavdevice/avdevice.h\"
#include \"libavutil/audio_fifo.h\"
#include \"libavutil/mathematics.h\"
#include \"libswscale/swscale.h\"
#include \"libavutil/imgutils.h\"
#include \"libavutil/time.h\"
#include \"libswresample/swresample.h\"
const char input_link[] = \"rtsp://admin:a1234567@192.168.1.66:554/h264/ch1/sub/av_stream\";
const char output_link[] = \"rtmp://192.168.1.103:8910/rtmplive/cctv\";
const int width = 640, height = 480, fps = 25;
int main(int argc, char* argv[]) {
inti_video_output_stream = -1;
int64_t i_video_frame = 0;
avformat_network_init();
avdevice_register_all();
AVFrame *p_frame = av_frame_alloc();
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
AVDictionary* options = NULL;
av_dict_set(&options, \"buffer_size\", \"33554432\", 0);
av_dict_set(&options, \"max_delay\", \"800000\", 0);
av_dict_set(&options, \"stimeout\", \"20000000\", 0);//设置超时断开连接时间
av_dict_set(&options, \"rtsp_transport\", \"tcp\", 0);//tcp方式打开
AVFormatContext *p_video_input_format_ctx = avformat_alloc_context();
AVStream *p_video_input_stream = NULL;
if (avformat_open_input(&p_video_input_format_ctx, input_link, NULL, &options) != 0) {
printf(\"error: input stream open fail!\");
return -1;
}
if (avformat_find_stream_info(p_video_input_format_ctx, NULL) < 0) {
printf(\"error: couldn\'t find stream information.\\\\n\");
return -1;
}
for (int i = 0; i < p_video_input_format_ctx->nb_streams; i++) {
if (p_video_input_format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
p_video_input_stream = p_video_input_format_ctx->streams[i];
break;
}
}
if (p_video_input_stream == NULL) {
printf(\"error: couldn\'t find video stream.\\\\n\");
return -1;
}
const AVCodec *p_video_input_codec = avcodec_find_decoder(p_video_input_stream->codecpar->codec_id);
AVCodecContext *p_video_input_codec_ctx = avcodec_alloc_context3(p_video_input_codec);
if (avcodec_open2(p_video_input_codec_ctx, p_video_input_codec, NULL) < 0) {
printf(\"error: couldn\'t open codec.\\\\n\");
return -1;
}
printf(\"----------Video Input Information----------\\\\n\");
av_dump_format(p_video_input_format_ctx, 0, NULL, 0);
printf(\"-------------------------------------------\\\\n\");
AVFormatContext *p_output_format_ctx;
if (avformat_alloc_output_context2(&p_output_format_ctx, 0, \"flv\", output_link) != 0) {
printf(\"error: avformat_alloc_output_context2!\\\\n\");
return -1;
}
const AVCodec *p_video_output_codec = avcodec_find_encoder_by_name(\"libx264\");
if (!p_video_output_codec){
printf(\"error: couldn\'t find encoder!\\\\n\");
return -1;
}
AVCodecContext *p_video_output_codec_ctx = avcodec_alloc_context3(p_video_output_codec);
if (!p_video_output_codec_ctx) {
printf(\"error: avcodec_alloc_context3 failed!\\\\n\");
return -1;
}
p_video_output_codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //全局参数
p_video_output_codec_ctx->codec_id = p_video_output_codec->id;
p_video_output_codec_ctx->bit_rate = 512 * 1024;//比特率
p_video_output_codec_ctx->width = width;
p_video_output_codec_ctx->height = height;
p_video_output_codec_ctx->time_base.num = 1;
p_video_output_codec_ctx->time_base.den = fps;
p_video_output_codec_ctx->framerate.num = fps;
p_video_output_codec_ctx->framerate.den = 1;
p_video_output_codec_ctx->gop_size = 50;
p_video_output_codec_ctx->max_b_frames = 0;
p_video_output_codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
// Set H264 preset and tune
AVDictionary *param = 0;
if (p_video_output_codec_ctx->codec_id == AV_CODEC_ID_H264) {
// 这个非常重要,如果不设置延时非常的大 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo 是x264编码速度的选项
av_dict_set(¶m, \"preset\", \"superfast\", 0);
av_dict_set(¶m, \"tune\", \"zerolatency\", 0);
}
if (avcodec_open2(p_video_output_codec_ctx, p_video_output_codec, ¶m) < 0){
printf(\"error: couldn\'t open encoder!\\\\n\");
return -1;
}
//添加视频流
AVStream *p_video_output_stream = avformat_new_stream(p_output_format_ctx, p_video_output_codec);
if (!p_video_output_stream) {
printf(\"error: avformat_new_stream failed!\\\\n\");
return -1;
}
//附加标志,这个一定要设置
p_video_output_stream->codecpar->codec_tag = 0;
//从编码器复制参数
avcodec_parameters_from_context(p_video_output_stream->codecpar, p_video_output_codec_ctx);
///打开rtmp 的网络输出IOAVIOContext:输入输出对应的结构体,用于输入输出(读写文件,RTMP协议等)。
if (avio_open(&p_output_format_ctx->pb, output_link, AVIO_FLAG_WRITE) != 0) {
printf(\"error: avio_open failed!\\\\n\");
return -1;
}
//写入封装头
if (avformat_write_header(p_output_format_ctx, NULL) != 0) {
printf(\"error: avformat_write_header failed!\\\\n\");
return -1;
}
for (int i = 0; i<p_output_format_ctx->nb_streams; i++) {
if (p_output_format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
i_video_output_stream = i;
}
}
printf(\"----------Output Information----------\\\\n\");
av_dump_format(p_output_format_ctx, 0, output_link, 1);
printf(\"-------------------------------------------\\\\n\");
//int iVideoFrameSize = avpicture_get_size(pVideoOutputCodecCtx->pix_fmt, pVideoOutputCodecCtx->width, pVideoOutputCodecCtx->height);
//uint8_t *pPictureBuf = new uint8_t[iVideoFrameSize];
int video_duration = (p_output_format_ctx->streams[i_video_output_stream]->time_base.den / p_output_format_ctx->streams[i_video_output_stream]->time_base.num) / fps;
while (1) {
if (av_read_frame(p_video_input_format_ctx, packet) >= 0){
if (avcodec_send_packet(p_video_input_codec_ctx, packet) < 0 || avcodec_receive_frame(p_video_input_codec_ctx, p_frame) < 0) {
printf(\"error: decode video failed!\\\\n\");
continue;
}
av_packet_unref(packet);
packet = av_packet_alloc();
//packet->data = NULL;
//packet->size = 0;
if (avcodec_send_frame(p_video_output_codec_ctx, p_frame) != 0) {
printf(\"error: send frame failed!\\\\n\");
continue;
}
av_frame_unref(p_frame);
if (avcodec_receive_packet(p_video_output_codec_ctx, packet) != 0 || packet->size <= 0) {
printf(\"error: avcodec_receive_packet failed!\\\\n\");
continue;
}
packet->stream_index = i_video_output_stream;
packet->pts = i_video_frame * video_duration;
packet->dts = i_video_frame * video_duration;
packet->duration = video_duration;
if (av_interleaved_write_frame(p_output_format_ctx, packet) < 0) {
printf(\"error: av_interleaved_write_frame failed!\\\\n\");
}
av_packet_unref(packet);
i_video_frame++;
av_frame_unref(p_frame);
av_packet_unref(packet);
}
usleep(10000);
}
return 0;
}
然后,我们进行编译,编译的命令放在下面,注意的是链接库最好按照我的顺序来,不然可能会出问题的。
可以看到,甚至连一个警告都没有,完美!
接下来我们就开始运行吧:
运行到这里可以看到我们让输出input信息和output信息还有编码器的信息已经成功输出了,但是输出后就好像卡死了,但是其实是它在正常工作,咱们没有继续让他进行输出啊,所以如果希望改进的话,你可以让他增加一些统计信息,但是可以50帧左右,也就是2秒输出一下就可以了,一方面这样看起来整洁容易看出问题,另外一方面也可以减轻CPU的负担。
使用top命令看一下CPU的使用率,不过才76%,如果满载的话将会是400%,可以说是即使是软编,那么也处理640x480的视频很轻松了。
实际测试效果图如上,为了隐私我打上了马赛克,不过可以看到时间的。
视频信息如上,可以看到软件已经推流成功了,完美!
说明
在这个程序中直接把链接写死在程序里了,其实相对来讲,我更建议大家通过启动时通过命令行后面的参数进行传入,在程序里直接就可以通过argv就可以进行读取了,这个是支持的,我已经写好在main里面了。
这个程序是可以读取本地文件,也可以生成本地文件,还可以读本地文件推流RTMP,也可以读RTSP生成本地文件,例如要生成本地文件的话,可以将代码中的第20行修改为:const char output_link[] = \"cctv.flv\";.
本程序只做了视频部分,因为视频部分比较占用CPU资源,音频部分占用小,而且视频部分代码比较简单好理解,后期增加音频流也基本一致,但是需要注意的有可能frame和packet并不是一一对应的,发送出去以后一定要用while来收回,还有就是注意时间轴的同步。
本程序使用的是纯C来进行编写的,主要是程序比较小,而且ffmpeg也是使用纯C写的,可以看到文件也都写在了一起,如果做的功能比较多,还是建议使用C++使用面向对象来完成,引入头文件时记的添加extern \"C\",而且要用到多线程,包括视频解码线程,音频解码线程以及编码线程等等。
要使用音视频的缓存AVFifoBuffer。
增加QT界面,同时最好使用QT的项目管理,这样不用每次都输入那么多的参数了。如果不适用QT,不做界面,那么建议使用Makefile来管理项目。
发表于 09-25 15:33
评论