服务器

一、C语言编写TCP视频服务器

  1. TCP视频服务器将视频文件逐块发送给客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 1234
#define BUFFER_SIZE 4096

void send_file(int client_socket, const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("Could not open file");
return;
}

char buffer[BUFFER_SIZE];
size_t bytes_read;

// 逐块读取文件并发送到客户端
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
if (send(client_socket, buffer, bytes_read, 0) == -1) {
perror("Send failed");
break;
}
}

fclose(file);
}

int main() {
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);

// 创建 TCP 套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}

// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的接口
server_addr.sin_port = htons(PORT); // 设置端口

// 绑定套接字
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
close(server_socket);
exit(EXIT_FAILURE);
}

// 开始监听
if (listen(server_socket, 5) == -1) {
perror("Listen failed");
close(server_socket);
exit(EXIT_FAILURE);
}

printf("Server listening on port %d...\n", PORT);

// 接受客户端连接
while (1) {
client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_socket == -1) {
perror("Accept failed");
continue;
}

printf("Client connected.\n");

// 发送视频文件
send_file(client_socket, "input.mp4"); // 替换为您的视频文件名

// 关闭客户端套接字
close(client_socket);
printf("Client disconnected.\n");
}

// 关闭服务器套接字
close(server_socket);
return 0;
}

2。客户端逐块接收视频并播放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/frame.h> // av_frame_get_best_effort_timestamp is declared here
#include <libavutil/time.h>
#include <libswscale/swscale.h>
#include <SDL2/SDL.h>

#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "swscale.lib")


#undef main
int main(int argc, char* argv[]) {
AVFormatContext* pFormatCtx = NULL;
int videoStream;
AVCodecContext* pCodecCtx = NULL;
AVCodec* pCodec = NULL;
AVFrame* pFrame = NULL;
AVFrame* pFrameRGB = NULL;
AVPacket packet;
struct SwsContext* sws_ctx = NULL;
uint8_t* buffer = NULL;
int numBytes;
double pts, actual_delay;
int64_t start_time, frame_time;

SDL_Window* screen = NULL;
SDL_Renderer* renderer = NULL;
SDL_Texture* texture = NULL;

// Initialize FFmpeg
avformat_network_init();

// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
exit(1);
}

// Open video stream
const char* inputURL = "tcp://127.0.0.1:1234";
if (avformat_open_input(&pFormatCtx, inputURL, NULL, NULL) != 0) {
fprintf(stderr, "Could not open stream.\n");
exit(1);
}

// Get stream info
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
fprintf(stderr, "Couldn't find stream information.\n");
exit(1);
}

// Find the first video stream
videoStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
}
if (videoStream == -1) {
fprintf(stderr, "Didn't find a video stream.\n");
exit(1);
}

// Get codec parameters
AVCodecParameters* pCodecPar = pFormatCtx->streams[videoStream]->codecpar;
// Find the decoder for the video stream
pCodec = avcodec_find_decoder(pCodecPar->codec_id);
if (pCodec == NULL) {
fprintf(stderr, "Codec not found.\n");
exit(1);
}

// Allocate codec context
pCodecCtx = avcodec_alloc_context3(pCodec);
if (!pCodecCtx) {
fprintf(stderr, "Could not allocate codec context.\n");
exit(1);
}

// Copy codec parameters to context
if (avcodec_parameters_to_context(pCodecCtx, pCodecPar) < 0) {
fprintf(stderr, "Could not copy codec context.\n");
exit(1);
}

// Open codec
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
fprintf(stderr, "Could not open codec.\n");
exit(1);
}

// Allocate video frames
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();
if (!pFrameRGB) {
fprintf(stderr, "Could not allocate RGB frame.\n");
exit(1);
}

// Determine required buffer size and allocate buffer
numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width,
pCodecCtx->height, 32);
buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height, 32);

// Initialize SWS context for software scaling
sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24,
SWS_BILINEAR, NULL, NULL, NULL);

// Create SDL window
screen = SDL_CreateWindow("FFmpeg Video Player",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
pCodecCtx->width,
pCodecCtx->height,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!screen) {
fprintf(stderr, "SDL: could not set video mode - %s\n", SDL_GetError());
exit(1);
}

