C++ 音视频流媒体浅谈

C++流媒体开发

今天就浅浅聊一下C++流媒体开发 

流媒体开发中最常见的是FFmpeg(编解码器)  业务逻辑主要是播放器了(如腾旭视频 爱奇艺等等)

FFmpeg是一个开源的音视频处理工具集,可以用于处理、转换和流媒体传输音视频文件。它包含了一系列的库和命令行工具,提供了强大的音视频编解码、格式转换、过滤器应用等功能。

以下是一些主要特点和功能:

  1. 格式支持广泛:FFmpeg支持几乎所有常见的音视频格式,包括但不限于MP4、AVI、MKV、MOV等。它能够对这些格式进行解码、编码和转换操作。

  2. 音视频编解码能力:FFmpeg支持多种音频编解码器(如AAC、MP3、FLAC)和视频编解码器(如H.264、H.265),可以实现音频和视频文件的压缩和解压缩操作。

  3. 视频流处理:FFmpeg可以处理各种视频流,包括网络摄像头实时流、屏幕捕捉流等。它能够进行录制、截取、转发等操作。

  4. 音频流处理:FFmpeg可以对音频流进行录制、混合、剪辑等操作。你可以从麦克风或其他输入设备获取音频,并将其发送到输出设备或保存为文件。

  5. 图像处理:除了音视频处理外,FFmpeg还提供了图像处理功能。你可以使用FFmpeg来调整图像大小,应用滤镜效果,进行图像转换等操作。

  6. 过滤器应用:FFmpeg内置了丰富的音视频过滤器,允许你对音视频进行处理和修改。你可以添加水印、调整亮度、对比度、色彩等参数,还可以实现视频剪裁、旋转和分割等操作。

FFmpeg是一个功能强大而灵活的工具,广泛应用于多媒体处理领域。它提供了简单易用的命令行界面和API接口,支持跨平台运行(Windows、Linux、macOS等),被众多开发者和专业用户所使用。

今天就聊一下音视频文件编码器(ffmpg)的转换吧(音视频的录制原理)

环境主要是Qt(Qt天然的支持跨平台)

编码主要流程

C++ 音视频流媒体浅谈_第1张图片

划分模块 

1.1 PCM 采集(麦克风+系统声音)+音频编码模块

1.2 Yun采集(摄像头+屏幕)+音视频编码模块 

   

解码主要流程:

C++ 音视频流媒体浅谈_第2张图片

将媒体数据解码 上面的过程逆行就可以了 这样就是一个完整播放器的模块

图中概念介绍:

1.时间戳  :时间戳通常指的是表示特定时间的数字或字符串

2.PCM:PCM(Pulse Code Modulation)是一种常用的数字音频编码方式,它将模拟声音信号转换为数字形式进行存储和传输。在PCM编码中,声音信号会被离散化成一系列采样点,并用固定的比特数来表示每个采样点的幅值。

3.Frame:在流媒体中,Frame(帧)是指一组连续的视频或音频数据。对于视频流来说,每个帧包含了一张完整的图像;对于音频流来说,每个帧包含了一段时间内的声音信号。

4.Packet:在计算机网络中,Packet(数据包)是将数据划分成小块进行传输的基本单位。它是网络通信中的信息载体,通过网络传输从源节点到目标节点。

5.Stream:音视频流

6.视频缓存:视频缓存是指在播放视频时,预先将部分视频数据存储在本地设备或服务器上,以提供更流畅的观看体验。

视频缓存 图像缓存类推

7.拉流;拉流是指从视频源服务器主动获取视频数据进行播放或处理的过程。在视频传输中,通常将视频源服务器称为推流端,而接收视频数据的设备称为拉流端

8.推流:推流是指将视频数据从源设备发送到视频服务器或云平台,以便其他用户可以通过网络观看或处理该视频流的过程。

9.YUV是一种常见的图像格式,它代表了图像的亮度(Y)和色度(U、V)信息。在视频处理和编码中经常使用YUV格式。

10.同步控制 :意思就是线程的同步 非异步编程

下面就介绍几个关于这些概念的代码实例

C++获取时间戳代码实例:

#include 
#include 

int main() {
    // 获取当前时间的时间戳(以秒为单位)
    auto now = std::chrono::system_clock::now();
    auto timestamp = std::chrono::duration_cast(now.time_since_epoch()).count();

    // 输出时间戳
    std::cout << "Current timestamp: " << timestamp << " seconds" << std::endl;

    return 0;
}

C++PCM应用

#include 
#include 

int main() {
    // 打开 PCM 文件
    std::ifstream pcmFile("audio.pcm", std::ios::binary);
    if (!pcmFile) {
        std::cerr << "Failed to open PCM file." << std::endl;
        return 1;
    }

    // 读取 PCM 数据并进行处理
    const int bufferSize = 1024; // 缓冲区大小
    char buffer[bufferSize]; // 缓冲区

    while (!pcmFile.eof()) {
        pcmFile.read(buffer, bufferSize);
        // 在这里可以对 buffer 中的 PCM 数据进行处理,比如解码、编码、特征提取等
        // 这里只是简单地输出每个采样点的值(假设采样格式为16位有符号整数)
        for (int i = 0; i < pcmFile.gcount(); i += 2) {
            short sample = (buffer[i + 1] << 8) | buffer[i];
            std::cout << "Sample: " << sample << std::endl;
        }
    }

    // 关闭 PCM 文件
    pcmFile.close();

    return 0;
}

