音视频系列2:ffmpeg将H.264解码为RGB

  • 前言
  • 源码

前言

喜大普奔,终于更新啦,上期说到,如何使用ffmpeg+rtmp进行拉流,不熟悉的小伙伴们,可以先看上一期。今天我们要实现的是使用ffmpeg+rtmp拉流,拉完的FLV流,提取出H.264视频,再提取出YUV,再提取出rgb图,最后用opencv处理图像。我的任务是得到rgb图像,从而可以对图像进行处理。

整体流程:rtmp拉流–>FLV–>H.264–>YUV–>RGB–>OPENCV处理

框架示意图:我们要实现下图绿色框部分:

源码

首先说明一下,filter在这里的作用就是从原复用格式中提取出相关的格式,比如FLV包含了H.264与AAC,filter就可以提取出其中的H.264,这里的filter仍使用旧版的api,编译会出现warning,但目前仍可以使用,等有空的时候我再更新代码吧。同样的,这次也是拉芒果台的视频。

main.cpp

#include <iostream>
extern "C"
{#include "libavformat/avformat.h"
#include <libavutil/mathematics.h>
#include <libavutil/time.h>
#include <libavutil/samplefmt.h>
#include <libavcodec/avcodec.h>
};
#include "opencv2/core.hpp"
#include<opencv2/opencv.hpp>void AVFrame2Img(AVFrame *pFrame, cv::Mat& img);
void Yuv420p2Rgb32(const uchar *yuvBuffer_in, const uchar *rgbBuffer_out, int width, int height);
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{AVFormatContext *ifmt_ctx = NULL;AVPacket pkt;AVFrame *pframe = NULL;int ret, i;int videoindex=-1;AVCodecContext  *pCodecCtx;AVCodec         *pCodec;const char *in_filename  = "rtmp://58.200.131.2:1935/livetv/hunantv";   //芒果台rtmp地址const char *out_filename_v = "test.h264"; //Output file URL//Registerav_register_all();//Networkavformat_network_init();//Inputif ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0){printf( "Could not open input file.");return -1;}if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0){printf( "Failed to retrieve input stream information");return -1;}videoindex=-1;for(i=0; i<ifmt_ctx->nb_streams; i++){if(ifmt_ctx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoindex=i;}}//Find H.264 DecoderpCodec = avcodec_find_decoder(AV_CODEC_ID_H264);if(pCodec==NULL){printf("Couldn't find Codec.\n");return -1;}pCodecCtx = avcodec_alloc_context3(pCodec);if (!pCodecCtx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){printf("Couldn't open codec.\n");return -1;}pframe=av_frame_alloc();if(!pframe){printf("Could not allocate video frame\n");exit(1);}FILE *fp_video=fopen(out_filename_v,"wb+"); //用于保存H.264cv::Mat image_test;AVBitStreamFilterContext* h264bsfc =  av_bitstream_filter_init("h264_mp4toannexb");while(av_read_frame(ifmt_ctx, &pkt)>=0){if (pkt.stream_index == videoindex) {av_bitstream_filter_filter(h264bsfc, ifmt_ctx->streams[videoindex]->codec, NULL, &pkt.data, &pkt.size,pkt.data, pkt.size, 0);printf("Write Video Packet. size:%d\tpts:%lld\n", pkt.size, pkt.pts);//保存为h.264 该函数用于测试//fwrite(pkt.data, 1, pkt.size, fp_video);// Decode AVPacketif(pkt.size){ret = avcodec_send_packet(pCodecCtx, &pkt);if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {std::cout << "avcodec_send_packet: " << ret << std::endl;continue;}//Get AVframeret = avcodec_receive_frame(pCodecCtx, pframe);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {std::cout << "avcodec_receive_frame: " << ret << std::endl;continue;}//AVframe to rgbAVFrame2Img(pframe,image_test);}}//Free AvPacketav_packet_unref(&pkt);}//Close filterav_bitstream_filter_close(h264bsfc);fclose(fp_video);avformat_close_input(&ifmt_ctx);if (ret < 0 && ret != AVERROR_EOF){printf( "Error occurred.\n");return -1;}return 0;
}
void Yuv420p2Rgb32(const uchar *yuvBuffer_in, const uchar *rgbBuffer_out, int width, int height)
{uchar *yuvBuffer = (uchar *)yuvBuffer_in;uchar *rgb32Buffer = (uchar *)rgbBuffer_out;int channels = 3;for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){int index = y * width + x;int indexY = y * width + x;int indexU = width * height + y / 2 * width / 2 + x / 2;int indexV = width * height + width * height / 4 + y / 2 * width / 2 + x / 2;uchar Y = yuvBuffer[indexY];uchar U = yuvBuffer[indexU];uchar V = yuvBuffer[indexV];int R = Y + 1.402 * (V - 128);int G = Y - 0.34413 * (U - 128) - 0.71414*(V - 128);int B = Y + 1.772*(U - 128);R = (R < 0) ? 0 : R;G = (G < 0) ? 0 : G;B = (B < 0) ? 0 : B;R = (R > 255) ? 255 : R;G = (G > 255) ? 255 : G;B = (B > 255) ? 255 : B;rgb32Buffer[(y*width + x)*channels + 2] = uchar(R);rgb32Buffer[(y*width + x)*channels + 1] = uchar(G);rgb32Buffer[(y*width + x)*channels + 0] = uchar(B);}}
}void AVFrame2Img(AVFrame *pFrame, cv::Mat& img)
{int frameHeight = pFrame->height;int frameWidth = pFrame->width;int channels = 3;//输出图像分配内存img = cv::Mat::zeros(frameHeight, frameWidth, CV_8UC3);Mat output = cv::Mat::zeros(frameHeight, frameWidth,CV_8U);//创建保存yuv数据的bufferuchar* pDecodedBuffer = (uchar*)malloc(frameHeight*frameWidth * sizeof(uchar)*channels);//从AVFrame中获取yuv420p数据,并保存到bufferint i, j, k;//拷贝y分量for (i = 0; i < frameHeight; i++){memcpy(pDecodedBuffer + frameWidth*i,pFrame->data[0] + pFrame->linesize[0] * i,frameWidth);}//拷贝u分量for (j = 0; j < frameHeight / 2; j++){memcpy(pDecodedBuffer + frameWidth*i + frameWidth / 2 * j,pFrame->data[1] + pFrame->linesize[1] * j,frameWidth / 2);}//拷贝v分量for (k = 0; k < frameHeight / 2; k++){memcpy(pDecodedBuffer + frameWidth*i + frameWidth / 2 * j + frameWidth / 2 * k,pFrame->data[2] + pFrame->linesize[2] * k,frameWidth / 2);}//将buffer中的yuv420p数据转换为RGB;Yuv420p2Rgb32(pDecodedBuffer, img.data, frameWidth, frameHeight);//简单处理,这里用了canny来进行二值化cvtColor(img, output, CV_RGB2GRAY);waitKey(2);Canny(img, output, 50, 50*2);waitKey(2);imshow("test",output);waitKey(10);// 测试函数// imwrite("test.jpg",img);//释放bufferfree(pDecodedBuffer);img.release();output.release();
}

CmakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(version1_0)set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "-D__STDC_CONSTANT_MACROS")find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )find_path(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h)
find_library(AVCODEC_LIBRARY avcodec)find_path(AVFORMAT_INCLUDE_DIR libavformat/avformat.h)
find_library(AVFORMAT_LIBRARY avformat)find_path(AVUTIL_INCLUDE_DIR libavutil/avutil.h)
find_library(AVUTIL_LIBRARY avutil)find_path(AVDEVICE_INCLUDE_DIR libavdevice/avdevice.h)
find_library(AVDEVICE_LIBRARY avdevice)add_executable(version1_0 main.cpp)
target_include_directories(version1_0 PRIVATE ${AVCODEC_INCLUDE_DIR} ${AVFORMAT_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIR} ${AVDEVICE_INCLUDE_DIR})
target_link_libraries(version1_0 PRIVATE ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY} ${AVUTIL_LIBRARY} ${AVDEVICE_LIBRARY} ${OpenCV_LIBS})

效果图:这里使用了canny对图像进行二值化,目的是为了测试opencv与代码的稳定性。

实测半小时以上程序不会崩溃,对于内存的处理应该没太大问题。

问题
但是对于网络情况,却有些不乐观,不知道是因为我家网络的缘故还是芒果台服务器的缘故,我拉的流总是会卡,不流畅,下午五六点测试卡的要死,晚上还好,但有时候也会卡。不过测试了一下,使用VLC播放器播放芒果台,也是卡卡的,所以估计是它服务器的问题

所以针对以上问题,我打算自己搭建nginx服务器,在本地推流,然后本地拉流,拉流推流同时运行,预知后事如何,咱们下一节见,会有那么一天,咱们能搭建出多用户无线图传SLAM系统。


2020年4月4号更新:
新增版本可以多线程拉流,且修改了部分bug,详见:
音视频系列6:ffmpeg多线程拉流

如果我的文章对你有帮助,欢迎点赞,评论,关注!

参考文献:
ffmpeg的example:decode_video.cpp
https://www.cnblogs.com/riddick/p/7719298.html
https://blog.csdn.net/hiwubihe/article/details/82346759

