我开发了以下代码:
extern "C"
{
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfiltergraph.h>
#include <libswscale/swscale.h>
}
#include <stdio.h>
static AVFormatContext *fmt_ctx = NULL;
static int frame_index = 0;
static int j = 0, nbytes=0;
uint8_t *video_outbuf = NULL;
static AVPacket *pAVPacket=NULL;
static int value=0;
static AVFrame *pAVFrame=NULL;
static AVFrame *outFrame=NULL;
static AVStream *video_st=NULL;
static AVFormatContext *outAVFormatContext=NULL;
static AVCodec *outAVCodec=NULL;
static AVOutputFormat *output_format=NULL;
static AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx;
static AVCodecContext *outAVCodecContext=NULL;
static int width, height;
static enum AVPixelFormat pix_fmt;
static AVStream *video_stream = NULL, *audio_stream = NULL;
static const char *src_filename = NULL;
static const char *video_dst_filename = NULL;
static const char *audio_dst_filename = NULL;
static FILE *video_dst_file = NULL;
static FILE *audio_dst_file = NULL;
static uint8_t *video_dst_data[4] = {NULL};
static int video_dst_linesize[4];
static int video_dst_bufsize;
static int video_stream_idx = -1, audio_stream_idx = -1;
static AVPacket *pkt=NULL;
static AVPacket *pkt1=NULL;
static AVFrame *frame = NULL;
//static AVPacket pkt;
static int video_frame_count = 0;
static int audio_frame_count = 0;
static int refcount = 0;
AVCodec *codec;
static struct SwsContext *sws_ctx;
AVCodecContext *c= NULL;
int i, out_size, size, x, y, outbuf_size;
AVFrame *picture;
uint8_t *outbuf, *picture_buf;
int video_outbuf_size;
int w, h;
AVPixelFormat pixFmt;
uint8_t *data[4];
int linesize[4];
static int open_codec_context(int *stream_idx,
AVCodecContext **dec_ctx, AVFormatContext
*fmt_ctx, enum AVMediaType type)
{
int ret, stream_index;
AVStream *st;
AVCodec *dec = NULL;
AVDictionary *opts = NULL;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
if (ret < 0) {
printf("Could not find %s stream in input file '%s'\n",
av_get_media_type_string(type), src_filename);
return ret;
} else {
stream_index = ret;
st = fmt_ctx->streams[stream_index];
/* find decoder for the stream */
dec = avcodec_find_decoder(st->codecpar->codec_id);
if (!dec) {
printf("Failed to find %s codec\n",
av_get_media_type_string(type));
return AVERROR(EINVAL);
}
/* Allocate a codec context for the decoder */
*dec_ctx = avcodec_alloc_context3(dec);
if (!*dec_ctx) {
printf("Failed to allocate the %s codec context\n",
av_get_media_type_string(type));
return AVERROR(ENOMEM);
}
/* Copy codec parameters from input stream to output codec context */
if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
printf("Failed to copy %s codec parameters to decoder context\n",
av_get_media_type_string(type));
return ret;
}
/* Init the decoders, with or without reference counting */
av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);
if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
printf("Failed to open %s codec\n",
av_get_media_type_string(type));
return ret;
}
*stream_idx = stream_index;
}
return 0;
}
int main (int argc, char **argv)
{
int ret = 0, got_frame;
src_filename = argv[1];
video_dst_filename = argv[2];
audio_dst_filename = argv[3];
av_register_all();
avcodec_register_all();
printf("Registered all\n");
/* open input file, and allocate format context */
if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
printf("Could not open source file %s\n", src_filename);
exit(1);
}
/* retrieve stream information */
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
printf("Could not find stream information\n");
exit(1);
}
if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx,
AVMEDIA_TYPE_VIDEO) >= 0) {
video_stream = fmt_ctx->streams[video_stream_idx];
avformat_alloc_output_context2(&outAVFormatContext, NULL, NULL,
video_dst_filename);
if (!outAVFormatContext)
{
printf("\n\nError : avformat_alloc_output_context2()");
return -1;
}
}
if (open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx,
AVMEDIA_TYPE_AUDIO) >= 0) {
audio_stream = fmt_ctx->streams[audio_stream_idx];
audio_dst_file = fopen(audio_dst_filename, "wb");
if (!audio_dst_file) {
printf("Could not open destination file %s\n", audio_dst_filename);
ret = 1;
goto end;
}
}
/* dump input information to stderr */
av_dump_format(fmt_ctx, 0, src_filename, 0);
if (!audio_stream && !video_stream) {
printf("Could not find audio or video stream in the input, aborting\n");
ret = 1;
goto end;
}
output_format = av_guess_format(NULL, video_dst_filename, NULL);
if( !output_format )
{
printf("\n\nError : av_guess_format()");
return -1;
}
video_st = avformat_new_stream(outAVFormatContext ,NULL);
if( !video_st )
{
printf("\n\nError : avformat_new_stream()");
return -1;
}
outAVCodecContext = avcodec_alloc_context3(outAVCodec);
if( !outAVCodecContext )
{
printf("\n\nError : avcodec_alloc_context3()");
return -1;
}
outAVCodecContext = video_st->codec;
outAVCodecContext->codec_id = AV_CODEC_ID_MPEG4;// AV_CODEC_ID_MPEG4; //
AV_CODEC_ID_H264 // AV_CODEC_ID_MPEG1VIDEO
outAVCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
outAVCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
outAVCodecContext->bit_rate = 400000; // 2500000
outAVCodecContext->width = 1920;
//outAVCodecContext->width = 500;
outAVCodecContext->height = 1080;
//outAVCodecContext->height = 500;
outAVCodecContext->gop_size = 3;
outAVCodecContext->max_b_frames = 2;
outAVCodecContext->time_base.num = 1;
outAVCodecContext->time_base.den = 30; // 15fps
if (outAVCodecContext->codec_id == AV_CODEC_ID_H264)
{
av_opt_set(outAVCodecContext->priv_data, "preset", "slow", 0);
}
outAVCodec = avcodec_find_encoder(AV_CODEC_ID_MPEG4);
if( !outAVCodec )
{
printf("\n\nError : avcodec_find_encoder()");
return -1;
}
/* Some container formats (like MP4) require global headers to be
present
Mark the encoder so that it behaves accordingly. */
if ( outAVFormatContext->oformat->flags & AVFMT_GLOBALHEADER)
{
outAVCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
value = avcodec_open2(outAVCodecContext, outAVCodec, NULL);
if( value < 0)
{
printf("\n\nError : avcodec_open2()");
return -1;
}
/* create empty video file */
if ( !(outAVFormatContext->flags & AVFMT_NOFILE) )
{
if( avio_open2(&outAVFormatContext->pb , video_dst_filename,
AVIO_FLAG_WRITE ,NULL, NULL) < 0 )
{
printf("\n\nError : avio_open2()");
}
}
if(!outAVFormatContext->nb_streams)
{
printf("\n\nError : Output file dose not contain any stream");
return -1;
}
/* imp: mp4 container or some advanced container file required header
information*/
value = avformat_write_header(outAVFormatContext , NULL);
if(value < 0)
{
printf("\n\nError : avformat_write_header()");
return -1;
}
printf("\n\nOutput file information :\n\n");
av_dump_format(outAVFormatContext , 0 ,video_dst_filename ,1);
int flag;
int frameFinished;
value = 0;
pAVPacket = (AVPacket *)av_malloc(sizeof(AVPacket));
av_init_packet(pAVPacket);
pAVFrame = av_frame_alloc();
if( !pAVFrame )
{
printf("\n\nError : av_frame_alloc()");
return -1;
}
outFrame = av_frame_alloc();//Allocate an AVFrame and set its fields to
default values.
if( !outFrame )
{
printf("\n\nError : av_frame_alloc()");
return -1;
}
nbytes = av_image_get_buffer_size(outAVCodecContext-
>pix_fmt,outAVCodecContext->width,outAVCodecContext->height,32);
video_outbuf = (uint8_t*)av_malloc(nbytes);
if( video_outbuf == NULL )
{
printf("\n\nError : av_malloc()");
}
value = av_image_fill_arrays( outFrame->data, outFrame->linesize,
video_outbuf , AV_PIX_FMT_YUV420P, outAVCodecContext-
>width,outAVCodecContext->height,1 ); // returns : the size in bytes
required for src
if(value < 0)
{
printf("\n\nError : av_image_fill_arrays()");
}
SwsContext* swsCtx_ ;
// Allocate and return swsContext.
// a pointer to an allocated context, or NULL in case of error
// Deprecated : Use sws_getCachedContext() instead.
swsCtx_ = sws_getContext(video_dec_ctx->width,
video_dec_ctx->height,
video_dec_ctx->pix_fmt,
video_dec_ctx->width,
video_dec_ctx->height,
video_dec_ctx->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
AVPacket outPacket;
int got_picture;
while( av_read_frame( fmt_ctx , pAVPacket ) >= 0 )
{
if(pAVPacket->stream_index == video_stream_idx)
{
value = avcodec_decode_video2(video_dec_ctx , pAVFrame ,
&frameFinished , pAVPacket );
if( value < 0)
{
printf("Error : avcodec_decode_video2()");
}
if(frameFinished)// Frame successfully decoded :)
{
sws_scale(swsCtx_, pAVFrame->data, pAVFrame-
>linesize,0, video_dec_ctx->height, outFrame->data,outFrame->linesize);
// sws_scale(swsCtx_, pAVFrame->data, pAVFrame-
>linesize,0, video_dec_ctx->height, outFrame->data,outFrame->linesize);
av_init_packet(&outPacket);
outPacket.data = NULL; // packet data will be
allocated by the encoder
outPacket.size = 0;
avcodec_encode_video2(outAVCodecContext ,
&outPacket ,outFrame , &got_picture);
if(got_picture)
{
if(outPacket.pts != AV_NOPTS_VALUE)
outPacket.pts =
av_rescale_q(outPacket.pts, video_st->codec->time_base, video_st-
>time_base);
if(outPacket.dts != AV_NOPTS_VALUE)
outPacket.dts =
av_rescale_q(outPacket.dts, video_st->codec->time_base, video_st-
>time_base);
printf("Write frame %3d (size= %2d)\n",
j++, outPacket.size/1000);
if(av_write_frame(outAVFormatContext ,
&outPacket) != 0)
{
printf("\n\nError :
av_write_frame()");
}
av_packet_unref(&outPacket);
} // got_picture
av_packet_unref(&outPacket);
} // frameFinished
}
}// End of while-loop
value = av_write_trailer(outAVFormatContext);
if( value < 0)
{
printf("\n\nError : av_write_trailer()");
}
//THIS WAS ADDED LATER
av_free(video_outbuf);
end:
avcodec_free_context(&video_dec_ctx);
avcodec_free_context(&audio_dec_ctx);
avformat_close_input(&fmt_ctx);
if (video_dst_file)
fclose(video_dst_file);
if (audio_dst_file)
fclose(audio_dst_file);
//av_frame_free(&frame);
av_free(video_dst_data[0]);
return ret < 0;
}
上述代码的问题在于它将视频向左旋转了90度。
Snapshot of video given as input to above program
Snapshot of output video. It is rotated by 90 degree to left.
我使用以下命令编译程序:
g++ -D__STDC_CONSTANT_MACROS -Wall -g ScreenRecorder.cpp -I/home/harry/Documents/compressor/ffmpeg-3.3/ -I/root/android-ndk-r14b/platforms/android-21/arch-x86_64/usr/include/ -c -o ScreenRecorder.o -w
使用以下命令链接它:
g++ -Wall -g ScreenRecorder.o -I/home/harry/Documents/compressor/ffmpeg-3.3/ -I/root/android-ndk-r14b/platforms/android-21/arch-x86_64/usr/include/ -L/usr/lib64 -L/lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.4.7/ -L/home/harry/Documents/compressor/ffmpeg-3.3/ffmpeg-build -L/root/android-ndk-r14b/platforms/android-21/arch-x86_64/usr/lib64 -o ScreenRecorder.exe -lavformat -lavcodec -lavutil -lavdevice -lavfilter -lswscale -lx264 -lswresample -lm -lpthread -ldl -lstdc++ -lc -lrt
正在使用以下命令运行程序:
./ScreenRecorder.exe vertical.MOV videoH.mp4 audioH.mp3
注意: - 源视频来自iphone,格式为.mov。 - 输出视频存储在.mp4文件中。
任何人都可以告诉我为什么它会将视频旋转90度?
我在转储中注意到的一件事如下所示:
Duration: 00:00:06.04, start: 0.000000, bitrate: 17087 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1920x1080, 17014 kb/s, 29.98 fps, 29.97 tbr, 600 tbn, 1200 tbc (default)
Metadata:
rotate : 90
creation_time : 2017-07-09T10:56:42.000000Z
handler_name : Core Media Data Handler
encoder : H.264
Side data:
displaymatrix: rotation of -90.00 degrees
它说displaymatrix: rotation of -90.00 degrees
。是否负责将视频旋转90度?
答案 0 :(得分:1)
我通过添加:
将输入流的元数据指定为输出流的元数据来解决它outAVFormatContext->metadata = fmt_ctx->metadata
outAVFormatContext->streams[video_stream_idx]->metadata=fmt_ctx->streams[video_stream_idx]->metadata