HLS學習(七)HLSDownloader原始碼分析(6)下載TS檔案片段
下載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; }
相關推薦
HLS學習(七)HLSDownloader原始碼分析(6)下載TS檔案片段
下載TS檔案片段 處理完Master PlayList和Media PlayList之後就可以開始下載TS視訊片段了 下載的流程如下: 1、初始化本地的檔名 2、設定本地檔案的訪問許可權 3、建立任務佇列 4、建立PlayList更新執行緒,因為伺服器上的m3u8檔案
HLS學習(六)HLSDownloader原始碼分析(5)解析Media PlayList
解析Media PlayList PlayList就是m3u8檔案或者索引檔案,Media PlayList也叫媒體播放列表或者媒體索引檔案解析Media PlayList的流程如下:1、如果hls_media_playlist結構體中媒體資訊存在,那麼先刪除2
HLS學習(四)HLSDownloader原始碼分析(3)總體流程
總體流程 下載一個媒體檔案的流程 1、初始化 2、根據URL下載m3u8檔案(即PlayList檔案) 3、判斷m3u8檔案的型別,是Master PlayList還是Media PlayList 4、如果是Master PlayList,那麼分析這個PlayList
HLS學習(五)HLSDownloader原始碼分析(4)解析Master PlayList
解析Master PlayList PlayList就是m3u8檔案或者索引檔案,Master PlayList也叫一級索引檔案。 解析Master PlayList的過程如下: 1、
opendaylight(Li) l2switch 原始碼分析(2)--parent
本文主要介紹l2switch中的parent工程,該工程定義了執行L2switch所使用的依賴模組以及版本等。 該工程下只有一個pom.xml檔案,下面對該檔案中的主要內容進行說明: 1. <parent> <groupId>org.open
Flume NG原始碼分析(七)ChannelSelector
前幾篇介紹了Flume NG Source元件的基本情況,接下來看看Channel相關的元件,Channel相關元件有: 1. Channel 2. ChannelSelector 3. Interceptor / InterceptorChain 4. ChannelProcess
OpenCV學習筆記(31)KAZE 演算法原理與原始碼分析(五)KAZE的原始碼優化及與SIFT的比較
KAZE系列筆記: 1. OpenCV學習筆記(27)KAZE 演算法原理與原始碼分析(一)非線性擴散濾波 2. OpenCV學習筆記(28)KAZE 演算法原理與原始碼分析(二)非線性尺度空間構建 3. Op
OpenCV學習筆記(30)KAZE 演算法原理與原始碼分析(四)KAZE特徵的效能分析與比較
KAZE系列筆記: 1. OpenCV學習筆記(27)KAZE 演算法原理與原始碼分析(一)非線性擴散濾波 2. OpenCV學習筆記(28)KAZE 演算法原理與原始碼分析(二)非線性尺度空間構
co_routine.cpp/.h/inner.h(上 : 協程的建立)—— libco原始碼分析、學習筆記
由於本原始碼蠻長的,所以按照功能劃分模組來分析。 一、協程(task)的建立 參考部落格 struct stCoRoutine_t為協程環境變數型別(主要是物理環境,比如上下文)。儲存著執行時協程執行環境所有資訊。 程式碼段 小部件 struct s
co_routine.cpp/.h/inner.h(第三部分 : 協程的執行)—— libco原始碼分析、學習筆記
由於本原始碼蠻長的,所以按照功能劃分模組來分析,分為若干部分,詳見二級目錄↑ 三、協程的執行 void co_yield_env( stCoRoutineEnv_t *env );//將當前執行的env從協程棧中出棧並將執行權交給父協程。 void co_y
co_routine.cpp/.h/inner.h(第四部分:定時器和事件迴圈)—— libco原始碼分析、學習筆記
由於本原始碼蠻長的,所以按照功能劃分模組來分析,分為若干部分,詳見二級目錄↑ 定時器和事件迴圈 libco管理定時事件便是使用時間輪這種資料結構 定時器前驅知識本篇只稍微提一下,具體知識請參考《Linux高效能伺服器程式設計 遊雙 著》第11章,或其他方式學
Redis原始碼分析(十七)--- multi事務操作
redis作為一非關係型資料庫,竟然同樣擁有與RDBMS的事務操作,不免讓我覺得比較驚訝。在redis就專門有檔案就是執行事務的相關操作的。也可以讓我們領略一下,在Redis的程式碼中是如何實現事務操作。首先亮出mulic.c下面的一些API。 /* ===
mochiweb原始碼分析(七)
前面說到了filelib:is_dir這函式,如果是目錄,則執行true流程,進入mochiweb_request:maybe_redirect/4這函式 然後根據第一個引數是否是[],呼叫不同的分支 回到之前說的true、false判斷,如果是檔案,則呼叫mochiweb_reque
Go學習之go-ethereum【以太坊】原始碼分析(一)
關於Go語言環境的安裝與配置,我在《入門篇》進行了詳細講解,有需要的朋友可以前往閱讀,本文進入當下比較火熱的區塊鏈專案 - 以太坊(go-ethereum)進行原始碼解讀。本文內容純屬個人見解,有錯誤理解或者不足之處還請見諒,歡迎一起交流學習。 - 環境準備 -
TensorFlow學習筆記之原始碼分析(3)---- retrain.py
"""簡單呼叫Inception V3架構模型的學習在tensorboard顯示了摘要。 這個例子展示瞭如何採取一個Inception V3架構模型訓練ImageNet影象和訓練新的頂層,可以識別其他類的影象。 每個影象裡,頂層接收作為輸入的一個2048維向量。這
Java併發包原始碼學習之執行緒池(一)ThreadPoolExecutor原始碼分析
Java中使用執行緒池技術一般都是使用Executors這個工廠類,它提供了非常簡單方法來建立各種型別的執行緒池: public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService
Java併發包原始碼學習之AQS框架(四)AbstractQueuedSynchronizer原始碼分析
經過前面幾篇文章的鋪墊,今天我們終於要看看AQS的廬山真面目了,建議第一次看AbstractQueuedSynchronizer 類原始碼的朋友可以先看下我前面幾篇文章: 分析原始碼是非常枯燥乏味的一件事,其實程式碼本身其實就是最好的說明了,因此基本都是貼出一些程式碼加上一些註釋, 因為Abstract
Libevent原始碼分析(七)--- IOCP
event_base中有一個iocp變數,event_base的初始化函式中會呼叫event_base_start_iocp開啟iocp功能,event_base_start_iocp又會呼叫event_iocp_port_launch來初始化IOCP:
Ceph 學習——OSD讀寫流程與原始碼分析(一)
直接上圖: 同樣當前最新的版本,和之前的版本有所不同,有一些模組簡化了,類的名字也改了。先介紹圖中涉及的相關的類,然後在對類中具體函式主要呼叫流程進行分析。 OSD 模組主要的類 盜圖:其中ReplicatedPG 在最新的版本中
七.linux開發之uboot移植(七)——uboot原始碼分析2-啟動第二階段之start_armboot函式分析1
一.uboot啟動第二階段之start_armboot函式簡介 1.start_armboot函式簡介 (1)這個函式在uboot/lib_arm/board.c的第444行開始到908行結束。 (2)、即一個函式組成uboot第二階段 2、