音视频系列2:ffmpeg将H.264解码为RGB相关推荐

  1. 音视频系列八 ffmpeg使用mediacodec硬解码

    文章目录 重新编译ffmpeg 配置JavaVM 替换AVCodec 重新编译ffmpeg ffmpeg默认是没有开启mediacodec的,所以需要在配置文件上增加配置 --enable-media ...

  2. 音视频开发(24)---H.264视频编码基本知识

    H.264视频编码基本知识 一.视频编码技术的发展历程 视频编码技术基本是由iso/iec制定的mpeg-x和itu-t制定的h.26x两大系列视频编码国际标准的推出.从h.261视频编码建议,到 h ...

  3. 音视频开发(42)---H.264 SVC 简介

    H.264 SVC 简介 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/CrystalShaw/article/details/81184531 一 ...

  4. 音视频开发 RTMP协议发送H.264编码及AAC编码的音视频(C++实现)

    RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia 公司创建,后来归Adobe公司所有,是一种私有协议,主要用来联系F ...

  5. 音视频开发(32)---H.264/AVC 主流视频编码标准的发展

    H.264/AVC 主流视频编码标准的发展 一. 主流视频编码标准的发展 长期以来,视频编码标准主要由两个国际组织负责制定:国际电信联盟ITU-T和国际标准化组织ISO.目前影响力最强的视频编码标准基 ...

  6. 音视频开发(31)---H.264格式分析

    H.264格式分析 一.H.264基本流结构 H.264 的基本流(elementary stream,ES)的结构分为两层,包括视频编码层(VCL)和网络适配层(NAL).视频编码层负责高效的视频内 ...

  7. 音视频开发(26)---H.264编码格式和视频编码的一些基本概念

    H.264编码格式和视频编码的一些基本概念 对于大多数人来说,对于视频的基本印象就是视频清晰度.体积大小.分辨率和视频格式.当然对于只对观看视频和下载视频的朋友们来说这些知识足够了.经常在VeryCD ...

  8. 【音视频】Ubuntu安装开源H.264标准编解码库x264

    1 x264    x264是基于H.264/AVC标准的一款免费.开源的视频编解码器(库),x264是目前使用最广的.最优秀的一款H.264编解码器.x264编解码器支持的功能众多,包括: 8x8与 ...

  9. 音视频系列:FFmpeg和NDK在Centos7上交叉编译

    更新: 2022.01.15:因应用商店需要上传64位APK,发现之前的脚本编译的arm64-v8a库,在使用时报错: java.lang.UnsatisfiedLinkError: dlopen f ...

最新文章

  1. Typora添加右键新建Markdown文件
  2. android 后台服务拍照,Android实现后台开启服务默默拍照功能
  3. Windows 7里的计算器,中文版,给Vista和2008用吧
  4. 看一下CDI 2.0 EDR1
  5. 写出高质量的代码——“零星”总结(延续3)
  6. 校园推广方案:常用手段及百试不爽的方法
  7. 借贷记账思考2015.12.28
  8. 阿里云服务器加快下载github
  9. gis怎么提取水系_arcgis水系提取流程(dem)
  10. 通信基础 8 —— MIMO / 3GPP / UMI
  11. SkeyeLive开源流媒体同屏直播软件源码功能框架解析
  12. 华为2019实习生专业面试经历——通信算法工程师
  13. 图解密码学密钥的分配方式
  14. Linux应用程序目录规范——XDG
  15. 使用sikuli测试web网页实例
  16. android(微博 微信 qq) 分享和第三分认证登录的封装
  17. libdbus 实例以及使用d-feet查看接口方法
  18. 物联网示范项目优秀案例集
  19. 对字符串按“红黄蓝”进行排序,如“蓝黄红红黄”,输出结果为“红红黄黄蓝”
  20. 手机界面设计—常识篇

热门文章

  1. 未来科技蒲公英大飞_大烟草的下跌告诉我们关于大科技的未来
  2. 推荐一本书: Rework 附中英文pdf下载
  3. KafKa 开启 SASL 验证
  4. mysql5.7 性能优化配置 innodb_buffer_pool_size
  5. 上周热点回顾(10.18-10.24)
  6. 如何使用格式工厂将vtt文件格式字幕加在视频文件中
  7. 千岛湖2日团建旅行!游览天下第一秀水,感受湖岛遍布的磅礴气势!_团建拓展_嗨牛团建_杭州站...
  8. php exif_read_data orientation,PHP exif_read_data Illegal IFD size
  9. Win11保留的存储空间怎么关闭?Win11释放系统保留存储空间教程
  10. Datatables表格插件学习