C++拉流代码应用

#include 

int main() {
    // 视频流URL
    std::string stream_url = "your_stream_url_here";

    // 创建视频捕获对象
    cv::VideoCapture cap(stream_url);

    // 检查视频捕获对象是否成功打开视频流
    if (!cap.isOpened()) {
        std::cout << "无法打开视频流" << std::endl;
        return -1;
    }

    cv::Mat frame;

    while (true) {
        // 读取帧
        cap >> frame;

        // 检查帧是否成功读取
        if (frame.empty()) {
            std::cout << "无法读取帧" << std::endl;
            break;
        }

        // 显示帧图像
        cv::imshow("Video Stream", frame);

        // 按下 'q' 键退出循环
        if (cv::waitKey(1) == 'q') {
            break;
        }
    }

    // 释放资源和关闭窗口
    cap.release();
    cv::destroyAllWindows();

    return 0;
}

C++推流代码应用

#include 
#include 
#include 
#include 

extern "C" {
  #include 
  #include 
}

int main(int argc, char* argv[]) {
  // 输入参数
  const char* input_file = "input.mp4";
  const char* output_url = "rtmp://your-streaming-server.com/live/stream_key";

  // 初始化FFmpeg
  av_register_all();

  // 打开输入文件
  AVFormatContext* input_ctx = NULL;
  if (avformat_open_input(&input_ctx, input_file, NULL, NULL) != 0) {
    fprintf(stderr, "无法打开输入文件\n");
    return -1;
  }

  // 查找输入文件流信息
  if (avformat_find_stream_info(input_ctx, NULL) < 0) {
    fprintf(stderr, "无法获取流信息\n");
    avformat_close_input(&input_ctx);
    return -1;
  }

  // 创建输出上下文并设置输出格式
  AVFormatContext* output_ctx = NULL;
  
   if (avformat_alloc_output_context2(&output_ctx, NULL, "flv", output_url) == -1) {   
     fprintf(stderr, "无法创建输出上下文\n");
     avformat_close_input(&input_ctx);
     return -1;   
   }
   

 
   // 遍历输入文件中的所有流,并在输出上下文中添加相应的流
   for (unsigned int i = 0; i < input_ctx->nb_streams; ++i) {
      AVStream* in_stream = input_ctx->streams[i];
      AVCodec* codec = avcodec_find_decoder(in_stream->codecpar->codec_id);
      AVStream* out_stream = avformat_new_stream(output_ctx, codec);
      
      if (!out_stream) {
          fprintf(stderr, "无法创建输出流\n");
          avformat_close_input(&input_ctx);
          avformat_free_context(output_ctx);
          return -1;
       }
  
      // 复制流参数
      if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0) {
          fprintf(stderr, "无法复制流参数\n");
          avformat_close_input(&input_ctx);
          avformat_free_context(output_ctx);
         return -1;
     }
   }

   // 打开输出URL
    if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {   
        if (avio_open(&output_ctx->pb, output_url, AVIO_FLAG_WRITE) < 0) {    
           fprintf(stderr, "无法打开输出URL\n");
           avformat_close_input(&input_ctx);   
           avformat_free_context(output_ctx); 
           return -1;   
       } 
   }

   // 写入文件头部信息
   if (avformat_write_header(output_ctx, NULL) < 0) {
       fprintf(stderr, "无法写入文件头部信息\n");
       avio_close(output_ctx->pb); 
       avformat_close_input(&input_ctx);   
       avformat_free_context(output_ctx); 
       return -1;  
   }

   // 推送数据
   AVPacket packet;
  
   while (av_read_frame(input_ctx, &packet) >= 0) {
     AVStream* in_stream = input_ctx->streams[packet.stream_index];
     AVStream* out_stream = output_ctx->streams[packet.stream_index];

     // 调整帧的时间基
     av_packet_rescale_ts(&packet, in_stream->time_base, out_stream->time_base);
     packet.pos = -1;
     
     // 写入输出流
     if (av_interleaved_write_frame(output_ctx, &packet) < 0) {
         fprintf(stderr, "无法写入输出流\n");
         break;
      }

      av_packet_unref(&packet);
   }

   // 写入文件尾部信息
   av_write_trailer(output_ctx);

   // 关闭输入和输出上下文
    avio_close(output_ctx->pb);  
    avformat_close_input(&input_ctx);   
    avformat_free_context(output_ctx); 

    return 0;
}

好了 到这里对音视频的介绍就告一段落了 快过年了 祝大家新的一年里风调雨顺 事业有成

在这里小编有一个课程想介绍给大家:https://xxetb.xetslk.com/s/2PjJ3T

你可能感兴趣的:(c++,音视频)