MumbleVideoPlayer
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
7extern "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
16std::string current_video_output_device = "None";
17
18void show_help() {
19std::cout << "Usage: mumble <videofile>" << std::endl << std::endl;
20std::cout << "Supported Video Outputs:" << std::endl;
21
22for(const auto& i : Output::func_mapping) {
23std::cout << " - " << i.first << std::endl;
24}
25}
26
27const char* inputFileName = nullptr;
28
29struct PlayerInfo player_info;
30
31int main(int argc, char** argv) {
32printf("Mumble Video Player by NDRAEY (c) 2023\n");
33
34std::vector<char*> charptr_args(argv + 1, argv + argc);
35
36for(size_t i = 0, list_size = charptr_args.size(); i < list_size; i++) {
37const auto& elem = std::string(charptr_args[i]);
38
39if(elem == "--help") { // Whut?
40show_help();
41
42exit(0);
43}
44
45if(elem == "--vo") {
46if(i + 1 >= list_size) {
47std::cerr << "error: specify video output" << std::endl;
48
49exit(1);
50}
51current_video_output_device = std::string(charptr_args[++i]);
52} else {
53std::cerr << "Treating as input file path: " << elem << std::endl;
54
55inputFileName = charptr_args[i];
56}
57}
58
59if(!inputFileName) {
60std::cerr << "No input file!" << std::endl;
61exit(1);
62}
63
64avformat_network_init();
65
66AVFormatContext* inputFormatContext = nullptr;
67AVCodecContext* videoCodecContext = nullptr;
68AVPacket* packet = nullptr;
69AVFrame* frame = nullptr;
70
71if (avformat_open_input(&inputFormatContext, inputFileName, nullptr, nullptr) < 0) {
72printf("Could not open input file!\n");
73return -1;
74}
75
76if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) {
77printf("No stream info!\n");
78return -1;
79}
80
81printf("Input: %s\n", inputFileName);
82printf("Streams: %d\n", inputFormatContext->nb_streams);
83
84unsigned int videoStreamIndex = 0xFFFFFFFF;
85for (unsigned int i = 0; i < inputFormatContext->nb_streams; i++) {
86if (inputFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
87videoStreamIndex = i;
88break;
89}
90}
91
92if (videoStreamIndex == 0xFFFFFFFF) {
93printf("No video codec!\n");
94return -1;
95}
96
97AVCodecParameters* videoCodecParameters = inputFormatContext->streams[videoStreamIndex]->codecpar;
98const AVCodec* videoCodec = avcodec_find_decoder(videoCodecParameters->codec_id);
99
100if (videoCodec == nullptr) {
101printf("No video codec!\n");
102return -1;
103}
104
105videoCodecContext = avcodec_alloc_context3(videoCodec);
106if (avcodec_parameters_to_context(videoCodecContext, videoCodecParameters) < 0) {
107printf("No video codec!\n");
108return -1;
109}
110
111if (avcodec_open2(videoCodecContext, videoCodec, nullptr) < 0) {
112printf("No video codec!\n");
113return -1;
114}
115
116AVStream* video_stream = inputFormatContext->streams[videoStreamIndex];
117player_info.current_video_stream = video_stream;
118
119// Print information about media file
120printf("Codec: %s\n", videoCodec->name);
121printf("Framerate: %.03f fps\n", (double)video_stream->r_frame_rate.num / (double)video_stream->r_frame_rate.den);
122
123player_info.framerate = (double)video_stream->r_frame_rate.num / (double)video_stream->r_frame_rate.den;
124
125printf("Frame size: %d\n", videoCodecContext->frame_size);
126printf("Sample rate: %d\n", videoCodecParameters->sample_rate);
127printf("Size: %dx%d\n", videoCodecContext->width, videoCodecContext->height);
128printf("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
134player_info.window_width = videoCodecContext->width;
135player_info.window_height = videoCodecContext->height;
136player_info.window_pixfmt = AV_PIX_FMT_RGB24;
137
138packet = av_packet_alloc();
139frame = av_frame_alloc();
140
141// Create Output object here.
142// Initialize it with some needed information.
143
144Output::Output* output = Output::FindByName(current_video_output_device, &player_info);
145
146if(output == nullptr) {
147std::cerr << "error: no suitable video output found!" << std::endl;
148
149exit(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
155std::cout << "Millis per frame: " << std::endl;
156
157ssize_t frameskip = 0;
158
159player_info.playing_started = timeInMilliseconds();
160
161while (av_read_frame(inputFormatContext, packet) >= 0) {
162// while(player_info.is_paused);
163auto start = timeInMilliseconds();
164
165if (packet->stream_index == (int)videoStreamIndex) {
166avcodec_send_packet(videoCodecContext, packet);
167
168while (avcodec_receive_frame(videoCodecContext, frame) >= 0) {
169player_info.frames_processed += 1;
170
171while (frameskip-- > 0) {
172// usleep(floor(1000000.0 / player_info.framerate));
173goto readnext;
174}
175
176AVFrame *rgbFrame = av_frame_alloc();
177rgbFrame->format = AV_PIX_FMT_RGB24;
178rgbFrame->width = frame->width;
179rgbFrame->height = frame->height;
180av_frame_get_buffer(rgbFrame, 32);
181
182// printf("SWS: %d x %d\n", player_info.window_width, player_info.window_height);
183
184SwsContext *swsContext = sws_getContext(
185frame->width, frame->height,
186videoCodecContext->pix_fmt,
187player_info.window_width, player_info.window_height,
188player_info.window_pixfmt,
189SWS_BILINEAR, nullptr, nullptr, nullptr
190);
191
192sws_scale(
193swsContext,
194frame->data,
195frame->linesize,
1960,
197frame->height,
198rgbFrame->data, rgbFrame->linesize
199);
200
201sws_freeContext(swsContext);
202
203
204// Writing frame data here!
205output->write(rgbFrame);
206
207auto end = timeInMilliseconds();
208player_info.delay = (double)(end - start);
209player_info.render_framerate = 1000.0 / player_info.delay;
210player_info.frame_is_waiting = player_info.render_framerate > player_info.framerate;
211
212if (player_info.frame_is_waiting) {
213usleep((int)((1000.0 / player_info.framerate) - player_info.delay) * 1000);
214} else {
215frameskip = floor(player_info.delay / player_info.framerate);
216}
217
218av_frame_free(&rgbFrame);
219}
220}
221readnext:
222av_packet_unref(packet);
223}
224
225// FIXME: Compiler warning: "Delete called on non-final 'Output::Debug' that has virtual functions but non-virtual destructor"
226delete (Output::Debug*)output;
227
228// Free allocated resources
229av_packet_free(&packet);
230av_frame_free(&frame);
231avcodec_free_context(&videoCodecContext);
232avformat_close_input(&inputFormatContext);
233avformat_network_deinit();
234
235return 0;
236}
237