aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2022-03-07 19:02:28 +0100
committerdec05eba <dec05eba@protonmail.com>2022-03-07 19:06:39 +0100
commit21c50903a68c253fa5fcb9ed5ac8ba5abb1142b9 (patch)
treec52907f4fe981e6b01a875cafc8f59bae0384987
parent123124a4a89d83f605d67d94145b4008b9a5b1d7 (diff)
Attempt to fix youtube video issue where video stops playing because audio finishes download
Remove youtube livestream code
-rw-r--r--plugins/youtube/YoutubeMediaProxy.hpp32
-rw-r--r--src/Downloader.cpp8
-rw-r--r--src/plugins/youtube/YoutubeMediaProxy.cpp267
3 files changed, 54 insertions, 253 deletions
diff --git a/plugins/youtube/YoutubeMediaProxy.hpp b/plugins/youtube/YoutubeMediaProxy.hpp
index eaef898..0fb2d6a 100644
--- a/plugins/youtube/YoutubeMediaProxy.hpp
+++ b/plugins/youtube/YoutubeMediaProxy.hpp
@@ -22,7 +22,7 @@ namespace QuickMedia {
virtual Error update() = 0;
virtual bool get_address(std::string &address) = 0;
- bool start_download(const std::string &media_url, ReadProgram &read_program, int64_t range_start, bool include_header, bool is_livestream = false, int livestream_sequence = -1);
+ bool start_download(const std::string &media_url, ReadProgram &read_program, int64_t range_start, int64_t end_range, bool include_header);
};
class YoutubeStaticMediaProxy : public YoutubeMediaProxy {
@@ -57,7 +57,9 @@ namespace QuickMedia {
ssize_t downloader_num_read_bytes = 0;
int64_t download_range_start = 0;
int64_t current_download_range = 0;
+ int64_t next_range_length = 0;
std::string download_header;
+ bool end_of_file = false;
bool download_header_finished = false;
bool download_header_sent = false;
bool download_header_remaining_sent = false;
@@ -70,32 +72,4 @@ namespace QuickMedia {
std::string client_request_buffer;
char download_read_buffer[16384];
};
-
- class YoutubeLiveStreamMediaProxy : public YoutubeMediaProxy {
- public:
- YoutubeLiveStreamMediaProxy() = default;
- YoutubeLiveStreamMediaProxy(YoutubeLiveStreamMediaProxy&) = delete;
- YoutubeLiveStreamMediaProxy&operator=(YoutubeLiveStreamMediaProxy&) = delete;
- ~YoutubeLiveStreamMediaProxy();
-
- bool start(const std::string &youtube_media_url, int64_t content_length) override;
- void stop() override;
- Error update() override;
- bool get_address(std::string &address) override;
- private:
- Error update_download_program_status();
- Error continue_send(const char *buffer_start, size_t total_bytes_to_write, int &buffer_offset);
- private:
- ReadProgram downloader_read_program;
- std::string youtube_media_url;
- int fd[2];
- int64_t livestream_sequence_num = -1;
- std::string download_header;
- bool download_header_finished = false;
- bool download_header_remaining_sent = false;
- int download_header_written_offset = 0;
- int download_read_buffer_offset = 0;
- ssize_t downloader_num_read_bytes = 0;
- char download_read_buffer[16384];
- };
} \ No newline at end of file
diff --git a/src/Downloader.cpp b/src/Downloader.cpp
index e5b1a3c..89d83cb 100644
--- a/src/Downloader.cpp
+++ b/src/Downloader.cpp
@@ -307,14 +307,10 @@ namespace QuickMedia {
int num_proxied_media = 0;
for(int i = 0; i < 2; ++i) {
media_proxies[i].output_filepath->clear();
- if(media_proxies[i].media_metadata->url.empty())
+ if(media_proxies[i].media_metadata->url.empty() || youtube_url_is_live_stream(media_proxies[i].media_metadata->url))
continue;
- if(youtube_url_is_live_stream(media_proxies[i].media_metadata->url))
- *media_proxies[i].media_proxy = std::make_unique<YoutubeLiveStreamMediaProxy>();
- else
- *media_proxies[i].media_proxy = std::make_unique<YoutubeStaticMediaProxy>();
-
+ *media_proxies[i].media_proxy = std::make_unique<YoutubeStaticMediaProxy>();
if(!(*media_proxies[i].media_proxy)->start(media_proxies[i].media_metadata->url, media_proxies[i].media_metadata->content_length)) {
fprintf(stderr, "Failed to load start youtube media proxy\n");
return false;
diff --git a/src/plugins/youtube/YoutubeMediaProxy.cpp b/src/plugins/youtube/YoutubeMediaProxy.cpp
index 9efd7ac..4d84674 100644
--- a/src/plugins/youtube/YoutubeMediaProxy.cpp
+++ b/src/plugins/youtube/YoutubeMediaProxy.cpp
@@ -30,12 +30,12 @@ static ssize_t read_eintr(int fd, void *buffer, size_t size) {
}
}
-static ssize_t write_all(int fd, const void *buffer, size_t size) {
+static ssize_t write_all_blocking(int fd, const void *buffer, size_t size) {
ssize_t bytes_written = 0;
while((size_t)bytes_written < size) {
ssize_t written = write(fd, (char*)buffer + bytes_written, size - bytes_written);
if(written == -1) {
- if(errno != EINTR)
+ if(errno != EINTR && errno != EWOULDBLOCK)
return -1;
} else {
bytes_written += written;
@@ -50,6 +50,9 @@ namespace QuickMedia {
static const char download_error_response_msg[] =
"HTTP/1.1 500 Internal Server Error\r\n"
"Content-Length: 0\r\n\r\n";
+ static const char download_finished_response_msg[] =
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 0\r\n\r\n";
static bool set_non_blocking(int fd) {
const int flags = fcntl(fd, F_GETFL, 0);
@@ -60,22 +63,15 @@ namespace QuickMedia {
// TODO: Restrict range end to remote file size (content-length which we have saved).
// TODO: Check if custom youtube redirect code is needed
- bool YoutubeMediaProxy::start_download(const std::string &media_url, ReadProgram &read_program, int64_t range_start, bool include_header, bool is_livestream, int livestream_sequence) {
- std::string r = std::to_string(range_start) + "-" + std::to_string(range_start + RANGE);
+ bool YoutubeMediaProxy::start_download(const std::string &media_url, ReadProgram &read_program, int64_t range_start, int64_t end_range, bool include_header) {
+ std::string r = std::to_string(range_start) + "-" + std::to_string(end_range);
std::string url = media_url;;
std::vector<const char*> args = { "curl",
"-H", "Accept-Language: en-US,en;q=0.5", "-H", "Connection: keep-alive",
"--no-buffer",
"-H", "user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
- "-g", "-s", "-L", "-f" };
-
- if(is_livestream) {
- if(livestream_sequence != -1)
- url += "&sq=" + std::to_string(livestream_sequence);
- } else {
- args.insert(args.end(), { "-r", r.c_str() });
- }
+ "-g", "-s", "-L", "-f", "-r", r.c_str() };
if(include_header)
args.push_back("-i");
@@ -124,6 +120,8 @@ namespace QuickMedia {
server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
server_addr.sin_port = htons(0);
+ next_range_length = RANGE;
+
if(bind(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("YoutubeStaticMediaProxy::start: bind failed");
goto err;
@@ -249,6 +247,20 @@ namespace QuickMedia {
return start_range;
}
+ static int64_t header_extract_content_length(const std::string &header) {
+ std::string content_length_str = header_extract_value(header, "content-length");
+ if(content_length_str.empty())
+ return 0;
+
+ errno = 0;
+ char *endptr;
+ int64_t content_length = strtoll(content_length_str.c_str(), &endptr, 10);
+ if(endptr == content_length_str.c_str() || errno != 0)
+ return 0;
+
+ return content_length;
+ }
+
// TODO: What about hls (live streams)? need to test with that. There may not be a need to use YoutubeMediaProxy for that case (in that case document it).
YoutubeStaticMediaProxy::Error YoutubeStaticMediaProxy::read_client_data() {
if(client_request_finished)
@@ -307,6 +319,8 @@ namespace QuickMedia {
download_header_remaining_sent = false;
download_header_written_offset = 0;
download_read_buffer_offset = 0;
+ next_range_length = RANGE;
+ end_of_file = false;
}
YoutubeStaticMediaProxy::Error YoutubeStaticMediaProxy::update_download_program_status(bool client_disconnected, int64_t new_range_start, bool restart_download) {
@@ -326,11 +340,10 @@ namespace QuickMedia {
downloader_read_program.read_fd = -1;
}
- // TODO: Why is this not 0 when download finishes?
if(program_status != 0) {
//fprintf(stderr, "YoutubeStaticMediaProxy::update_download_program_status: download failed, exit status: %d\n", program_status);
if(client_fd != -1) {
- write_all(client_fd, download_error_response_msg, sizeof(download_error_response_msg) - 1);
+ write_all_blocking(client_fd, download_finished_response_msg, sizeof(download_finished_response_msg) - 1);
close(client_fd);
client_fd = -1;
client_request_buffer.clear();
@@ -342,7 +355,7 @@ namespace QuickMedia {
if(client_disconnected) {
current_download_range = 0;
} else {
- current_download_range += RANGE + 1;
+ current_download_range += next_range_length;
}
if(new_range_start != -1) {
@@ -372,11 +385,17 @@ namespace QuickMedia {
download_header_remaining_sent = true;
}
- const bool start_download_success = start_download(youtube_media_url, downloader_read_program, current_download_range, new_range_start != -1);
+ int64_t end_range = current_download_range + RANGE;
+ if(end_range > content_length) {
+ end_range = content_length;
+ end_of_file = true;
+ }
+
+ const bool start_download_success = start_download(youtube_media_url, downloader_read_program, current_download_range, end_range, new_range_start != -1);
if(!start_download_success) {
fprintf(stderr, "YoutubeStaticMediaProxy::update_download_program_status: failed to start download\n");
if(client_fd != -1) {
- write_all(client_fd, download_error_response_msg, sizeof(download_error_response_msg) - 1);
+ write_all_blocking(client_fd, download_error_response_msg, sizeof(download_error_response_msg) - 1);
close(client_fd);
client_fd = -1;
client_request_buffer.clear();
@@ -414,7 +433,7 @@ namespace QuickMedia {
return Error::OK;
}
- const ssize_t num_bytes_written = write_all(client_fd, buffer_start + buffer_offset, num_bytes_to_write);
+ const ssize_t num_bytes_written = write_all_blocking(client_fd, buffer_start + buffer_offset, num_bytes_to_write);
if(num_bytes_written == -1) {
const int err = errno;
if(err == EAGAIN || err == EWOULDBLOCK) {
@@ -478,6 +497,17 @@ namespace QuickMedia {
return Error::ERROR;
}
} else if(downloader_num_read_bytes == 0) {
+ if(end_of_file) {
+ if(client_fd != -1) {
+ write_all_blocking(client_fd, download_finished_response_msg, sizeof(download_finished_response_msg) - 1);
+ close(client_fd);
+ client_fd = -1;
+ client_request_buffer.clear();
+ client_request_finished = false;
+ }
+ return Error::OK;
+ }
+
Error err = update_download_program_status(false, -1, true);
if(err != Error::OK)
return err;
@@ -507,6 +537,7 @@ namespace QuickMedia {
download_header_offset_to_end_of_header = header_end;
download_read_buffer_offset = -1;
+ next_range_length = header_extract_content_length(download_header.substr(0, header_end));
header_replace_content_length(download_header, header_end, content_length - download_range_start);
} else {
if(download_header.size() > MAX_BUFFER_SIZE) {
@@ -575,204 +606,4 @@ namespace QuickMedia {
//fprintf(stderr, "YoutubeStaticMediaProxy::accept_client: client connected!\n");
return new_client_fd;
}
-
- YoutubeLiveStreamMediaProxy::~YoutubeLiveStreamMediaProxy() {
- stop();
- }
-
- bool YoutubeLiveStreamMediaProxy::start(const std::string &youtube_media_url, int64_t) {
- fd[0] = -1;
- fd[1] = -1;
- if(pipe(fd) == -1) {
- perror("YoutubeLiveStreamMediaProxy::start: failed to open pipe");
- return false;
- }
-
- //if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1) {
- // perror("YoutubeLiveStreamMediaProxy::start: failed to open pipe");
- // return false;
- //}
-
- for(int i = 0; i < 2; ++i) {
- if(!set_non_blocking(fd[i])) {
- stop();
- return false;
- }
- }
-
- if(!start_download(youtube_media_url, downloader_read_program, 0, true, true)) {
- stop();
- return false;
- }
-
- this->youtube_media_url = youtube_media_url;
- return true;
- }
-
- void YoutubeLiveStreamMediaProxy::stop() {
- for(int i = 0; i < 2; ++i) {
- if(fd[i] != -1) {
- close(fd[i]);
- fd[i] = -1;
- }
- }
-
- if(downloader_read_program.read_fd != -1) {
- close(downloader_read_program.read_fd);
- downloader_read_program.read_fd = -1;
- }
-
- if(downloader_read_program.pid != -1) {
- kill(downloader_read_program.pid, SIGTERM);
- wait_program(downloader_read_program.pid);
- downloader_read_program.pid = -1;
- }
- }
-
- YoutubeMediaProxy::Error YoutubeLiveStreamMediaProxy::update_download_program_status() {
- int program_status = 0;
- if(wait_program_non_blocking(downloader_read_program.pid, &program_status) == 0 && program_status == 0)
- return Error::OK;
-
- downloader_read_program.pid = -1;
- if(downloader_read_program.read_fd != -1) {
- close(downloader_read_program.read_fd);
- downloader_read_program.read_fd = -1;
- }
-
- // TODO: Why is this not 0 when download finishes?
- if(program_status != 0) {
- //fprintf(stderr, "YoutubeLiveStreamMediaProxy::update_download_program_status: download failed, exit status: %d\n", program_status);
- stop();
- return Error::ERROR;
- }
-
- ++livestream_sequence_num;
- const bool start_download_success = start_download(youtube_media_url, downloader_read_program, 0, false, true, livestream_sequence_num);
- if(!start_download_success) {
- fprintf(stderr, "YoutubeLiveStreamMediaProxy::update_download_program_status: failed to start download\n");
- stop();
- return Error::ERROR;
- }
-
- return Error::OK;
- }
-
- YoutubeMediaProxy::Error YoutubeLiveStreamMediaProxy::continue_send(const char *buffer_start, size_t total_bytes_to_write, int &buffer_offset) {
- int num_bytes_to_write = total_bytes_to_write - buffer_offset;
- //assert(num_bytes_to_write >= 0);
- if(num_bytes_to_write < 0) num_bytes_to_write = 0;
- if(num_bytes_to_write == 0) {
- buffer_offset = 0;
- return Error::OK;
- }
-
- const ssize_t num_bytes_written = write_all(fd[1], buffer_start + buffer_offset, num_bytes_to_write);
- if(num_bytes_written == -1) {
- const int err = errno;
- if(err == EAGAIN || err == EWOULDBLOCK) {
- return Error::OK;
- } else if(err == EPIPE || err == ECONNRESET) {
- //fprintf(stderr, "YoutubeLiveStreamMediaProxy::continue_send: client disconnected\n");
- stop();
- return Error::ERROR;
- } else {
- perror("YoutubeLiveStreamMediaProxy::continue_send: write failed");
- return Error::ERROR;
- }
- } else if(num_bytes_written == 0) {
- //fprintf(stderr, "YoutubeLiveStreamMediaProxy::continue_send: client disconnected\n");
- stop();
- return Error::ERROR;
- } else if(num_bytes_written != num_bytes_to_write) {
- buffer_offset += num_bytes_written;
- } else {
- buffer_offset = 0;
- }
- return Error::OK;
- }
-
- YoutubeMediaProxy::Error YoutubeLiveStreamMediaProxy::update() {
- if(fd[1] == -1 || downloader_read_program.read_fd == -1)
- return Error::OK;
-
- if(download_read_buffer_offset == 0) {
- downloader_num_read_bytes = read_eintr(downloader_read_program.read_fd, download_read_buffer, sizeof(download_read_buffer));
- if(downloader_num_read_bytes == -1) {
- const int err = errno;
- if(err == EAGAIN || err == EWOULDBLOCK) {
- return Error::OK;
- } else {
- perror("YoutubeLiveStreamMediaProxy::update: curl read failed");
- return Error::ERROR;
- }
- } else if(downloader_num_read_bytes == 0) {
- Error err = update_download_program_status();
- if(err != Error::OK)
- return err;
- }
- }
-
- if(!download_header_finished) {
- download_header.append(download_read_buffer, downloader_num_read_bytes);
- size_t header_end = std::string::npos;
- const size_t offset_to_start_of_header = find_start_of_first_non_redirect_header(download_header.c_str(), download_header.size(), header_end);
- if(header_end != std::string::npos) {
- download_header.erase(0, offset_to_start_of_header);
- header_end -= offset_to_start_of_header;
-
- download_header_finished = true;
- download_header_remaining_sent = false;
- download_header_written_offset = header_end;
- download_read_buffer_offset = -1;
- fprintf(stderr, "header: |%.*s|\n", download_header_written_offset, download_header.c_str());
-
- if(livestream_sequence_num == -1) {
- // TODO: What about |header_end|?
- std::string sequence_num = header_extract_value(download_header, "x-sequence-num");
- fprintf(stderr, "server sequence num: |%s|\n", sequence_num.c_str());
- if(sequence_num.empty())
- fprintf(stderr, "YoutubeLiveStreamMediaProxy::update: missing sequence num from server\n");
- else
- livestream_sequence_num = strtoll(sequence_num.c_str(), nullptr, 10);
- }
- } else {
- if(download_header.size() > MAX_BUFFER_SIZE) {
- fprintf(stderr, "YoutubeLiveStreamMediaProxy::update: buffer is full (malicious server?)\n");
- if(downloader_read_program.pid != -1) {
- kill(downloader_read_program.pid, SIGTERM);
- wait_program(downloader_read_program.pid);
- downloader_read_program.pid = -1;
- }
- download_header_finished = true;
- return Error::ERROR;
- }
- }
- }
-
- if(download_header_finished && !download_header_remaining_sent) {
- Error err = continue_send(download_header.data(), download_header.size(), download_header_written_offset);
- if(err != Error::OK)
- return err;
-
- if(download_header_written_offset == 0) {
- download_header_remaining_sent = true;
- download_read_buffer_offset = 0;
- return Error::OK;
- }
- }
-
- if(download_header_remaining_sent)
- return continue_send(download_read_buffer, downloader_num_read_bytes, download_read_buffer_offset);
-
- return Error::OK;
- }
-
- bool YoutubeLiveStreamMediaProxy::get_address(std::string &address) {
- if(fd[0] == -1)
- return false;
-
- address = "fd://" + std::to_string(fd[0]);
- return true;
- }
}