近日,米爾電子發(fā)布MYC-LR3568核心板及開(kāi)發(fā)板,核心板基于高性能、低功耗的國(guó)產(chǎn)芯片-瑞芯微RK3568。核心
發(fā)表于 08-02 08:01
?284次閱讀
RK3568開(kāi)發(fā)板支持AMP雙系統(tǒng)
發(fā)表于 06-04 15:33
?425次閱讀
:風(fēng)火輪科技的YY3568開(kāi)發(fā)板。 YY3568主板基于 Rockchip RK3568 芯片平臺(tái),四核 64位 Cortex-A55 核,
發(fā)表于 03-07 08:41
?1528次閱讀
YY3568主板基于 Rockchip RK3568 芯片平臺(tái),四核 64位 Cortex-A55 核,主頻最高達(dá) 2GHz,集成雙核心架構(gòu)GPU以及高效能NPU,芯片性能優(yōu)異。
發(fā)表于 03-06 18:17
?4248次閱讀
這次非常有幸,能夠得到深圳風(fēng)火輪youyeetoo X1的體驗(yàn)機(jī)會(huì),感謝電子發(fā)燒友和風(fēng)火輪。
在申請(qǐng)youyeetoo X1之前,已經(jīng)通過(guò)風(fēng)火輪的官方WiKi做過(guò)了一下了解,官方的介紹簡(jiǎn)介給力
發(fā)表于 02-01 00:20
前言
很榮幸有機(jī)會(huì)參與到我們電子發(fā)燒友論壇平臺(tái)的試用活動(dòng),也感謝深圳風(fēng)火輪科技提供了這一次寶貴的機(jī)會(huì)!提供了youyeetoo X1 windows這么一款優(yōu)秀開(kāi)發(fā)板。閑話(huà)少說(shuō),我們快入正文;
官網(wǎng)
發(fā)表于 01-26 17:58
感謝電子發(fā)燒友和深圳風(fēng)火輪科技的信任,本次參加youyeetoo X1 Windows 開(kāi)發(fā)板的評(píng)測(cè)。
開(kāi)發(fā)板特點(diǎn)
youyeetoo X1 是一款由深圳風(fēng)火輪科技推出的x86架構(gòu)單板
發(fā)表于 01-26 16:32
BQ3568_使用說(shuō)明_開(kāi)發(fā)板詳情
發(fā)表于 01-10 17:06
?449次閱讀
在2023年的尾聲之際,風(fēng)火輪科技如約而至,再次為我們帶來(lái)驚喜。繼前兩款備受贊譽(yù)的開(kāi)發(fā)板之后,他們?cè)俅瓮瞥隽巳碌牡谌?b class='flag-5'>開(kāi)發(fā)板--youyeetoo R1,為科技領(lǐng)域注入一股清新的活力。
發(fā)表于 01-02 14:08
?1129次閱讀
YY3568開(kāi)發(fā)板是 「風(fēng)火輪科技」 基于Rockchip RK3568 芯片平臺(tái)設(shè)計(jì)的開(kāi)發(fā)板,四核 64Cortex-A55 核,主頻最高
發(fā)表于 11-28 10:59
?1800次閱讀
軟件開(kāi)發(fā)版本。而最新版的HomeAssistant是2023.11.2,要求Python要3.11以上版本,但是YY3568的Debian-10系統(tǒng)自帶的Python為3.9,更新軟件倉(cāng)庫(kù)后依然沒(méi)有
發(fā)表于 11-14 11:49
,并成功在自己的開(kāi)發(fā)筆記本,以及手機(jī)(Android-12)上運(yùn)行,因此想測(cè)試一下能否在YY3568的Android-11系統(tǒng)上運(yùn)行。
根據(jù)YY3568的wiki上的內(nèi)容,使用adb方式進(jìn)行apk安裝
發(fā)表于 11-14 11:44
號(hào)外,號(hào)外。。。深圳風(fēng)火輪科技又推出新品啦--youyeetoo X1。youyeetoo X1 是一款由深圳風(fēng)火輪科技推出的x86架構(gòu)單板電腦(SBC),可運(yùn)行全功能版的windows和Linux
發(fā)表于 10-12 18:01
?1875次閱讀
號(hào)外,號(hào)外。。。 深圳風(fēng)火輪科技又推出新品啦--youyeetoo X1。 youyeetoo X1 是一款由深圳風(fēng)火輪科技推出的x86架構(gòu)單板電腦(SBC),可運(yùn)行全功能版的windows
發(fā)表于 10-10 10:22
?833次閱讀
在上一篇,我們已經(jīng)成功使用FFMPEG通過(guò)命令行的方式進(jìn)行了RTMP的直播,其實(shí)FFMPEG還可以通過(guò)API的方式進(jìn)行編程,并且使用API的方式,那么擁有著更多的靈活性,這也是為什么要自編譯ffmpeg,而不是直接使用APT命令安裝的方式來(lái)運(yùn)行它。我們最終的目標(biāo)是使用RKMPP來(lái)將視頻進(jìn)行編碼,然后通過(guò)FFMPEG發(fā)送到RTMP服務(wù)器上進(jìn)行直播,那么方案有兩種,一種就是修改FFMPEG源碼進(jìn)行編譯,第二種方式就只能通過(guò)API編程來(lái)進(jìn)行了。
那么這一篇的任務(wù)有兩方面:
1.因?yàn)槲覀円褂胒fmpeg推RTMP流,那么我們先不繼承RKMPP,先從RTSP進(jìn)行拉流,然后使用軟解軟編后進(jìn)行推流,使用純C編寫(xiě)。
2.在Linux下面的軟件編譯,gcc hello.c -o hello一般只用來(lái)編譯hello world,但是在實(shí)際編譯過(guò)程中我們常常需要引入其他的庫(kù)來(lái)進(jìn)行編譯,所以需要增加一些參數(shù)。
廢話(huà)就說(shuō)到這里吧,上代碼:
#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);//設(shè)置超時(shí)斷開(kāi)連接時(shí)間
av_dict_set(&options, \"rtsp_transport\", \"tcp\", 0);//tcp方式打開(kāi)
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; //全局參數(shù)
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) {
// 這個(gè)非常重要,如果不設(shè)置延時(shí)非常的大 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo 是x264編碼速度的選項(xiàng)
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;
}
//附加標(biāo)志,這個(gè)一定要設(shè)置
p_video_output_stream->codecpar->codec_tag = 0;
//從編碼器復(fù)制參數(shù)
avcodec_parameters_from_context(p_video_output_stream->codecpar, p_video_output_codec_ctx);
///打開(kāi)rtmp 的網(wǎng)絡(luò)輸出IOAVIOContext:輸入輸出對(duì)應(yīng)的結(jié)構(gòu)體,用于輸入輸出(讀寫(xiě)文件,RTMP協(xié)議等)。
if (avio_open(&p_output_format_ctx->pb, output_link, AVIO_FLAG_WRITE) != 0) {
printf(\"error: avio_open failed!\\\\n\");
return -1;
}
//寫(xiě)入封裝頭
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;
}
然后,我們進(jìn)行編譯,編譯的命令放在下面,注意的是鏈接庫(kù)最好按照我的順序來(lái),不然可能會(huì)出問(wèn)題的。
可以看到,甚至連一個(gè)警告都沒(méi)有,完美!
接下來(lái)我們就開(kāi)始運(yùn)行吧:
運(yùn)行到這里可以看到我們讓輸出input信息和output信息還有編碼器的信息已經(jīng)成功輸出了,但是輸出后就好像卡死了,但是其實(shí)是它在正常工作,咱們沒(méi)有繼續(xù)讓他進(jìn)行輸出啊,所以如果希望改進(jìn)的話(huà),你可以讓他增加一些統(tǒng)計(jì)信息,但是可以50幀左右,也就是2秒輸出一下就可以了,一方面這樣看起來(lái)整潔容易看出問(wèn)題,另外一方面也可以減輕CPU的負(fù)擔(dān)。
使用top命令看一下CPU的使用率,不過(guò)才76%,如果滿(mǎn)載的話(huà)將會(huì)是400%,可以說(shuō)是即使是軟編,那么也處理640x480的視頻很輕松了。
實(shí)際測(cè)試效果圖如上,為了隱私我打上了馬賽克,不過(guò)可以看到時(shí)間的。
視頻信息如上,可以看到軟件已經(jīng)推流成功了,完美!
說(shuō)明
在這個(gè)程序中直接把鏈接寫(xiě)死在程序里了,其實(shí)相對(duì)來(lái)講,我更建議大家通過(guò)啟動(dòng)時(shí)通過(guò)命令行后面的參數(shù)進(jìn)行傳入,在程序里直接就可以通過(guò)argv就可以進(jìn)行讀取了,這個(gè)是支持的,我已經(jīng)寫(xiě)好在main里面了。
這個(gè)程序是可以讀取本地文件,也可以生成本地文件,還可以讀本地文件推流RTMP,也可以讀RTSP生成本地文件,例如要生成本地文件的話(huà),可以將代碼中的第20行修改為:const char output_link[] = \"cctv.flv\";.
本程序只做了視頻部分,因?yàn)橐曨l部分比較占用CPU資源,音頻部分占用小,而且視頻部分代碼比較簡(jiǎn)單好理解,后期增加音頻流也基本一致,但是需要注意的有可能frame和packet并不是一一對(duì)應(yīng)的,發(fā)送出去以后一定要用while來(lái)收回,還有就是注意時(shí)間軸的同步。
本程序使用的是純C來(lái)進(jìn)行編寫(xiě)的,主要是程序比較小,而且ffmpeg也是使用純C寫(xiě)的,可以看到文件也都寫(xiě)在了一起,如果做的功能比較多,還是建議使用C++使用面向?qū)ο髞?lái)完成,引入頭文件時(shí)記的添加extern \"C\",而且要用到多線(xiàn)程,包括視頻解碼線(xiàn)程,音頻解碼線(xiàn)程以及編碼線(xiàn)程等等。
要使用音視頻的緩存AVFifoBuffer。
增加QT界面,同時(shí)最好使用QT的項(xiàng)目管理,這樣不用每次都輸入那么多的參數(shù)了。如果不適用QT,不做界面,那么建議使用Makefile來(lái)管理項(xiàng)目。
發(fā)表于 09-25 15:33
評(píng)論