Я начал изучать ffmpeg несколько слабых назад. На данный момент я могу транскодировать любое видео в mp4 с помощью кодека h264 / AVC. Основная схема примерно такая:
-открытый вход
-demux
-decode
-encode
-mux
Фактический код ниже:
#include <iostream>
#include <math.h>
extern "C"{
#ifndef __STDC_CONSTANT_MACROS
#undef main /* Prevents SDL from overriding main() */
# define __STDC_CONSTANT_MACROS
#endif#pragma comment (lib,"avcodec.lib")
#pragma comment (lib,"avformat.lib")
#pragma comment (lib,"swscale.lib")
#pragma comment(lib,"avutil.lib")
#include <libavcodec\avcodec.h>
#include <libavformat\avformat.h>
#include <libswscale\swscale.h>
#include <libavutil\mem.h>
#include <libavutil/opt.h>
#include <libavutil\channel_layout.h>
#include <libavutil\common.h>
#include <libavutil\imgutils.h>
#include <libavutil\mathematics.h>
#include <libavutil\samplefmt.h>
}using namespace std;void open_video(AVFormatContext*oc , AVCodec *codec, AVStream * st)
{
int ret;
AVCodecContext *c ;
c = st->codec;
/*open codec */
cout << "probably starts here" << endl;
ret = avcodec_open2(c,codec,NULL);
cout << "and ends here" << endl;
if ( ret < 0)
{
cout << ("Could not open video codec") << endl;
}}
/*This function will add a new stream to our file.
@param
oc -> Format context that the new stream will be added.
codec -> codec of the stream, this will be passed.
codec_id ->
chWidth->
chHeight->
*/
AVStream * addStream(AVFormatContext * oc, AVCodec **codec, enum AVCodecID codec_id, int chWidth, int chHeight, int fps)
{
AVCodecContext *c;
AVStream *st;
//find encoder of the stream, it passes this information to @codec, later on
//it will be used in encoding the video @ avcodec_encode_video2 in loop.
*codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if ( (*codec) == NULL)
cout << "ERROR CAN NOT FIND ENCODER! ERROR! ERROR! AVCODEC_FIND_ENCODER FAILED !!!1 """ << endl;
if(!(*codec))
printf ("Could not find encoder for ' %s ' ", avcodec_get_name(codec_id));//create a new stream with the found codec inside oc(AVFormatContext).
st = avformat_new_stream ( oc, *codec);
if (!st)
cout << " Cannot allocate stream " << endl;
//Setting the stream id.
//Since, there can be other streams in this AVFormatContext,
//we should find the first non used index. And this is oc->nb_streams(number of streams) - 1
st ->id = oc ->nb_streams - 1;
c = st->codec;
//setting the stream's codec's properties.
c-> codec_id = codec_id;
c->bit_rate = 4000000;
c->width = chWidth;
c->height = chHeight;
c->time_base.den = fps;
//fps;
c->time_base.num = 1;
c->gop_size = 12;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
/* just for testing, we also add B frames */
c->max_b_frames = 2;
}if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
/* Needed to avoid using macroblocks in which some coeffs overflow.
* This does not happen with normal video, it just happens here as
* the motion of the chroma plane does not match the luma plane. */
c->mb_decision = 2;
}
/* Some formats want stream headers to be separate. */
if (oc->oformat->flags & AVFMT_GLOBALHEADER)
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
//returning our lovely new brand stream.
return st;}
int changeResolution ( string source, int format )
{
//Data members
struct SwsContext *sws_ctx = NULL;
AVFrame *pFrame = NULL;
AVFrame *outFrame = NULL;
AVPacket packet;
uint8_t *buffer = NULL;
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
AVDictionary *optionsDict = NULL;
AVFormatContext *pFormatCtx = NULL;
AVFormatContext *outputContext = NULL;
AVCodecContext *pCodecCtx;
AVCodec *pCodec ;
AVCodec *codec;
AVCodec *videoCodec;
AVOutputFormat *fmt;
AVStream *video_stream;
int changeWidth;
int changeHeight;
int frameFinished;
int numBytes;
int fps;int lock = 0;
//Register all codecs & other important stuff. Vital!..
av_register_all();//Selects the desired resolution.
if (format == 0)
{
changeWidth = 320;
changeHeight = 180;
}
else if (format == 1)
{
changeWidth = 640;
changeHeight = 480;
}
else if (format == 2)
{
changeWidth = 960;
changeHeight = 540;
}
else if (format == 3)
{
changeWidth = 1024;
changeHeight = 768;
}
else
{
changeWidth = 1280;
changeHeight = 720;
}// Open video file
int aaa;
aaa = avformat_open_input(&pFormatCtx, source.c_str(), NULL, NULL) ;
if(aaa !=0)
{
cout << " cannot open input file \n" << endl;
cout << "aaa = " << aaa << endl;
return -1; // Couldn't open file
}
// Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0)
return -1; // Couldn't find stream information
//just checking duration casually for no reason
/*int64_t duration = pFormatCtx->duration;
cout << "the duration is " << duration << " " << endl;*/
//this writes the info about the file
av_dump_format(pFormatCtx, 0, 0, 0);
cin >> lock;
// Find the first video stream
int videoStream=-1;
int i;
for(i=0; i<3; i++)
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream=i;
cout << " lel \n " ;
break;
}if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
fps = pCodecCtx -> time_base.den;//Find the decoder of the input file, for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}// Open codec, you must open it first, in order to use it.
if(avcodec_open2(pCodecCtx, pCodec, &optionsDict)<0)
return -1; // Could not open codec// Allocate video frame ( pFrame for taking the packets into, outFrame for processed frames to packet.)
pFrame=avcodec_alloc_frame();
outFrame = avcodec_alloc_frame();i=0;
int ret;
int video_frame_count = 0;
//Initiate the outFrame set the buffer & fill the properties
numBytes=avpicture_get_size(PIX_FMT_YUV420P, changeWidth, changeHeight);
buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)outFrame, buffer, PIX_FMT_YUV420P, changeWidth, changeHeight );int pp;
int frameNo = 0;
//allocate the outputContext, it will be the AVFormatContext of our output file.
//It will try to find the format by giving the file name.
avformat_alloc_output_context2(&outputContext,NULL,NULL, "myoutput.mp4");
//Cant find the file extension, using MPEG as default.
if (!outputContext) {
printf("Could not deduce output format from file extension: using MPEG.\n");
avformat_alloc_output_context2(&outputContext, NULL, "mpeg", "myoutput.mp4");
}
//Still cant set file extension, exit.
if (!outputContext) {
return 1;
}
//set AVOutputFormat fmt to our outputContext's format.
fmt = outputContext -> oformat;
video_stream = NULL;
//If fmt has a valid codec_id, create a new video stream.
//This function will set the streams codec & codecs desired properties.
//Stream's codec will be passed to videoCodec for later usage.
if (fmt -> video_codec != AV_CODEC_ID_NONE)
video_stream = addStream(outputContext, &videoCodec, fmt ->video_codec, changeWidth, changeHeight,fps);//open the video using videoCodec. by avcodec_open2() i.e open the codec.
if (video_stream)
open_video(outputContext, videoCodec, video_stream);
//Creating our new output file.
if (!(fmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&outputContext->pb, "toBeStreamed.264", AVIO_FLAG_WRITE);
if (ret < 0) {
cout << " cant open file " << endl;
return 1;
}
}
//Writing the header of format context.
//ret = avformat_write_header(outputContext, NULL);
if (ret >= 0) {
cout << "writing header success !!!" << endl;
}//Start reading packages from input file.
while(av_read_frame(pFormatCtx, &packet)>=0 ) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video package into frames
ret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
if( ret < 0)
{
printf ( " Error decoding frame !!.." );
return ret;
}
if (frameFinished){
printf("video_frame n:%d coded_n:%d\n" , video_frame_count++, pFrame->coded_picture_number);
}
av_free_packet(&packet);
//do stuff with frame, in this case we are changing the resolution.
static struct SwsContext *img_convert_ctx_in = NULL;
if (img_convert_ctx_in == NULL)
{
img_convert_ctx_in =sws_getContext( pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
changeWidth,
changeHeight,
PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL,
NULL,
NULL );
}
//scale the frames
sws_scale(img_convert_ctx_in,
pFrame->data,
pFrame->linesize,
0,
pCodecCtx->height,
outFrame->data,
outFrame->linesize);
//initiate the pts value
if ( frameNo == 0)
outFrame->pts = 0;//calculate the pts value & set it.
outFrame->pts += av_rescale_q(1, video_stream->codec->time_base, video_stream->time_base);
//encode frames into packages. Package passed in @packet.
if(avcodec_encode_video2(outputContext->streams[0]->codec, &packet, outFrame, &pp) < 0 )
cout << "Encoding frames into packages, failed. " << endl;
frameNo++;
//write the packages into file, resulting in creating a video file.
av_interleaved_write_frame(outputContext,&packet);}
}av_free_packet(&packet);
//av_write_trailer(outputContext);
avio_close(outputContext->pb);// Free the RGB image
av_free(buffer);
av_free(outFrame);
// Free the YUV frame
av_free(pFrame);
// Close the codec
avcodec_close(video_stream->codec);
avcodec_close(pCodecCtx);
// Close the video file
avformat_close_input(&pFormatCtx);
return 0;}
в конце процесса я получаю нужный файл с нужным кодеком & контейнер & разрешающая способность.
Моя проблема в том, что в части нашего проекта мне нужно получить элементарные видеопотоки в файле. Такой как пример.264. Однако я не могу добавить поток без создания AVFormatContext. Я не могу создать AVFormatContext, потому что 264 файла не имеют контейнера, они просто необработанное видео? Насколько я знаю.
Я пробовал способ в decoding_encoding.c, который использует FWRITE. Однако этот пример был для кодека mpeg-2, и когда я пытался адаптировать этот код к кодеку H264 / AVC, я получал ошибку «деление с плавающей точкой на ноль» из mediainfo, и, кроме того, некоторые свойства видео не отображались (например, как FPS & воспр & добротность). Я думаю, что это связано с «конечным кодом», который пример добавляет в конце кода. Это для MPEG-2. (uint8_t endcode [] = {0, 0, 1, 0xb7};)
В любом случае, я хотел бы получить стартовую точку для этой задачи. Мне удалось зайти так далеко, используя интернет-ресурсы (довольно мало & устарел для ffmpeg) но теперь я немного застрял.
Задача ещё не решена.
Других решений пока нет …