亚欧色一区w666天堂,色情一区二区三区免费看,少妇特黄A片一区二区三区,亚洲人成网站999久久久综合,国产av熟女一区二区三区

  • 發布文章
  • 消息中心
點贊
收藏
評論
分享
原創

基于ffmpeg的視頻文件拼接技術

2025-09-26 10:18:02
1
0

一、技術背景與需求分析

 
在視頻處理場景中,為MP4文件添加前后貼內容(如開場動畫、片尾字幕)是常見需求。FFmpeg作為開源多媒體框架,其API支持精確控制視頻流的時間軸操作。本文將以添加1秒前貼(黑屏靜音)和3秒后貼為例,演示如何通過FFmpeg API實現無損拼接。
 
 

二、技術實現步驟

 

1. 環境準備

 
FFmpeg庫配置:需鏈接libavformat(封裝)、libavcodec(編解碼)及libavutil(工具庫)。
 
輸入輸出定義:假設輸入文件為input.mp4,輸出為output.mp4,前貼為1秒黑屏(header.mp4),后貼為3秒黑屏(tailer.mp4)。
 

2. 核心代碼流程

 
int main() {
    const char* inputs[] = {"header.mp4", "content.mp4", "tailer.mp4"};
    const char* output = "merged.mp4";
    
    AVOutputFormat* ofmt = NULL;
    AVFormatContext* ifmt_ctx[3] = {NULL};
    AVFormatContext* ofmt_ctx = NULL;
    AVPacket pkt;
    int ret, i;
 
    // 初始化輸出上下文
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, output);
    if (!ofmt_ctx) return -1;
    ofmt = ofmt_ctx->oformat;
 
    // 遍歷輸入文件
    for (i = 0; i < 3; i++) {
        if ((ret = avformat_open_input(&ifmt_ctx[i], inputs[i], NULL, NULL)) < 0) {
            fprintf(stderr, "Could not open input file %s\n", inputs[i]);
            goto end;
        }
        if ((ret = avformat_find_stream_info(ifmt_ctx[i], NULL)) < 0) {
            fprintf(stderr, "Failed to retrieve stream info\n");
            goto end;
        }
    }
 
    // 由于三分文件編碼參數一致,復制任何一個流信息到輸出文件即可,這里選擇第一個文件
for (unsigned int j = 0; j < ifmt_ctx[0]->nb_streams; j++) {
AVStream* in_stream = ifmt_ctx[0]->streams[j];
AVStream* out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream) {
ret = AVERROR(ENOMEM);
goto end;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy codec parameters\n");
goto end;
}
out_stream->codecpar->codec_tag = 0;
}
 
 
    // 打開輸出文件
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, output, AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file %s\n", output);
            goto end;
        }
    }
 
    // 寫入文件頭
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error writing header\n");
        goto end;
    }
 
    // 合并數據包
    int video_dts_offset = 0;
    int audio_dts_offset = 0;
    int last_packet_stream_index = 0;
    for (i = 0; i < 3; i++) {
        while (1) {
            ret = av_read_frame(ifmt_ctx[i], &pkt);
            if (ret < 0) break;
            
            AVStream* in_stream = ifmt_ctx[i]->streams[pkt.stream_index];
            AVStream* out_stream = ofmt_ctx->streams[pkt.stream_index];
            
            pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
            pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
            pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
 
            last_packet_stream_index = pkt.stream_index;
            if (is_video_packet){
                 // 視頻
                 pkt.pts += video_dts_offset;
                 pkt.dts += video_dts_offset;
            } else {
                  // 音頻
                  pkt.pts += video_dts_offset;
                 pkt.dts += video_dts_offset;
             }
            pkt.pos = -1;
            
            ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
            if (ret < 0) {
                fprintf(stderr, "Error writing packet\n");
                break;
            }
            av_packet_unref(&pkt);
        }
 
        // 偽代碼,根據最后一幀的類型,分別計算音頻幀和視頻幀的偏移。
        if (last_packet_stream_index == video_index){
           audio_dts_offset =  
last_packet_dts * av_q2d(v_stream->time_base) / av_q2d(a_stream->time_base);
           video_dts_offset += last_packet_dts;
        else {
             audio_dts_offset  += last_packet_dts;
             video_dts_offset = 
last_packet_dts * av_q2d(a_stream->time_base) / av_q2d(v_stream->time_base);
         }
    }
 
    // 寫入文件尾
    av_write_trailer(ofmt_ctx);
 
end:
    // 清理資源
    for (i = 0; i < 3; i++) {
        if (ifmt_ctx[i]) avformat_close_input(&ifmt_ctx[i]);
    }
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
    return ret < 0 ? 1 : 0;
}
 

3. 關鍵參數說明

 
時間戳控制:通過計算上一個文件的時間戳偏移, 應用到下一個視頻的時間戳上,保證時間戳的連續性。
 
編碼兼容性:輸出流需與原視頻編碼格式(如H.264)一致,避免轉碼損失。
 
 

三、性能優化與注意事項

 
內存管理:使用av_packet_unref釋放未使用的AVPacket,防止內存泄漏。
 
錯誤處理:檢查avformat_write_header返回值,確保文件寫入成功。
 
跨平臺適配:Windows需注意路徑分隔符,Linux需處理文件權限。
 
 
 

四、擴展應用

 
動態貼片:替換黑屏文件為自定義視頻(如Logo動畫),需調整分辨率與幀率匹配。
音頻同步:若需添加音頻貼片,需創建獨立的AVStream并設置采樣率。
0條評論
0 / 1000
g****n
5文章數
0粉絲數
g****n
5 文章 | 0 粉絲
原創