renderer = SDL_CreateRenderer(screen, -1, 0);
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING,
pCodecCtx->width, pCodecCtx->height);

// Get start time
start_time = av_gettime();

// Read frames and decode
// Inside the main decoding loop:
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
if (avcodec_send_packet(pCodecCtx, &packet) == 0) {
while (avcodec_receive_frame(pCodecCtx, pFrame) == 0) {
// Convert the image
sws_scale(sws_ctx,
(uint8_t const* const*)pFrame->data,
pFrame->linesize,
0,
pCodecCtx->height,
pFrameRGB->data,
pFrameRGB->linesize);

// Update texture
SDL_UpdateTexture(texture, NULL, pFrameRGB->data[0], pFrameRGB->linesize[0]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);

// Timing control
pts = pFrame->best_effort_timestamp * av_q2d(pFormatCtx->streams[videoStream]->time_base);
frame_time = start_time + (int64_t)(pts * 1000000);
actual_delay = frame_time - av_gettime();
if (actual_delay > 0) {
av_usleep((unsigned)actual_delay);
}
}
}
}
av_packet_unref(&packet);

// Handle SDL events
SDL_Event event;
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) {
break;
}
}

end:
// Cleanup
av_free(buffer);
av_frame_free(&pFrameRGB);
av_frame_free(&pFrame);
avcodec_free_context(&pCodecCtx);
avformat_close_input(&pFormatCtx);

SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(screen);
SDL_Quit();

return 0;
}

二、C语言编写http视频服务器传输

1. C语言编写http服务器程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 8080
#define BUFFER_SIZE 4096
long fsize(FILE *file) {
long current_pos = ftell(file);
fseek(file, 0, SEEK_END);
long size = ftell(file);
fseek(file, current_pos, SEEK_SET);
return size;
}
void send_file(int client_socket, const char *filename) {
FILE *file = fopen(filename, "rb");
if (file == NULL) {
perror("Could not open file");
return;
}

// 发送 HTTP 响应头
char response_header[256];
sprintf(response_header, "HTTP/1.1 200 OK\r\n"
"Content-Type: video/mp4\r\n"
"Content-Length: %ld\r\n"
"Connection: close\r\n"
"\r\n",
(long)fsize(file));
send(client_socket, response_header, strlen(response_header), 0);

char buffer[BUFFER_SIZE];
size_t bytes_read;

// 逐块读取文件并发送到客户端
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
send(client_socket, buffer, bytes_read, 0);
}

fclose(file);
}

int main() {
int server_socket, client_socket;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);
char request[BUFFER_SIZE];

// 创建 TCP 套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}

// 设置服务器地址结构
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的接口
server_addr.sin_port = htons(PORT); // 设置端口


int optval = 1;
// 设置 SO_REUSEADDR
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
perror("setsockopt SO_REUSEADDR failed");
}

// 绑定套接字
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("Bind failed");
close(server_socket);
exit(EXIT_FAILURE);
}

// 开始监听
if (listen(server_socket, 5) == -1) {
perror("Listen failed");
close(server_socket);
exit(EXIT_FAILURE);
}

printf("Server listening on port %d...\n", PORT);

// 接受客户端连接
while (1) {
client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_socket == -1) {
perror("Accept failed");
continue;
}

printf("Client connected.\n");

// 读取客户端请求
recv(client_socket, request, sizeof(request) - 1, 0);
printf("Request: %s\n", request);

// 发送视频文件
send_file(client_socket, "input.mp4"); // 替换为您的视频文件名

// 关闭客户端套接字
close(client_socket);
printf("Client disconnected.\n");
}

// 关闭服务器套接字
close(server_socket);
return 0;
}

2.在客户端中播放

  1. 方法一:使用ffplay播放

    1
    ffplay -i http://10.0.3.1:8080
  2. 方法二:使用html中的video标签在浏览器中播放

    1
    2
    3
    4
    <video controls>
    <source src="http://10.0.3.1:8080" type="video/mp4">
    Your browser does not support the video tag.
    </video>
  3. 方法三:使用C语言代码播放,修改之前的客户端代码。(略)