MumbleVideoPlayer

Форк
0
236 строк · 6.9 Кб
1
#include <vector>
2
#include <iostream>
3
#include "../include/Output/Debug.hpp"
4
#include "../include/Output/FindByName.hpp"
5
#include "../include/Base.hpp"
6

7
extern "C" {
8
	#include <libavcodec/avcodec.h>
9
	#include <libavformat/avformat.h>
10
	#include <libavutil/imgutils.h>
11
	#include <libswscale/swscale.h>
12
	#include <termios.h>
13
	#include <fcntl.h>
14
}
15

16
std::string current_video_output_device = "None";
17

18
void show_help() {
19
	std::cout << "Usage: mumble <videofile>" << std::endl << std::endl;
20
	std::cout << "Supported Video Outputs:" << std::endl;
21

22
	for(const auto& i : Output::func_mapping) {
23
		std::cout << "    - " << i.first << std::endl;
24
	}
25
}
26

27
const char* inputFileName = nullptr;
28

29
struct PlayerInfo player_info;
30

31
int main(int argc, char** argv) {
32
	printf("Mumble Video Player by NDRAEY (c) 2023\n");
33

34
	std::vector<char*> charptr_args(argv + 1, argv + argc);
35

36
	for(size_t i = 0, list_size = charptr_args.size(); i < list_size; i++) {
37
		const auto& elem = std::string(charptr_args[i]);
38

39
		if(elem == "--help") { // Whut?
40
			show_help();
41

42
			exit(0);
43
		}
44

45
		if(elem == "--vo") {
46
			if(i + 1 >= list_size) {
47
				std::cerr << "error: specify video output" << std::endl;
48

49
				exit(1);
50
			}
51
			current_video_output_device = std::string(charptr_args[++i]);
52
		} else {
53
			std::cerr << "Treating as input file path: " << elem << std::endl;
54

55
			inputFileName = charptr_args[i];
56
		}
57
	}
58

59
	if(!inputFileName) {
60
		std::cerr << "No input file!" << std::endl;
61
		exit(1);
62
	}
63

64
    avformat_network_init();
65

66
    AVFormatContext* inputFormatContext = nullptr;
67
    AVCodecContext* videoCodecContext = nullptr;
68
    AVPacket* packet = nullptr;
69
    AVFrame* frame = nullptr;
70

71
    if (avformat_open_input(&inputFormatContext, inputFileName, nullptr, nullptr) < 0) {
72
        printf("Could not open input file!\n");
73
        return -1;
74
    }
75

76
    if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) {
77
        printf("No stream info!\n");
78
        return -1;
79
    }
80

81
	printf("Input: %s\n", inputFileName);
82
	printf("Streams: %d\n", inputFormatContext->nb_streams);
83

84
    unsigned int videoStreamIndex = 0xFFFFFFFF;
85
    for (unsigned int i = 0; i < inputFormatContext->nb_streams; i++) {
86
        if (inputFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
87
            videoStreamIndex = i;
88
            break;
89
        }
90
    }
91

92
    if (videoStreamIndex == 0xFFFFFFFF) {
93
        printf("No video codec!\n");
94
        return -1;
95
    }
96

97
    AVCodecParameters* videoCodecParameters = inputFormatContext->streams[videoStreamIndex]->codecpar;
98
    const AVCodec* videoCodec = avcodec_find_decoder(videoCodecParameters->codec_id);
99

100
	if (videoCodec == nullptr) {
101
        printf("No video codec!\n");
102
        return -1;
103
    }
104

105
    videoCodecContext = avcodec_alloc_context3(videoCodec);
106
    if (avcodec_parameters_to_context(videoCodecContext, videoCodecParameters) < 0) {
107
        printf("No video codec!\n");
108
        return -1;
109
    }
110

111
    if (avcodec_open2(videoCodecContext, videoCodec, nullptr) < 0) {
112
        printf("No video codec!\n");
113
        return -1;
114
    }
115

116
	AVStream* video_stream = inputFormatContext->streams[videoStreamIndex];
117
	player_info.current_video_stream = video_stream;
118

119
	// Print information about media file
120
	printf("Codec: %s\n", videoCodec->name);
121
	printf("Framerate: %.03f fps\n", (double)video_stream->r_frame_rate.num / (double)video_stream->r_frame_rate.den);
122

123
	player_info.framerate = (double)video_stream->r_frame_rate.num / (double)video_stream->r_frame_rate.den;
124

