首先致敬雷神提供的資源,使用雷神的代碼實現視頻剪切功能
雷神實現的ffmpeg代碼文章
說明一下,這里轉載首先是記錄一下實現方法,其次就是解決代碼無法正常運行問題(avformat_write_header返回-22)
本文介紹一個基于FFMPEG的封裝格式轉換器。所謂的封裝格式轉換,就是在AVI,FLV,MKV,MP4這些格式之間轉換(對應.avi,.flv,.mkv,.mp4文件)。需要注意的是,本程序并不進行視音頻的編碼和解碼工作。而是直接將視音頻壓縮碼流從一種封裝格式文件中獲取出來然后打包成另外一種封裝格式的文件。傳統的轉碼程序工作原理如下圖所示:
上圖例舉了一個舉例:FLV(視頻:H.264,音頻:AAC)轉碼為AVI(視頻:MPEG2,音頻MP3)的例子。可見視頻轉碼的過程通俗地講相當于把視頻和音頻重新“錄”了一遍。
本程序的工作原理如下圖所示:
由圖可見,本程序并不進行視頻和音頻的編解碼工作,因此本程序和普通的轉碼軟件相比,有以下兩個特點:
處理速度極快。視音頻編解碼算法十分復雜,占據了轉碼的絕大部分時間。因為不需要進行視音頻的編碼和解碼,所以節(jié)約了大量的時間。
視音頻質量無損。因為不需要進行視音頻的編碼和解碼,所以不會有視音頻的壓縮損傷。
#include <stdio.h>
extern "C"
{
#include <FFmpeg\libavcodec\avcodec.h>
#include <FFmpeg\libavformat\avformat.h>
}
#pragma comment (lib, "avcodec.lib")
#pragma comment (lib, "avformat.lib")
#pragma comment (lib, "avutil.lib")
int main(int argc, char* argv[])
{
int startTime = 1;//起始時間 如果為0就不啟用該變量
int endTime = 4;//結束時間 如果為0就不啟用該變量
AVCodecContext* pCodecCtx = NULL;
AVCodecContext *pAVCodecContext = NULL;
AVOutputFormat *ofmt = NULL;
AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
AVPacket pkt;
const char *in_filename, *out_filename;
int ret, i;
in_filename = "e://Test.mp4";
out_filename = "e://Temp.mp4";
av_register_all();
//輸入(Input)
if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
printf("Could not open input file.");
goto end;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
printf("Failed to retrieve input stream information");
goto end;
}
av_dump_format(ifmt_ctx, 0, in_filename, 0);
//輸出(Output)
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
printf("Could not create output context\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ofmt = ofmt_ctx->oformat;
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
//根據輸入流創(chuàng)建輸出流(Create output AVStream according to input AVStream)
AVStream *in_stream = ifmt_ctx->streams[i];
AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
if (!out_stream) {
printf("Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
if (ret < 0)
{
printf("Failed to copy context from input to output stream codec context\n");
goto end;
}
out_stream->codec->codec_tag = 0;
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
//Param that must set
pCodecCtx = out_stream->codec;
/*
//特殊處理
如果使用雷神的代碼這里不增加特殊的處理,會在avformat_write_header函數執(zhí)行的時候
返回-22
i=0: codec_id = AV_CODEC_ID_HEV //H265
i=1:codec_id = AV_CODEC_ID_AAC
i=2:codec_id = AV_CODEC_ID_NONE
i=3:codec_id = AV_CODEC_ID_NONE
i=4:codec_id = AV_CODEC_ID_NONE
*/
pAVCodecContext = ifmt_ctx->streams[i]->codec;
if (i < 2)
pCodecCtx->codec_id = ifmt_ctx->streams[i]->codec->codec_id;
else
pCodecCtx->codec_id = ifmt_ctx->streams[i%2]->codec->codec_id;
#if 0
//其他參數配置
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx->width = 1920;
pCodecCtx->height = 1080;
pCodecCtx->bit_rate = pAVCodecContext->bit_rate;
pCodecCtx->gop_size = 250;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
//Optional Param
pCodecCtx->max_b_frames = 3;
#endif
}
pAVCodecContext = ifmt_ctx->streams[0]->codec;
// 顯示視頻相關的參數信息(編碼上下文)
printf("比特率: = %lld\n", pAVCodecContext->bit_rate);
printf("寬高: = %d\n" , pAVCodecContext->width );
printf("格式: = %d\n" , pAVCodecContext->pix_fmt); // AV_PIX_FMT_YUV420P 0
printf("幀率: = %d\n",pAVCodecContext->time_base.den );
printf("總時長: = %f s\n", (ifmt_ctx->duration) / AV_TIME_BASE );
printf("總幀數: = %d\n", ifmt_ctx->streams[0]->nb_frames );
int fps = ifmt_ctx->streams[0]->avg_frame_rate.num * 1.0f / ifmt_ctx->streams[0]->avg_frame_rate.den;
int interval = 1 * 1000 / fps;
printf("平均幀率: = %d\n", fps );
printf("幀間隔: = %d ms\n", interval);
//輸出一下格式------------------
av_dump_format(ofmt_ctx, 0, out_filename, 1);
//打開輸出文件(Open output file)
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
printf("Could not open output file '%s'", out_filename);
goto end;
}
}
//寫文件頭(Write file header)
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
printf("Error occurred when opening output file\n");
goto end;
}
//跳轉到多少秒
if(startTime <= 0)
{
ret = av_seek_frame(ifmt_ctx, -1, startTime * AV_TIME_BASE, AVSEEK_FLAG_ANY);
if (ret<0) {
fprintf(stderr, "Error seek\n");
}
}
int frame_index = 0;
while (1) {
AVStream *in_stream, *out_stream;
//獲取一個AVPacket(Get an AVPacket)
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0)
break;
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
//裁剪區(qū)域
if (endTime > startTime)
{
if (av_q2d(in_stream->time_base) * pkt.pts > (startTime + endTime))
{
av_packet_unref(&pkt);
break;
}
}
/* copy packet */
//轉換PTS/DTS(Convert PTS/DTS)
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
//寫入(Write)
ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
printf("Error muxing packet\n");
break;
}
printf("Write %8d frames to output file\n", frame_index);
av_free_packet(&pkt);
frame_index++;
}
//寫文件尾(Write file trailer)
av_write_trailer(ofmt_ctx);
end:
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
printf("Error occurred.\n");
return -1;
}
return 0;
}
轉:文章資源:https://blog.csdn.net/leixiaohua1020/article/details/84597944文章來源:http://www.zghlxwxcb.cn/news/detail-406531.html
完整工程下載地址(代碼是一樣的,就是包含了ffmpeg運行所需要的環(huán)境,能直接編譯通過):
https://download.csdn.net/download/qq_36351159/85748703
工程項目VS2015 x64 Debug下載 (包括ffmpeg庫,配置好的所需包含目錄、庫,可直接編譯)文章來源地址http://www.zghlxwxcb.cn/news/detail-406531.html
到了這里,關于使用FFMpeg實現視頻剪切功能的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!