2.ffmpeg:H.264视频码流解析——视频编码

本文初衷是博主学习所用,学习雷神文章整理成自己笔记,希望雷神理解

参考雷神:视音频数据处理入门:H.264视频码流解析

前一篇文章是对视音频原始数据的处理,本文开始介绍视音频码流的处理——视频编码

1.原理

H.264原始码流(又称为“裸流”)是由一个一个的NALU组成的。他们的结构如下图所示。

其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU对应的Slice为一帧的开始就用0x00000001,否则就用0x000001。
H.264码流解析的步骤就是首先从码流中搜索0x000001和0x00000001,分离出NALU;然后再分析NALU的各个字段。本文的程序即实现了上述的两个步骤。

2.视频编码

视频编码的主要作用是将视频像素数据(RGB,YUV等)压缩成为视频码流,从而降低视频的数据量。如果视频不经过压缩编码的话,体积通常是非常大的,一部电影可能就要上百G的空间。视频编码是视音频技术中最重要的技术之一。视频码流的数据量占了视音频总数据量的绝大部分。高效率的视频编码在同等的码率下,可以获得更高的视频质量。

视频编码的简单原理可以参考:视频压缩编码和音频压缩编码的基本原理

注:视频编码技术在整个视音频技术中应该是最复杂的技术。如果没有基础的话,可以先买一些书看一下原理,比如说《现代电视原理》《数字电视广播原理与应用》(本科的课本)中的部分章节。

注:H.264仍然是主流编码标准

3.深入了解视频编码

参考雷神:视音频数据处理入门:H.264视频码流解析

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在C#中使用FFmpeg播放RTSP H264视频流并将其显示到窗体上,你可以使用FFmpeg.AutoGen库来实现。以下是一个简单的示例代码: 首先,你需要在项目中引用FFmpeg.AutoGen库。你可以通过NuGet包管理器来安装它。 然后,你可以使用以下代码来实现播放RTSP视频流并将其显示到窗体上: ```csharp using System; using System.Drawing; using System.Windows.Forms; using FFmpeg.AutoGen; namespace RTSPPlayer { public partial class MainForm : Form { private AVFormatContext* _formatContext; private AVCodecContext* _codecContext; private AVFrame* _frame; private AVPacket _packet; private SwsContext* _swsContext; private IntPtr _videoFrameBuffer; private Bitmap _bitmap; private bool _isPlaying; public MainForm() { InitializeComponent(); } private void MainForm_Load(object sender, EventArgs e) { // 初始化FFmpeg ffmpeg.av_register_all(); ffmpeg.avformat_network_init(); // 打开RTSP流 string rtspUrl = "your_rtsp_url"; AVDictionary* options = null; ffmpeg.av_dict_set(&options, "rtsp_transport", "tcp", 0); ffmpeg.av_dict_set(&options, "stimeout", "5000000", 0); int result = ffmpeg.avformat_open_input(&_formatContext, rtspUrl, null, &options); if (result != 0) { MessageBox.Show("无法打开RTSP流"); return; } // 查找视频流信息 result = ffmpeg.avformat_find_stream_info(_formatContext, null); if (result < 0) { MessageBox.Show("无法找到视频流信息"); return; } // 查找视频流索引 int videoStreamIndex = -1; for (int i = 0; i < _formatContext->nb_streams; i++) { if (_formatContext->streams[i]->codecpar->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex == -1) { MessageBox.Show("无法找到视频流"); return; } // 获取视频解码器 AVCodec* codec = ffmpeg.avcodec_find_decoder(_formatContext->streams[videoStreamIndex]->codecpar->codec_id); if (codec == null) { MessageBox.Show("无法找到视频解码器"); return; } // 创建解码器上下文 _codecContext = ffmpeg.avcodec_alloc_context3(codec); ffmpeg.avcodec_parameters_to_context(_codecContext, _formatContext->streams[videoStreamIndex]->codecpar); // 打开解码器 result = ffmpeg.avcodec_open2(_codecContext, codec, null); if (result < 0) { MessageBox.Show("无法打开视频解码器"); return; } // 创建帧对象 _frame = ffmpeg.av_frame_alloc(); // 创建包对象 ffmpeg.av_init_packet(&_packet); // 创建图像转换上下文 _swsContext = ffmpeg.sws_getContext(_codecContext->width, _codecContext->height, _codecContext->pix_fmt, _codecContext->width, _codecContext->height, AVPixelFormat.AV_PIX_FMT_BGR24, ffmpeg.SWS_BILINEAR, null, null, null); // 创建视频帧缓冲区 int bufferSize = ffmpeg.av_image_get_buffer_size(AVPixelFormat.AV_PIX_FMT_BGR24, _codecContext->width, _codecContext->height, 1); _videoFrameBuffer = ffmpeg.av_malloc((ulong)bufferSize); ffmpeg.av_image_fill_arrays(_frame->data, _frame->linesize, (byte*)_videoFrameBuffer, AVPixelFormat.AV_PIX_FMT_BGR24, _codecContext->width, _codecContext->height, 1); // 创建位图对象 _bitmap = new Bitmap(_codecContext->width, _codecContext->height, _codecContext->width * 3, PixelFormat.Format24bppRgb, _videoFrameBuffer); // 开始播放 _isPlaying = true; Play(); } private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { // 停止播放 _isPlaying = false; ffmpeg.av_packet_unref(&_packet); ffmpeg.av_frame_free(&_frame); ffmpeg.avcodec_close(_codecContext); ffmpeg.avformat_close_input(&_formatContext); ffmpeg.avformat_network_deinit(); ffmpeg.av_free(_videoFrameBuffer); ffmpeg.sws_freeContext(_swsContext); } private void Play() { while (_isPlaying) { // 读取一帧数据 int result = ffmpeg.av_read_frame(_formatContext, &_packet); if (result < 0) { // 读取失败,结束播放 _isPlaying = false; break; } if (_packet.stream_index == _formatContext->streams[0]->index) { // 解码视频帧 result = ffmpeg.avcodec_send_packet(_codecContext, &_packet); if (result < 0) { // 解码失败,跳过该帧 ffmpeg.av_packet_unref(&_packet); continue; } result = ffmpeg.avcodec_receive_frame(_codecContext, _frame); if (result < 0) { // 解码失败,跳过该帧 ffmpeg.av_packet_unref(&_packet); continue; } // 转换图像格式 ffmpeg.sws_scale(_swsContext, _frame->data, _frame->linesize, 0, _codecContext->height, _frame->data, _frame->linesize); // 刷新窗体 pictureBox.Image = _bitmap; pictureBox.Refresh(); } // 释放数据ffmpeg.av_packet_unref(&_packet); } } } } ``` 请注意,上述代码仅为示例,你需要根据实际情况进行适当的修改和调整。 相关问题: 1. 如何在C#中使用FFmpeg播放RTSP视频流? 2. 如何在C#中显示视频帧到窗体上? 3. 如何解码H264视频流? 4. 如何使用FFmpeg.AutoGen库在C#中进行视频解码?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值