125
	printf("Frame size: %d\n", videoCodecContext->frame_size);
126
	printf("Sample rate: %d\n", videoCodecParameters->sample_rate);
127
	printf("Size: %dx%d\n", videoCodecContext->width, videoCodecContext->height);
128
	printf("Duration: %02lld:%02lld:%02lld.%.00f\n",
129
		   (long long int)inputFormatContext->duration / (3600 * (long long int)AV_TIME_BASE), // hour
130
		   (long long int)(inputFormatContext->duration / (60 * AV_TIME_BASE)) % 60, // minute
131
		   (long long int)(inputFormatContext->duration / AV_TIME_BASE) % 60, // seconds
132
		   (double)(inputFormatContext->duration % AV_TIME_BASE) / 1000.0); // millis
133

134
	player_info.window_width = videoCodecContext->width;
135
	player_info.window_height =  videoCodecContext->height;
136
	player_info.window_pixfmt = AV_PIX_FMT_RGB24;
137

138
    packet = av_packet_alloc();
139
    frame = av_frame_alloc();
140

141
	// Create Output object here.
142
	// Initialize it with some needed information.
143

144
	Output::Output* output = Output::FindByName(current_video_output_device, &player_info);
145

146
	if(output == nullptr) {
147
		std::cerr << "error: no suitable video output found!" << std::endl;
148

149
		exit(1);
150
	}
151

152
	// TODO: Controlling with keyboard.
153
	// FIXME: I cannot use termios because termios and O_NONBLOCK on stdin is breaking Terminal output
154

155
	std::cout << "Millis per frame: " << std::endl;
156

157
	ssize_t frameskip = 0;
158

159
	player_info.playing_started = timeInMilliseconds();
160

161
	while (av_read_frame(inputFormatContext, packet) >= 0) {
162
//		while(player_info.is_paused);
163
		auto start = timeInMilliseconds();
164

165
        if (packet->stream_index == (int)videoStreamIndex) {
166
            avcodec_send_packet(videoCodecContext, packet);
167

168
            while (avcodec_receive_frame(videoCodecContext, frame) >= 0) {
169
				player_info.frames_processed += 1;
170

171
				while (frameskip-- > 0) {
172
//					usleep(floor(1000000.0 / player_info.framerate));
173
					goto readnext;
174
				}
175

176
				AVFrame *rgbFrame = av_frame_alloc();
177
				rgbFrame->format = AV_PIX_FMT_RGB24;
178
				rgbFrame->width = frame->width;
179
				rgbFrame->height = frame->height;
180
				av_frame_get_buffer(rgbFrame, 32);
181

182
//				printf("SWS: %d x %d\n", player_info.window_width, player_info.window_height);
183

184
				SwsContext *swsContext = sws_getContext(
185
					frame->width, frame->height,
186
					videoCodecContext->pix_fmt,
187
					player_info.window_width, player_info.window_height,
188
					player_info.window_pixfmt,
189
					SWS_BILINEAR, nullptr, nullptr, nullptr
190
				);
191

192
				sws_scale(
193
					swsContext,
194
					frame->data,
195
					frame->linesize,
196
					0,
197
					frame->height,
198
					rgbFrame->data, rgbFrame->linesize
199
				);
200

201
				sws_freeContext(swsContext);
202

203

204
				// Writing frame data here!
205
				output->write(rgbFrame);
206

207
				auto end = timeInMilliseconds();
208
				player_info.delay = (double)(end - start);
209
				player_info.render_framerate = 1000.0 / player_info.delay;
210
				player_info.frame_is_waiting = player_info.render_framerate > player_info.framerate;
211

212
				if (player_info.frame_is_waiting) {
213
					usleep((int)((1000.0 / player_info.framerate) - player_info.delay) * 1000);
214
				} else {
215
					frameskip = floor(player_info.delay / player_info.framerate);
216
				}
217

218
				av_frame_free(&rgbFrame);
219
            }
220
        }
221
		readnext:
222
        av_packet_unref(packet);
223
    }
224

225
	// FIXME: Compiler warning: "Delete called on non-final 'Output::Debug' that has virtual functions but non-virtual destructor"
226
	delete (Output::Debug*)output;
227

228
    // Free allocated resources
229
    av_packet_free(&packet);
230
    av_frame_free(&frame);
231
    avcodec_free_context(&videoCodecContext);
232
    avformat_close_input(&inputFormatContext);
233
    avformat_network_deinit();
234

235
    return 0;
236
}
237

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.