Qt/C++ 音视频开发 - FFmpeg保存裸流

Qt/C++ 音视频开发 - FFmpeg保存裸流

介绍

FFmpeg 是一个开源的多媒体框架,能够处理音频、视频及其他多媒体文件和流。裸流指的是未经封装处理的原始音视频数据。使用FFmpeg保存裸流,可以通过高效的方式处理并存储音视频数据。

应用使用场景

  • 实时流媒体传输:在没有时间进行复杂封装的情况下,将音视频数据直接传输。
  • 低延迟应用:减少因为封装产生的冗余数据和延迟。
  • 数据分析:对原始音视频数据进行深入分析时,不需要解封装过程。
  • 嵌入式设备:资源有限的设备上,避免额外的封装步骤以节省内存和CPU资源。

以下是一些示例代码,用于实现实时流媒体传输、低延迟应用、数据分析和嵌入式设备上的音视频数据直接传输。我们分别使用了Python和C语言示范这一过程。

实时流媒体传输与低延迟应用 (Python)

使用 socket 库进行简单的实时音视频传输。

服务器端:
import socket

def stream_server(host='0.0.0.0', port=12345):
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind((host, port))
    server_socket.listen(1)
    connection, address = server_socket.accept()
    
    print(f"Connection from: {address}")
    
    while True:
        data = connection.recv(4096)
        if not data:
            break
        # Process the received raw audio/video data
        print(f"Received data: {data[:10]}...")  # Display part of the data for demonstration

    connection.close()

if __name__ == "__main__":
    stream_server()
客户端:
import socket

def stream_client(host='localhost', port=12345):
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect((host, port))

    with open('sample_video.raw', 'rb') as file:
        chunk = file.read(4096)
        while chunk:
            client_socket.send(chunk)
            chunk = file.read(4096)

    client_socket.close()

if __name__ == "__main__":
    stream_client()

数据分析 - Python分析原始数据

import numpy as np

def analyze_raw_audio_data(file_path):
    with open(file_path, 'rb') as file:
        data = np.frombuffer(file.read(), dtype=np.int16)
    
    # Perform analysis like computing average amplitude
    avg_amplitude = np.mean(np.abs(data))
    print(f"Average Amplitude: {avg_amplitude}")

if __name__ == "__main__":
    analyze_raw_audio_data('sample_audio.raw')

嵌入式设备 - C语言直接传输数据

在嵌入式设备上,避免封装步骤以节省内存和CPU资源。

服务器端:
#include 
#include 
#include 
#include 
#include 

#define PORT 12345
#define BUFFER_SIZE 4096

void stream_server() {
    int server_fd, new_socket;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    char buffer[BUFFER_SIZE] = {0};

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }
    if (listen(server_fd, 3) < 0) {
        perror("listen");
        close(server_fd);
        exit(EXIT_FAILURE);
    }
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, 
                       (socklen_t*)&addrlen))<0) {
        perror("accept");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    ssize_t valread;
    while ((valread = read(new_socket, buffer, BUFFER_SIZE)) > 0) {
        // Process the received raw audio/video data
        printf("Received %zd bytes\n", valread);
    }

    close(new_socket);
    close(server_fd);
}

int main() {
    stream_server();
    return 0;
}
客户端:
#include 
#include 
#include 
#include 
#include 

#define PORT 12345
#define BUFFER_SIZE 4096

void stream_client(const char *filename) {
    struct sockaddr_in address;
    int sock = 0;
    struct sockaddr_in serv_addr;
    char buffer[BUFFER_SIZE] = {0};
    FILE *file;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Socket creation error \n");
        return;
    }

    memset(&serv_addr, '0', sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) {
        printf("\nInvalid address/ Address not supported \n");
        return;
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return;
    }

    file = fopen(filename, "rb");
    if (!file) {
        perror("File opening failed");
        close(sock);
        return;
    }

    size_t nread;
    while ((nread = fread(buffer, 1, BUFFER_SIZE, file)) > 0) {
        send(sock, buffer, nread, 0);
    }

    fclose(file);
    close(sock);
}

int main() {
    stream_client("sample_video.raw");
    return 0;
}

原理解释

保存裸流的核心在于直接处理音视频编码后的数据,不进行封装。一般流程如下:

  1. 初始化编码器和解码器。
  2. 打开输入源(如摄像头、麦克风等)。
  3. 读取输入帧,进行编码。
  4. 将编码后的数据直接写入输出文件或网络流中。

算法原理流程图

初始化编码器和解码器
打开输入源
读取输入帧
编码数据
写入输出

算法原理解释

  1. 初始化编码器和解码器:设置编码参数,如码率、分辨率、采样率等。
  2. 打开输入源:获取输入设备句柄或者文件路径。
  3. 读取输入帧:从输入源读取一帧原始数据。
  4. 编码数据:使用FFmpeg的编码器将原始数据转换为压缩格式。
  5. 写入输出:将编码后的数据直接写入文件或网络流。

实际应用代码示例实现

示例代码

extern "C" {
#include 
#include 
#include 
}

int main(int argc, char *argv[]) {
    av_register_all();
    AVFormatContext *pFormatCtx = nullptr;

    avformat_open_input(&pFormatCtx, argv[1], nullptr, nullptr);
    avformat_find_stream_info(pFormatCtx, nullptr);

    int videoStreamIndex = -1;
    for (unsigned i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }

    AVCodecContext *pCodecCtx = avcodec_alloc_context3(nullptr);
    avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStreamIndex]->codecpar);
    AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    avcodec_open2(pCodecCtx, pCodec, nullptr);

    AVPacket packet;
    FILE *outFile = fopen("output.raw", "wb");

    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        if (packet.stream_index == videoStreamIndex) {
            fwrite(packet.data, 1, packet.size, outFile);
        }
        av_packet_unref(&packet);
    }

    fclose(outFile);
    avcodec_free_context(&pCodecCtx);
    avformat_close_input(&pFormatCtx);

    return 0;
}

测试代码

编译和运行上述代码:

g++ -o save_raw_stream save_raw_stream.cpp -lavformat -lavcodec -lavutil -lswscale
./save_raw_stream input.mp4

部署场景

该方案适用于各类需要处理原始音视频数据的应用,包括但不限于:

  • 嵌入式设备视频监控
  • 视频会议系统
  • 实时流媒体服务器

材料链接

  • FFmpeg 官网
  • FFmpeg 文档

总结

使用FFmpeg保存裸流是一种高效的处理原始音视频数据的方法,适用于各种对延迟敏感的应用场景。尽管裸流缺乏封装信息,但其高效和简单的特点使其在特定领域内得到了广泛应用。

未来展望

随着多媒体技术的发展,更多的硬件加速技术会被引入,进一步提高裸流处理的效率。同时,未来可能会有新的标准和协议出现,以更好地支持裸流在不同场景中的应用。

你可能感兴趣的:(人工智能时代,qt,c++,音视频)