基于ffmpeg的視頻文件拼接技術

2025-09-26 10:18:02
1
0

一、技術背景與需求分析

 
在視頻處理場景中,為MP4文件添加前后貼內容(如開場動畫、片尾字幕)是常見需求。FFmpeg作為開源多媒體框架,其API支持精確控制視頻流的時間軸操作。本文將以添加1秒前貼(黑屏靜音)和3秒后貼為例,演示如何通過FFmpeg API實現無損拼接。
 
 

二、技術實現步驟

 

1. 環境準備

 
FFmpeg庫配置:需鏈接libavformat(封裝)、libavcodec(編解碼)及libavutil(工具庫)。
 
輸入輸出定義:假設輸入文件為input.mp4,輸出為output.mp4,前貼為1秒黑屏(header.mp4),后貼為3秒黑屏(tailer.mp4)。
 

2. 核心代碼流程

 
int main() {
    const char* inputs[] = {"header.mp4", "content.mp4", "tailer.mp4"};
    const char* output = "merged.mp4";
    
    AVOutputFormat* ofmt = NULL;
    AVFormatContext* ifmt_ctx[3] = {NULL};
    AVFormatContext* ofmt_ctx = NULL;
    AVPacket pkt;
    int ret, i;
 
    // 初始化輸出上下文
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, output);
    if (!ofmt_ctx) return -1;
    ofmt = ofmt_ctx->oformat;
 
    // 遍歷輸入文件
    for (i = 0; i < 3; i++) {
        if ((ret = avformat_open_input(&ifmt_ctx[i], inputs[i], NULL, NULL)) < 0) {
            fprintf(stderr, "Could not open input file %s\n", inputs[i]);
            goto end;
        }
        if ((ret = avformat_find_stream_info(ifmt_ctx[i], NULL)) < 0) {
            fprintf(stderr, "Failed to retrieve stream info\n");
            goto end;
        }
    }
 
    // 由于三分文件編碼參數一致,復制任何一個流信息到輸出文件即可,這里選擇第一個文件
for (unsigned int j = 0; j < ifmt_ctx[0]->nb_streams; j++) {
AVStream* in_stream = ifmt_ctx[0]->streams[j];
AVStream* out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream) {
ret = AVERROR(ENOMEM);
goto end;
}
ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
if (ret < 0) {
fprintf(stderr, "Failed to copy codec parameters\n");
goto end;
}
out_stream->codecpar->codec_tag = 0;
}
 
 
    // 打開輸出文件
    if (!(ofmt->flags & AVFMT_NOFILE)) {
        ret = avio_open(&ofmt_ctx->pb, output, AVIO_FLAG_WRITE);
        if (ret < 0) {
            fprintf(stderr, "Could not open output file %s\n", output);
            goto end;
        }
    }
 
    // 寫入文件頭
    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error writing header\n");
        goto end;
    }
 
    // 合并數據包
    int video_dts_offset = 0;
    int audio_dts_offset = 0;
    int last_packet_stream_index = 0;
    for (i = 0; i < 3; i++) {
        while (1) {
            ret = av_read_frame(ifmt_ctx[i], &pkt);
            if (ret < 0) break;
            
            AVStream* in_stream = ifmt_ctx[i]->streams[pkt.stream_index];
            AVStream* out_stream = ofmt_ctx->streams[pkt.stream_index];
            
            pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
            pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF);
            pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
 
            last_packet_stream_index = pkt.stream_index;
            if (is_video_packet){
                 // 視頻
                 pkt.pts += video_dts_offset;
                 pkt.dts += video_dts_offset;
            } else {
                  // 音頻
                  pkt.pts += video_dts_offset;
                 pkt.dts += video_dts_offset;
             }
            pkt.pos = -1;
            
            ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
            if (ret < 0) {
                fprintf(stderr, "Error writing packet\n");
                break;
            }
            av_packet_unref(&pkt);
        }
 
        // 偽代碼,根據最后一幀的類型,分別計算音頻幀和視頻幀的偏移。
        if (last_packet_stream_index == video_index){
           audio_dts_offset =  
last_packet_dts * av_q2d(v_stream->time_base) / av_q2d(a_stream->time_base);
           video_dts_offset += last_packet_dts;
        else {
             audio_dts_offset  += last_packet_dts;
             video_dts_offset = 
last_packet_dts * av_q2d(a_stream->time_base) / av_q2d(v_stream->time_base);
         }
    }
 
    // 寫入文件尾
    av_write_trailer(ofmt_ctx);
 
end:
    // 清理資源
    for (i = 0; i < 3; i++) {
        if (ifmt_ctx[i]) avformat_close_input(&ifmt_ctx[i]);
    }
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE)) avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
    return ret < 0 ? 1 : 0;
}
 

3. 關鍵參數說明

 
時間戳控制:通過計算上一個文件的時間戳偏移, 應用到下一個視頻的時間戳上,保證時間戳的連續性。
 
編碼兼容性:輸出流需與原視頻編碼格式(如H.264)一致,避免轉碼損失。
 
 

三、性能優化與注意事項

 
內存管理:使用av_packet_unref釋放未使用的AVPacket,防止內存泄漏。
 
錯誤處理:檢查avformat_write_header返回值,確保文件寫入成功。
 
跨平臺適配:Windows需注意路徑分隔符,Linux需處理文件權限。
 
 
 

四、擴展應用

 
動態貼片:替換黑屏文件為自定義視頻(如Logo動畫),需調整分辨率與幀率匹配。
音頻同步:若需添加音頻貼片,需創建獨立的AVStream并設置采樣率。
文章來自個人專欄
文章 | 訂閱
0條評論
0 / 1000
請輸入你的評論
0
0