使用ffmpeg-3.3.3和openssl-1.0.2l對TS封裝H264編碼的檔案進行NAL的加密,以下是我實作的接口代碼:
//#include "StdAfx.h"
#include "TroubleShoot.h"
#ifdef __cplusplus //
extern "C"{
#endif
#include "TSEncrypt.h"
#include
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include
#include
#include
#include
static FILE *fp_stream=NULL; //輸出檔案句柄 static unsigned char s_cp_key[SUMA_KEY_AND_IV_LEN]={0}; //cpck static unsigned char s_cp_iv[SUMA_KEY_AND_IV_LEN]={0}; //cpiv static int s_en_type=0; //讀檔案資料後是否要進行解密處理 static bool enable_encrypt_card=false; //是否啟用硬體加密卡 #if 1 //加密 static int aescbc128_encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, unsigned char *ciphertext) { if((plaintext==NULL)||(plaintext_len==0)||(key==NULL)||(iv==NULL)||(ciphertext==NULL)) return -1; if(enable_encrypt_card) { bool status=false;// = AESEncryptInCBC(plaintext_len,(const BYTE *)plaintext,(const BYTE *)key,(const BYTE *)iv,(BYTE *)ciphertext); if(status) return plaintext_len; else return -1; } else { EVP_CIPHER_CTX *ctx=NULL; int len=0; int ciphertext_len=0; ctx = EVP_CIPHER_CTX_new(); if(ctx==NULL) return -1; EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); EVP_CIPHER_CTX_set_padding(ctx, 0); EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len); ciphertext_len = len; EVP_EncryptFinal_ex(ctx, ciphertext + len, &len); ciphertext_len += len; EVP_CIPHER_CTX_free(ctx); return ciphertext_len; } } //解密 static int aescbc128_decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key, unsigned char *iv, unsigned char *plaintext) { if((ciphertext==NULL)||(ciphertext_len==0)||(key==NULL)||(iv==NULL)||(plaintext==NULL)) return -1; if(enable_encrypt_card) { bool status=false;// = AESDecryptInCBC(ciphertext_len,(const BYTE *)ciphertext,(const BYTE *)key,(const BYTE *)iv,(BYTE *)plaintext); if(status) return ciphertext_len; else return -1; } else { EVP_CIPHER_CTX *ctx=NULL; int len=0; int plaintext_len=0; ctx = EVP_CIPHER_CTX_new(); if(ctx==NULL) return -1; EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv); EVP_CIPHER_CTX_set_padding(ctx, 0); EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len); plaintext_len = len; EVP_DecryptFinal_ex(ctx, plaintext + len, &len); plaintext_len += len; EVP_CIPHER_CTX_free(ctx); return plaintext_len; } } #endif /*全檔案加解密接口*/ int Suma_full_file_encrypt_or_decrypt(char *in_path,char *out_path,unsigned char *key,unsigned char *iv,int flag) { struct stat sta_buff; int ret=0; unsigned char *tmp_buf=NULL; unsigned int tmp_buf_size=SUMA_READ_DATA_LEN; unsigned int read_len=0; unsigned int write_len=0; unsigned int total_write_len=0; unsigned int processed_len=0; unsigned int tmp_len=0; FILE* fp_r=NULL; FILE* fp_w=NULL; unsigned char tmp_iv[SUMA_KEY_AND_IV_LEN]={0}; DvtLog(DVT_MSG_TYPE_INFORMATION,"全檔案加解密操作開始"); if((in_path==NULL)||(out_path==NULL)||(key==NULL)||(iv==NULL)) { DvtLog(DVT_MSG_TYPE_ERROR,"全檔案加解密的參數錯誤!"); ret=-1; return ret; } ret = stat(in_path, &sta_buff); if(ret != 0) { DvtLog(DVT_MSG_TYPE_ERROR,"全檔案加解密時輸入檔案不存在!"); return ret; } fp_r=fopen(in_path,"rb"); if(fp_r == NULL) { DvtLog(DVT_MSG_TYPE_ERROR,"全檔案加解密時打開輸入檔案失敗!"); ret=-1; goto end; } fp_w=fopen(out_path,"wb+"); if(fp_w == NULL) { DvtLog(DVT_MSG_TYPE_ERROR,"全檔案加解密時打開輸出檔案失敗!"); ret=-1; goto end; } tmp_buf = (unsigned char *)malloc(tmp_buf_size); if(tmp_buf == NULL) { DvtLog(DVT_MSG_TYPE_ERROR,"全檔案加解密時配置設定緩存失敗!"); ret=-1; goto end; } while(sta_buff.st_size>total_write_len) { read_len=fread(tmp_buf,1,tmp_buf_size,fp_r); tmp_len=read_len/SUMA_KEY_AND_IV_LEN*SUMA_KEY_AND_IV_LEN; if(flag == 0) { processed_len = aescbc128_encrypt (tmp_buf, tmp_len, key, iv,tmp_buf); memcpy(iv,tmp_buf+tmp_len-SUMA_KEY_AND_IV_LEN,SUMA_KEY_AND_IV_LEN); } else { memcpy(tmp_iv,tmp_buf+tmp_len-SUMA_KEY_AND_IV_LEN,SUMA_KEY_AND_IV_LEN); processed_len = aescbc128_decrypt (tmp_buf, tmp_len, key, iv,tmp_buf); memcpy(iv,tmp_iv,SUMA_KEY_AND_IV_LEN); } if(processed_len != tmp_len) { DvtLog(DVT_MSG_TYPE_ERROR,"全檔案加解密時加解密後的長度不等于原始長度!"); ret=-1; goto end; } write_len = fwrite(tmp_buf,1,read_len,fp_w); if(write_len != read_len) { DvtLog(DVT_MSG_TYPE_ERROR,"全檔案加解密時向輸出檔案寫入資料失敗!"); ret=-1; goto end; } total_write_len+=write_len; } end: if(tmp_buf != NULL) { free(tmp_buf); tmp_buf=NULL; } if(fp_r != NULL) fclose(fp_r); if(fp_w != NULL) fclose(fp_w); if(ret == 0) DvtLog(DVT_MSG_TYPE_INFORMATION,"全檔案加解密操作成功!"); return ret; } /*解密nal單中繼資料接口*/ static int decrypt_nal_data(unsigned char *key,unsigned char *iv,unsigned char *input,unsigned int in_len,unsigned int *out_len){ unsigned char *bufEncData=NULL; int changed_len=0,k=0,j=0,tmp_buf_data_len=0,decrypt_len=0; unsigned char *tmp_buf=NULL; int ret=-1; if((key==NULL)||(iv==NULL)||(input==NULL)||(out_len==NULL)||(in_len
k+2; j--) bufEncData[j] = bufEncData[j-1]; bufEncData[k+2] = 0x03; dwLen++; continue; } if(bufEncData[k] == 0x0 && bufEncData[k+1] == 0x0 && bufEncData[k+2] == 0x03 /*&& bufEncData[k+3] > 0x03*/){ for(j=dwLen; j>k+2; j--) bufEncData[j] = bufEncData[j-1]; bufEncData[k+2] = 0x03; dwLen++; continue; } } return 0; } static int read_file(void *opaque, uint8_t *buf, int buf_size){ if((buf==NULL)||(buf_size==0)){ return -1; } if(!feof(fp_stream)){ int read_len=fread(buf,1,buf_size,fp_stream); if(s_en_type == 0){ unsigned char tmp_iv[SUMA_KEY_AND_IV_LEN]={0}; unsigned int tmp_len=0; /*因為是讀取部分資料,是以需要記錄下一次解密需要使用的iv*/ tmp_len=read_len/SUMA_KEY_AND_IV_LEN*SUMA_KEY_AND_IV_LEN; memcpy(tmp_iv,buf+(tmp_len-SUMA_KEY_AND_IV_LEN),SUMA_KEY_AND_IV_LEN); /*解密*/ int processed_len = aescbc128_decrypt (buf, tmp_len, s_cp_key, s_cp_iv,buf); /*下一次解密的iv指派*/ memcpy(s_cp_iv,tmp_iv,SUMA_KEY_AND_IV_LEN); } return read_len; }else{ return -1; } } static int open_file(int type,char *input,char *output,AVFormatContext** ic,AVFormatContext** oc,int *video_index) { int ret=0; struct stat sta_buff; unsigned char *aviobuffer = NULL; AVIOContext *avio = NULL; AVOutputFormat* ofmt = NULL; AVStream* video_st = NULL; AVStream* audio_st = NULL; unsigned int i = 0; char szError[256] = {0}; /*判斷檔案是否存在*/ ret = stat(input, &sta_buff); if(ret != 0){ DvtLog(DVT_MSG_TYPE_ERROR,"未找到輸入檔案!"); return ret; } av_register_all(); if(type==0) { /*以流的方式打開需要加密的檔案*/ fp_stream=fopen(input,"rb"); if(fp_stream == NULL){ DvtLog(DVT_MSG_TYPE_ERROR,"打開輸入檔案失敗!"); ret=-1; goto end; } *ic = avformat_alloc_context(); if(ic == NULL){ DvtLog(DVT_MSG_TYPE_ERROR,"配置設定輸入檔案ic失敗!"); goto end; } aviobuffer=(unsigned char *)av_malloc(SUMA_READ_DATA_LEN); if(aviobuffer == NULL){ DvtLog(DVT_MSG_TYPE_ERROR,"配置設定緩存aviobuffer失敗!"); ret=-1; goto end; } avio =avio_alloc_context(aviobuffer, SUMA_READ_DATA_LEN,0,NULL,read_file,NULL,NULL); if(avio == NULL){ DvtLog(DVT_MSG_TYPE_ERROR,"配置設定avio失敗!"); ret=-1; goto end; } (*ic)->pb=avio; ret = avformat_open_input(ic,NULL,NULL,NULL); } else { ret = avformat_open_input(ic, input, NULL, NULL); } if(ret != 0){ av_strerror(ret, szError, 256); DvtLog(DVT_MSG_TYPE_ERROR,"ic avformat_open_input失敗:%s!",szError); goto end; } /*flags中有AVFMT_FLAG_KEEP_SIDE_DATA應該不會添加額外的14位元組的資料 0x8c4d9d108e25e9feULL*/ (*ic)->flags|=AVFMT_FLAG_KEEP_SIDE_DATA; if ((ret = avformat_find_stream_info(*ic, 0)) < 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Failed to retrieve input stream information!"); goto end; } av_dump_format(*ic, 0, input, 0); //輸出(Output) avformat_alloc_output_context2(oc, NULL, NULL, output); if (!*oc) { DvtLog(DVT_MSG_TYPE_ERROR,"Could not create output context!"); ret = AVERROR_UNKNOWN; goto end; } (*oc)->flags|=AVFMT_FLAG_KEEP_SIDE_DATA; ofmt = (*oc)->oformat; for (i = 0; i < (*ic)->nb_streams; i++) { //根據輸入流建立輸出流(Create output AVStream according to input AVStream) AVStream *in_stream = (*ic)->streams[i]; AVStream *out_stream = avformat_new_stream((*oc), in_stream->codec->codec); if (!out_stream) { DvtLog(DVT_MSG_TYPE_ERROR,"Failed allocating output stream!"); ret = AVERROR_UNKNOWN; goto end; } //複制AVCodecContext的設定(Copy the settings of AVCodecContext) ret = avcodec_copy_context(out_stream->codec, in_stream->codec); if (ret < 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Failed to copy context from input to output stream codec context!"); goto end; } /*拷貝codecpar ffmpeg後續會使用codecpar替換codec參數 需要給out_stream的codecpar指派*/ //memcpy(out_stream->codecpar,in_stream->codecpar,sizeof(AVCodecParameters)); ret = avcodec_parameters_copy(out_stream->codecpar,in_stream->codecpar); if (ret < 0) { DvtLog(DVT_MSG_TYPE_ERROR,"failed to avcodec_parameters_copy"); goto end; } out_stream->codec->codec_tag = 0; if ((*oc)->oformat->flags & AVFMT_GLOBALHEADER) out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER; if(in_stream->codec->codec_type==AVMEDIA_TYPE_VIDEO) *video_index = i; /*異常情況 音頻流的采樣率為0 強制設定這可能有問題*/ if((out_stream->codec->codec_type==AVMEDIA_TYPE_AUDIO)&&(out_stream->codecpar->sample_rate==0)) { out_stream->codecpar->sample_rate=48000; DvtLog(DVT_MSG_TYPE_WARNING,"audio out_stream->codecpar->sample_rate==0"); } AVCodecContext* input_codec_context = NULL; AVCodecContext* output_codec_context = NULL; input_codec_context = in_stream->codec; output_codec_context = out_stream->codec; if (av_q2d(input_codec_context->time_base) * input_codec_context->ticks_per_frame > av_q2d(in_stream->time_base) && av_q2d(in_stream->time_base) < 1.0 / 1000) { output_codec_context->time_base = input_codec_context->time_base; output_codec_context->time_base.num *= input_codec_context->ticks_per_frame; } else { output_codec_context->time_base = in_stream->time_base; } } //輸出一下格式------------------ av_dump_format(*oc, 0, output, 1); //打開輸出檔案(Open output file) if (!(ofmt->flags & AVFMT_NOFILE)) { ret = avio_open(&((*oc)->pb), output, AVIO_FLAG_WRITE); if (ret < 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Could not open output file '%s'",output); goto end; } } //寫檔案頭(Write file header) ret = avformat_write_header(*oc, NULL); if (ret < 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Error occurred when opening output file!"); goto end; } /*flags中有AVFMT_FLAG_KEEP_SIDE_DATA應該不會添加額外的14位元組的資料 0x8c4d9d108e25e9feULL*/ //(*ic)->flags|=AVFMT_FLAG_KEEP_SIDE_DATA; //(*oc)->flags|=AVFMT_FLAG_KEEP_SIDE_DATA; end: return ret; } static bool ParseH264Nal(unsigned char* pbyData,unsigned int data_len,unsigned int *nal_len,unsigned int* pbyUnitType,unsigned int *offset,unsigned int *en_len) { unsigned int wOffset = 0,i=0; //判斷是否是NAL單元 if ((0 == pbyData[0]) && (0 == pbyData[1])){ if (1 == pbyData[2]){ wOffset += 3; } else if ((0 == pbyData[2]) && (1 == pbyData[3])){ wOffset += 4; } else return false; } else return false; *pbyUnitType = 0x1f & pbyData[wOffset]; if(*pbyUnitType==5||*pbyUnitType==1){ //資料單元,海思平台輸入的資料中隻有一個有效的資料單元 *nal_len = data_len; if(*nal_len>wOffset+32) { wOffset += 32; *en_len = *nal_len - wOffset; *offset=wOffset; } else { *offset=0; *en_len=0; } } else{ //描述單元,逐位元組分析找到下一個單元的頭來确認目前單元長度 for(i=0;i
en_type; enable_encrypt_card = info->enable_encrypt_card; /*如果需要先全檔案解密 給cpck cpiv指派 在讀檔案時就解密*/ if(s_en_type==0){ memcpy(s_cp_key,info->de_key,SUMA_KEY_AND_IV_LEN); memcpy(s_cp_iv,info->de_iv,SUMA_KEY_AND_IV_LEN); } int video_index = -1; ret = open_file(0,info->in_path,info->out_path,&ic,&oc,&video_index); if(ret!=0) { DvtLog(DVT_MSG_TYPE_INFORMATION,"open_file failed!"); return ret; } packet=(AVPacket *)av_malloc(sizeof(AVPacket)); packet_dst=(AVPacket *)av_malloc(sizeof(AVPacket)); if((packet==NULL)||(packet_dst==NULL)) { DvtLog(DVT_MSG_TYPE_INFORMATION,"packet為NULL!"); ret=-1; goto end; } unsigned int frame_index=0; int64_t last_pts=0,last_dts=0,base_duration=0; /*拆包得到一幀資料,音頻資料不做處理,僅處理視訊資料*/ do{ decode_done = av_read_frame(ic, packet); if (decode_done < 0){ break; } /*視訊資料*/ if(packet->stream_index==video_index){ unsigned int tmp_packet_size=0; tmp_packet_size=packet->size; for(i=0;i
data+i; /*解析視訊資料應包含多個NAL,其中多個描述NAL和一個資料NAL*/ nal_status = ParseH264Nal(pbyINData,tmp_packet_size-i,&nal_len,&pbyUnitType,&offset,&en_len); if(nal_status){ /*資料NAL,且能夠加密*/ if((pbyUnitType==1||pbyUnitType==5)&&(en_len>=16)){ /*先解密*/ if(s_en_type==1){ decrypt_nal_data(info->de_key,info->de_iv,pbyINData+offset,en_len,&changed_len); if(en_len>changed_len) { packet->size=packet->size-(en_len-changed_len); tmp_packet_size=tmp_packet_size-(en_len-changed_len); en_len=changed_len; } }//s_en_type==1 encrypt_nal_data(info->en_key,info->en_iv,pbyINData+offset,en_len,&changed_len); if(changed_len>en_len){ /*建立新的packet*/ av_new_packet(packet_dst, packet->size+changed_len-en_len); /*拷貝packet*/ av_packet_copy_props(packet_dst, packet); memcpy(packet_dst->data,packet->data,packet_dst->size); ambiguity_add(packet_dst->data+i+offset,en_len); ambiguity_flag=1; } }//nal data i+=nal_len; }//nal else break; }//for packet #if 0 AVStream *in_stream, *out_stream; in_stream = ic->streams[packet->stream_index]; out_stream = oc->streams[packet->stream_index]; /* copy packet */ packet->pts = av_rescale_q_rnd(packet->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); packet->dts = av_rescale_q_rnd(packet->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX)); packet->duration = av_rescale_q(packet->duration, in_stream->time_base, out_stream->time_base); #endif }//video /*如果沒二義性處理直接寫packet 如果有的話寫packet_dst*/ if(ambiguity_flag==0) ret = av_interleaved_write_frame(oc, packet); else{ ret = av_interleaved_write_frame(oc, packet_dst); av_free_packet(packet_dst); ambiguity_flag=0; } if (ret < 0){ av_strerror(ret, szError, 256); DvtLog(DVT_MSG_TYPE_WARNING,"oc av_interleaved_write_frame失敗:%s!",szError); } else if (ret > 0) { DvtLog(DVT_MSG_TYPE_INFORMATION,"End of stream requested!"); av_free_packet(packet); break; } av_free_packet(packet); }while(!decode_done); av_write_trailer(oc); avio_close(oc->pb); avformat_free_context(oc); //avformat_close_input(&ic); end: if(packet!=NULL){ av_free(packet); } if(packet_dst!=NULL){ av_free(packet_dst); } if(ic!=NULL){ //avformat_free_context(ic); avformat_close_input(&ic); } if(fp_stream!=NULL){ fclose(fp_stream); } if(ret==0) DvtLog(DVT_MSG_TYPE_INFORMATION,"Suma_TSEncrypt OK!"); return ret; } int Suma_decrypt_file(char *in_path,char *out_path,unsigned char *key,unsigned char *iv,bool enable_hw) { AVFormatContext* ic = NULL; AVFormatContext* oc = NULL; unsigned int i = 0; int nRet =0; char szError[256] = {0}; static AVPacket *packet=NULL; int encrypt_flag=0; int video_index=-1; if((in_path==NULL)||(in_path==NULL)||(key==NULL)||(iv==NULL)) { DvtLog(DVT_MSG_TYPE_INFORMATION,"輸入參數指針為NULL!"); return -1; } /*是否啟動加密卡辨別*/ enable_encrypt_card=enable_hw; nRet = open_file(1,in_path,out_path,&ic,&oc,&video_index); if(nRet!=0) { DvtLog(DVT_MSG_TYPE_INFORMATION,"open_file failed!"); return -1; } packet=(AVPacket *)av_malloc(sizeof(AVPacket)); if(packet==NULL) { DvtLog(DVT_MSG_TYPE_INFORMATION,"packet為NULL!"); return -1; } int decode_done = 0; do { decode_done = av_read_frame(ic, packet); if (decode_done < 0) { Sleep(25); break; } /*隻對視訊處理*/ if(packet->stream_index==video_index){ long unsigned int dwOutLen=0; unsigned char* pbyINData=NULL; unsigned char* pbyOutData=NULL; unsigned int nal_len=0,k=0; bool nal_status=false; int nResult=-1; unsigned int pbyUnitType=0,offset=0,en_len=0,changed_len=0; for(i=0;i
size;) { pbyINData = (unsigned char*)packet->data+i; /*解析視訊資料應包含多個NAL,其中多個描述NAL和一個資料NAL*/ nal_status = ParseH264Nal(pbyINData,packet->size-i,&nal_len,&pbyUnitType,&offset,&en_len); if(nal_status) { /*資料NAL,且能夠加密*/ if((pbyUnitType==1||pbyUnitType==5)&&(en_len>=16)) { decrypt_nal_data(key,iv,pbyINData+offset,en_len,&changed_len); packet->size=packet->size-(en_len-changed_len); encrypt_flag=1; }// 1 5 encrypt i+=nal_len; }//nal else break; }//nal for }//video frame else{ } nRet = av_interleaved_write_frame(oc, packet); if (nRet < 0) { av_strerror(nRet, szError, 256); DvtLog(DVT_MSG_TYPE_WARNING,"oc av_interleaved_write_frame失敗:%s!",szError); } else if (nRet > 0) { DvtLog(DVT_MSG_TYPE_INFORMATION,"End of stream requested!"); av_free_packet(packet); break; } av_free_packet(packet); }while(!decode_done); av_write_trailer(oc); avio_close(oc->pb); avformat_free_context(oc); avformat_close_input(&ic); if(packet!=NULL) { av_free(packet); } DvtLog(DVT_MSG_TYPE_INFORMATION,"Suma_decrypt_file OK!"); return 0; } static char cmd[SUMA_CMD_LEN]={0}; int Suma_TStoHLS(TStoHLSInfo *info){ struct stat sta_buff; int ret=0; char *tmp=NULL; char input_filename[SUMA_PATH_LEN]={0}; char drive[3]={0}; if(info==NULL) return -1; ret = stat(info->in_path, &sta_buff); if(ret != 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Suma_TStoHLS輸入檔案不存在!"); return ret; } int size = strlen(info->in_path); for(int i=0; i
in_path[i] == '\\') info->in_path[i] = '/'; } /*擷取輸入檔案名*/ tmp = strrchr(info->in_path,'/'); if(tmp==NULL) { DvtLog(DVT_MSG_TYPE_ERROR,"Suma_TStoHLS輸入檔案不是絕對路徑!"); return -1; } memcpy(input_filename,tmp+1,strlen(tmp)-1); tmp = strrchr(input_filename,'.'); if(tmp!=NULL) { memset(input_filename+(strlen(input_filename)-strlen(tmp)),0,strlen(tmp)); } ret = stat(info->tools_path, &sta_buff); if(ret != 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Suma_TStoHLS切片工具不存在!"); return ret; } /*如果輸出目錄不存在則建立輸出目錄*/ ret = stat(info->out_path, &sta_buff); if(ret != 0) { memset(cmd,0,SUMA_CMD_LEN); sprintf(cmd,"mkdir \"%s\"",info->out_path); ret = system(cmd); if(ret != 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Suma_TStoHLS建立目錄失敗!"); return ret; } } /*擷取輸出目錄的盤符*/ memcpy(drive,info->out_path,2); /*先進入輸出目錄所在盤 再進入輸出目錄 建立對應輸入檔案名的目錄 進入新建立的目錄 開始切片*/ memset(cmd,0,SUMA_CMD_LEN); sprintf(cmd,"%s & cd %s & %s -i %s -y -vcodec copy -acodec copy -map 0 -f segment -segment_list %s.m3u8 -segment_time %d %s-%s.ts", drive,info->out_path,info->tools_path,info->in_path,input_filename,info->duration,input_filename,"%05d"); ret = system(cmd); if(ret != 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Suma_TStoHLS切片失敗!"); return ret; } /*m3u8檔案處理*/ FILE *fp_m3u8=NULL; char m3u8_filename[SUMA_PATH_LEN]={0}; char ext_x_key[SUMA_CMD_LEN]={0}; char strLine[1024]={0}; unsigned int seek_len=0; sprintf(m3u8_filename,"%s/%s.m3u8",info->out_path,input_filename); ret = stat(m3u8_filename, &sta_buff); if(ret != 0) { DvtLog(DVT_MSG_TYPE_ERROR,"Suma_TStoHLS m3u8檔案不存在!"); return ret; } fp_m3u8 = fopen(m3u8_filename,"rb"); if(fp_m3u8==NULL) { DvtLog(DVT_MSG_TYPE_ERROR,"Suma_TStoHLS打開m3u8檔案失敗!"); return ret; } while (!feof(fp_m3u8)) //循環讀取每一行,直到檔案尾 { fgets(strLine,1024,fp_m3u8); //将fp所指向的檔案一行内容讀到strLine緩沖區 seek_len+=strlen(strLine); ret =memcmp(strLine,"#EXT-X-TARGETDURATION",strlen("#EXT-X-TARGETDURATION")); if(ret==0) break; //DO SOMETHING ELSE } fseek(fp_m3u8,0,SEEK_SET); char *tmp_buf = (char *)malloc(sta_buff.st_size); fread(tmp_buf,1,sta_buff.st_size,fp_m3u8); fclose(fp_m3u8); fp_m3u8 = fopen(m3u8_filename,"wb+"); fwrite(tmp_buf,1,seek_len,fp_m3u8); sprintf(ext_x_key,"#EXT-X-KEY:METHOD=SAMPLE-AES,VDECFORMAT=\"h264\",KEYFORMATVERSIONS=\"1\",KEYFORMAT=\"shctdrm\",IV=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x,URI=\"%s?KeyID=%s\"\n", info->iv[0],info->iv[1],info->iv[2],info->iv[3], info->iv[4],info->iv[5],info->iv[6],info->iv[7], info->iv[8],info->iv[9],info->iv[10],info->iv[11], info->iv[12],info->iv[13],info->iv[14],info->iv[15], info->drmserver_url,info->keyid); fwrite(ext_x_key,1,strlen(ext_x_key),fp_m3u8); fwrite(tmp_buf+seek_len,1,sta_buff.st_size-seek_len,fp_m3u8); fclose(fp_m3u8); if(tmp_buf!=NULL) free(tmp_buf); DvtLog(DVT_MSG_TYPE_INFORMATION,"Suma_TStoHLS OK!"); return ret; } #endif } #ifdef __cplusplus } #endif
因為需要對源檔案進行保護,是以源檔案是進行了全檔案的sample-aes加密處理的,在做NAL加密之前需要先對整個檔案進行解密,是以在讀檔案時使用流的方式,讀到資料先解密,讀到packet之後再進行NAL加密,加密之後要進行防二義性的處理,進行這個處理之後資料可能會變大,需重新配置設定一個packet來儲存資料。
對NAL加密之後的TS檔案,有需求使用新的密鑰重新加密,接口中也添加對這個需求的處理。