下载TS文件片段
处理完Master PlayList和Media PlayList之后就可以开始下载TS视频片段了
下载的流程如下:
1、初始化本地的文件名
2、设置本地文件的访问权限
3、创建任务队列
4、创建PlayList更新线程,因为服务器上的m3u8文件可能会经过改动,因此需要实时更新
5、遍历任务队列,队列里面存放的是TS媒体文件的信息,根据这些信息,下载TS视频文件。另外,如果下载的文件的大小已经超过了最大的文件的大小 ,那么表示服务器上的m3u8文件已经修改,需要更新PlayList
// 下载TS文件
int download_hls(struct hls_media_playlist *me,bool live_streamming,int max_file_size,const char* custom_filename, std::string user_agent, bool force_overwrite=true)
{
if(live_streamming){
MSG_VERBOSE("Downloading Live Streaming\n");
}else{
MSG_VERBOSE("Downloading %d segments.\n", me->count);
}
char filename[MAX_FILENAME_LEN];
// 初始化本地文件名
if (custom_filename!=NULL && *custom_filename!='\0') {
strcpy(filename, custom_filename);
} else {
strcpy(filename, "000_hls_output.ts");
}
// 访问权限的设置
if (access(filename, F_OK) != -1) {
if (force_overwrite) {
if (remove(filename) != 0) {
MSG_ERROR("Error overwriting file");
exit(1);
}
} else {
char userchoice;
MSG_PRINT("File already exists. Overwrite? (y/n) ");
scanf("\n%c", &userchoice);
if (userchoice == 'y') {
if (remove(filename) != 0) {
MSG_ERROR("Error overwriting file");
exit(1);
}
} else {
MSG_WARNING("Choose a different filename. Exiting.\n");
exit(0);
}
}
}
// 创建任务队列
std::shared_ptr<Queue<std::shared_ptr<hls_media_segment>>> segment_queue(new Queue<std::shared_ptr<hls_media_segment>>());
std::mutex mutex_;
std::condition_variable cond_;
bool flag_finish=false;
int lattest_sequence_number=-1;
// 更新PlayList的线程,服务器上的m3u8文件可能更新得很频繁
std::shared_ptr<std::thread> update_playlist_thread=std::make_shared<std::thread>([&]{
bool first_download_iteration = true;
int wait_second= me->target_duration > 0 ?me->target_duration:1;
while(first_download_iteration || live_streamming){
{
std::lock_guard<std::mutex> mlock(mutex_);
if(flag_finish){
break;
}
}
first_download_iteration = false;
int i=0,current_file_count=0;
for (; i < me->count&&me->media_segment[i].sequence_number <= lattest_sequence_number; i++);
//Build downloading queue
current_file_count = i;
for (; i < me->count; i++) {
//MSG_PRINT(me->media_segment[i].url);
segment_queue->push(std::make_shared<hls_media_segment>(me->media_segment[i]));
lattest_sequence_number= me->media_segment[i].sequence_number;
}
MSG_PRINT("Update %d ts to queue\n",me->count - current_file_count);
{
std::unique_lock<std::mutex> mlock(mutex_);
cond_.notify_one();
}
if(live_streamming){
if(me->count - current_file_count < 1){
wait_second = wait_second*2;
}else{
wait_second = me->target_duration;
}
MSG_PRINT("Sleep %d Seconds\n",wait_second);
std::this_thread::sleep_for(std::chrono::seconds(wait_second));
if (handle_hls_media_playlist(me,user_agent)) {
break;
}
MSG_PRINT("Reload m3u3 Playlist\n");
}
}
});
FILE *pFile = fopen(filename, "wb");
int file_size=0;
{
std::unique_lock<std::mutex> mlock(mutex_);
cond_.wait(mlock,[segment_queue]()->bool{return !(segment_queue->empty());});
}
while(!segment_queue->empty()){
auto segment=segment_queue->pop();
MSG_PRINT("Thread %d Downloading segment %d\n", 0, segment->sequence_number);
auto buffer = std::make_shared<ByteBuffer>();
// 下载TS视频片段
buffer->len = (int)get_data_from_url(segment->url.c_str(), NULL, &(buffer->data), BINARY,user_agent);
if (me->encryption == true && me->encryptiontype == ENC_AES128) {
decrypt_aes128(segment.get(), buffer.get());
} else if (me->encryption == true && me->encryptiontype == ENC_AES_SAMPLE) {
decrypt_sample_aes(segment.get(), buffer.get());
}
// 写入本地文件中
fwrite(buffer->data, 1, buffer->len, pFile);
fflush(pFile);
free(buffer->data);
file_size=file_size+buffer->len;
MSG_PRINT("Totally Downloading Size: %d....Max size %d\n", file_size,max_file_size);
// 如果下载的文件的大小已经超过了最大的文件大小,那么表示服务器上的PlayList可能已经更新了,本地应该及时更新
if(max_file_size>0 && file_size >= max_file_size){
std::lock_guard<std::mutex> mlock(mutex_);
flag_finish = true; // 设置为true,那么更新线程就可以继续执行了
MSG_PRINT("Reach Max Size %d! Finish.\n", max_file_size);
break;
}
//delete segment;
}
fclose(pFile);
update_playlist_thread->join();
return 0;
}