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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#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;
}
原理解释
保存裸流的核心在于直接处理音视频编码后的数据,不进行封装。一般流程如下:
- 初始化编码器和解码器。
- 打开输入源(如摄像头、麦克风等)。
- 读取输入帧,进行编码。
- 将编码后的数据直接写入输出文件或网络流中。
算法原理流程图
算法原理解释
- 初始化编码器和解码器:设置编码参数,如码率、分辨率、采样率等。
- 打开输入源:获取输入设备句柄或者文件路径。
- 读取输入帧:从输入源读取一帧原始数据。
- 编码数据:使用FFmpeg的编码器将原始数据转换为压缩格式。
- 写入输出:将编码后的数据直接写入文件或网络流。
实际应用代码示例实现
示例代码
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
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保存裸流是一种高效的处理原始音视频数据的方法,适用于各种对延迟敏感的应用场景。尽管裸流缺乏封装信息,但其高效和简单的特点使其在特定领域内得到了广泛应用。
未来展望
随着多媒体技术的发展,更多的硬件加速技术会被引入,进一步提高裸流处理的效率。同时,未来可能会有新的标准和协议出现,以更好地支持裸流在不同场景中的应用。