Как извлечь элементарное видео из mp4 с помощью программы ffmpeg?

Я начал изучать 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) но теперь я немного застрял.

3

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]