From 49993e6f9c77bf4c610b53e3abc4ce5651c7700b Mon Sep 17 00:00:00 2001 From: Comment Date: Thu, 21 Mar 2013 22:05:38 +0000 Subject: wrestling with libavcodec --- api-example.c | 458 ++++++++++++++++++++++++++++++++++ avcodec_audio_example.c | 114 +++++++++ avcodec_audio_example1.c | 93 +++++++ avcodec_audio_example2.c | 143 +++++++++++ avcodec_audio_example3-c++11.c | 120 +++++++++ notes | 8 + output-example.c | 554 +++++++++++++++++++++++++++++++++++++++++ rotord/rotor.cpp | 43 ++-- 8 files changed, 1516 insertions(+), 17 deletions(-) create mode 100644 api-example.c create mode 100644 avcodec_audio_example.c create mode 100644 avcodec_audio_example1.c create mode 100644 avcodec_audio_example2.c create mode 100644 avcodec_audio_example3-c++11.c create mode 100644 notes create mode 100644 output-example.c diff --git a/api-example.c b/api-example.c new file mode 100644 index 0000000..fb48b1f --- /dev/null +++ b/api-example.c @@ -0,0 +1,458 @@ +/* + * copyright (c) 2001 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * avcodec API use example. + * + * Note that this library only handles codecs (mpeg, mpeg4, etc...), + * not file formats (avi, vob, etc...). See library 'libavformat' for the + * format handling + */ + +#include +#include +#include + +#ifdef HAVE_AV_CONFIG_H +#undef HAVE_AV_CONFIG_H +#endif + +#include "libavcodec/avcodec.h" +#include "libavutil/mathematics.h" + +#define INBUF_SIZE 4096 + +/* + * Audio encoding example + */ +static void audio_encode_example(const char *filename) +{ + AVCodec *codec; + AVCodecContext *c= NULL; + int frame_size, i, j, out_size, outbuf_size; + FILE *f; + short *samples; + float t, tincr; + uint8_t *outbuf; + + printf("Audio encoding\n"); + + /* find the MP2 encoder */ + codec = avcodec_find_encoder(CODEC_ID_MP2); + if (!codec) { + fprintf(stderr, "codec not found\n"); + exit(1); + } + + c= avcodec_alloc_context(); + + /* put sample parameters */ + c->bit_rate = 64000; + c->sample_rate = 44100; + c->channels = 2; + + /* open it */ + if (avcodec_open(c, codec) < 0) { + fprintf(stderr, "could not open codec\n"); + exit(1); + } + + /* the codec gives us the frame size, in samples */ + frame_size = c->frame_size; + samples = malloc(frame_size * 2 * c->channels); + outbuf_size = 10000; + outbuf = malloc(outbuf_size); + + f = fopen(filename, "wb"); + if (!f) { + fprintf(stderr, "could not open %s\n", filename); + exit(1); + } + + /* encode a single tone sound */ + t = 0; + tincr = 2 * M_PI * 440.0 / c->sample_rate; + for(i=0;i<200;i++) { + for(j=0;j 0) { + out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; + len = avcodec_decode_audio3(c, (short *)outbuf, &out_size, &avpkt); + if (len < 0) { + fprintf(stderr, "Error while decoding\n"); + exit(1); + } + if (out_size > 0) { + /* if a frame has been decoded, output it */ + fwrite(outbuf, 1, out_size, outfile); + } + avpkt.size -= len; + avpkt.data += len; + } + } + + fclose(outfile); + fclose(f); + free(outbuf); + + avcodec_close(c); + av_free(c); +} + +/* + * Video encoding example + */ +static void video_encode_example(const char *filename) +{ + AVCodec *codec; + AVCodecContext *c= NULL; + int i, out_size, size, x, y, outbuf_size; + FILE *f; + AVFrame *picture; + uint8_t *outbuf, *picture_buf; + + printf("Video encoding\n"); + + /* find the mpeg1 video encoder */ + codec = avcodec_find_encoder(CODEC_ID_MPEG1VIDEO); + if (!codec) { + fprintf(stderr, "codec not found\n"); + exit(1); + } + + c= avcodec_alloc_context(); + picture= avcodec_alloc_frame(); + + /* put sample parameters */ + c->bit_rate = 400000; + /* resolution must be a multiple of two */ + c->width = 352; + c->height = 288; + /* frames per second */ + c->time_base= (AVRational){1,25}; + c->gop_size = 10; /* emit one intra frame every ten frames */ + c->max_b_frames=1; + c->pix_fmt = PIX_FMT_YUV420P; + + /* open it */ + if (avcodec_open(c, codec) < 0) { + fprintf(stderr, "could not open codec\n"); + exit(1); + } + + f = fopen(filename, "wb"); + if (!f) { + fprintf(stderr, "could not open %s\n", filename); + exit(1); + } + + /* alloc image and output buffer */ + outbuf_size = 100000; + outbuf = malloc(outbuf_size); + size = c->width * c->height; + picture_buf = malloc((size * 3) / 2); /* size for YUV 420 */ + + picture->data[0] = picture_buf; + picture->data[1] = picture->data[0] + size; + picture->data[2] = picture->data[1] + size / 4; + picture->linesize[0] = c->width; + picture->linesize[1] = c->width / 2; + picture->linesize[2] = c->width / 2; + + /* encode 1 second of video */ + for(i=0;i<25;i++) { + fflush(stdout); + /* prepare a dummy image */ + /* Y */ + for(y=0;yheight;y++) { + for(x=0;xwidth;x++) { + picture->data[0][y * picture->linesize[0] + x] = x + y + i * 3; + } + } + + /* Cb and Cr */ + for(y=0;yheight/2;y++) { + for(x=0;xwidth/2;x++) { + picture->data[1][y * picture->linesize[1] + x] = 128 + y + i * 2; + picture->data[2][y * picture->linesize[2] + x] = 64 + x + i * 5; + } + } + + /* encode the image */ + out_size = avcodec_encode_video(c, outbuf, outbuf_size, picture); + printf("encoding frame %3d (size=%5d)\n", i, out_size); + fwrite(outbuf, 1, out_size, f); + } + + /* get the delayed frames */ + for(; out_size; i++) { + fflush(stdout); + + out_size = avcodec_encode_video(c, outbuf, outbuf_size, NULL); + printf("write frame %3d (size=%5d)\n", i, out_size); + fwrite(outbuf, 1, out_size, f); + } + + /* add sequence end code to have a real mpeg file */ + outbuf[0] = 0x00; + outbuf[1] = 0x00; + outbuf[2] = 0x01; + outbuf[3] = 0xb7; + fwrite(outbuf, 1, 4, f); + fclose(f); + free(picture_buf); + free(outbuf); + + avcodec_close(c); + av_free(c); + av_free(picture); + printf("\n"); +} + +/* + * Video decoding example + */ + +static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize, + char *filename) +{ + FILE *f; + int i; + + f=fopen(filename,"w"); + fprintf(f,"P5\n%d %d\n%d\n",xsize,ysize,255); + for(i=0;icapabilities&CODEC_CAP_TRUNCATED) + c->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */ + + /* For some codecs, such as msmpeg4 and mpeg4, width and height + MUST be initialized there because this information is not + available in the bitstream. */ + + /* open it */ + if (avcodec_open(c, codec) < 0) { + fprintf(stderr, "could not open codec\n"); + exit(1); + } + + /* the codec gives us the frame size, in samples */ + + f = fopen(filename, "rb"); + if (!f) { + fprintf(stderr, "could not open %s\n", filename); + exit(1); + } + + frame = 0; + for(;;) { + avpkt.size = fread(inbuf, 1, INBUF_SIZE, f); + if (avpkt.size == 0) + break; + + /* NOTE1: some codecs are stream based (mpegvideo, mpegaudio) + and this is the only method to use them because you cannot + know the compressed data size before analysing it. + + BUT some other codecs (msmpeg4, mpeg4) are inherently frame + based, so you must call them with all the data for one + frame exactly. You must also initialize 'width' and + 'height' before initializing them. */ + + /* NOTE2: some codecs allow the raw parameters (frame size, + sample rate) to be changed at any frame. We handle this, so + you should also take care of it */ + + /* here, we use a stream based decoder (mpeg1video), so we + feed decoder and see if it could decode a frame */ + avpkt.data = inbuf; + while (avpkt.size > 0) { + len = avcodec_decode_video2(c, picture, &got_picture, &avpkt); + if (len < 0) { + fprintf(stderr, "Error while decoding frame %d\n", frame); + exit(1); + } + if (got_picture) { + printf("saving frame %3d\n", frame); + fflush(stdout); + + /* the picture is allocated by the decoder. no need to + free it */ + snprintf(buf, sizeof(buf), outfilename, frame); + pgm_save(picture->data[0], picture->linesize[0], + c->width, c->height, buf); + frame++; + } + avpkt.size -= len; + avpkt.data += len; + } + } + + /* some codecs, such as MPEG, transmit the I and P frame with a + latency of one frame. You must do the following to have a + chance to get the last frame of the video */ + avpkt.data = NULL; + avpkt.size = 0; + len = avcodec_decode_video2(c, picture, &got_picture, &avpkt); + if (got_picture) { + printf("saving last frame %3d\n", frame); + fflush(stdout); + + /* the picture is allocated by the decoder. no need to + free it */ + snprintf(buf, sizeof(buf), outfilename, frame); + pgm_save(picture->data[0], picture->linesize[0], + c->width, c->height, buf); + frame++; + } + + fclose(f); + + avcodec_close(c); + av_free(c); + av_free(picture); + printf("\n"); +} + +int main(int argc, char **argv) +{ + const char *filename; + + /* must be called before using avcodec lib */ + avcodec_init(); + + /* register all the codecs */ + avcodec_register_all(); + + if (argc <= 1) { + audio_encode_example("/tmp/test.mp2"); + audio_decode_example("/tmp/test.sw", "/tmp/test.mp2"); + + video_encode_example("/tmp/test.mpg"); + filename = "/tmp/test.mpg"; + } else { + filename = argv[1]; + } + + // audio_decode_example("/tmp/test.sw", filename); + video_decode_example("/tmp/test%d.pgm", filename); + + return 0; +} diff --git a/avcodec_audio_example.c b/avcodec_audio_example.c new file mode 100644 index 0000000..236b976 --- /dev/null +++ b/avcodec_audio_example.c @@ -0,0 +1,114 @@ +int main(int argc, char **argv) +{ + + const char* input_filename=argv[1]; + + //avcodec_register_all(); + av_register_all(); + //av_ini + + AVFormatContext* container=avformat_alloc_context(); + if(avformat_open_input(&container,input_filename,NULL,NULL)<0){ + die("Could not open file"); + } + + if(av_find_stream_info(container)<0){ + die("Could not find file info"); + } + av_dump_format(container,0,input_filename,false); + + int stream_id=-1; + int i; + for(i=0;inb_streams;i++){ + if(container->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){ + stream_id=i; + break; + } + } + if(stream_id==-1){ + die("Could not find Audio Stream"); + } + + AVDictionary *metadata=container->metadata; + + AVCodecContext *ctx=container->streams[stream_id]->codec; + AVCodec *codec=avcodec_find_decoder(ctx->codec_id); + + if(codec==NULL){ + die("cannot find codec!"); + } + + if(avcodec_open(ctx,codec)<0){ + die("Codec cannot be found"); + } + + //ctx=avcodec_alloc_context3(codec); + + //initialize AO lib + ao_initialize(); + + int driver=ao_default_driver_id(); + + ao_sample_format sformat; + AVSampleFormat sfmt=ctx->sample_fmt; + if(sfmt==AV_SAMPLE_FMT_U8){ + printf("U8\n"); + + sformat.bits=8; + }else if(sfmt==AV_SAMPLE_FMT_S16){ + printf("S16\n"); + sformat.bits=16; + }else if(sfmt==AV_SAMPLE_FMT_S32){ + printf("S32\n"); + sformat.bits=32; + } + + sformat.channels=ctx->channels; + sformat.rate=ctx->sample_rate; + sformat.byte_format=AO_FMT_NATIVE; + sformat.matrix=0; + + ao_device *adevice=ao_open_live(driver,&sformat,NULL); + //end of init AO LIB + + AVPacket packet; + av_init_packet(&packet); + + AVFrame *frame=avcodec_alloc_frame(); + + + + int buffer_size=AVCODEC_MAX_AUDIO_FRAME_SIZE+ FF_INPUT_BUFFER_PADDING_SIZE;; + uint8_t buffer[buffer_size]; + packet.data=buffer; + packet.size =buffer_size; + + + + int len; + int frameFinished=0; + while(av_read_frame(container,&packet)>=0) + { + + if(packet.stream_index==stream_id){ + //printf("Audio Frame read \n"); + int len=avcodec_decode_audio4(ctx,frame,&frameFinished,&packet); + //frame-> + if(frameFinished){ + //printf("Finished reading Frame len : %d , nb_samples:%d buffer_size:%d line size: %d \n",len,frame->nb_samples,buffer_size,frame->linesize[0]); + ao_play(adevice, (char*)frame->extended_data[0],frame->linesize[0] ); + }else{ + //printf("Not Finished\n"); + } + + }else { + printf("Someother packet possibly Video\n"); + } + + + } + + av_close_input_file(container); + ao_shutdown(); + return 0; +} \ No newline at end of file diff --git a/avcodec_audio_example1.c b/avcodec_audio_example1.c new file mode 100644 index 0000000..b296c00 --- /dev/null +++ b/avcodec_audio_example1.c @@ -0,0 +1,93 @@ +bool Render_context::load_audio(string &filename){ + //load audio into memory and create thumbnail + AVCodec *codec; + AVCodecContext *c= NULL; + int len; + FILE *f; + uint8_t inbuf[AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; + AVPacket avpkt; + AVFrame *decoded_frame = NULL; + + av_init_packet(&avpkt); + + // http://stackoverflow.com/questions/9799560/decode-audio-using-libavcodec-and-play-using-libao + + /* find the mpeg audio decoder */ + codec = avcodec_find_decoder(AV_CODEC_ID_MP2); + if (!codec) { + fprintf(stderr, "Codec not found\n"); + return false; + } + + c = avcodec_alloc_context3(codec); + if (!c) { + fprintf(stderr, "Could not allocate audio codec context\n"); + return false; + } + + /* open it */ + if (avcodec_open2(c, codec, NULL) < 0) { + fprintf(stderr, "Could not open codec\n"); + return false; + } + + f = fopen(filename.c_str(), "rb"); + if (!f) { + fprintf(stderr, "Could not open %s\n", filename.c_str()); + return false; + } + + /* decode until eof */ + avpkt.data = inbuf; + avpkt.size = fread(inbuf, 1, AUDIO_INBUF_SIZE, f); + + while (avpkt.size > 0) { + int got_frame = 0; + + if (!decoded_frame) { + if (!(decoded_frame = avcodec_alloc_frame())) { + printf("Could not allocate audio frame\n"); + return false; + } + } else + avcodec_get_frame_defaults(decoded_frame); + + len = avcodec_decode_audio4(c, decoded_frame, &got_frame, &avpkt); + if (len < 0) { + fprintf(stderr, "Error while decoding\n"); + return false; + } + if (got_frame) { + /* if a frame has been decoded, output it */ + int data_size = av_samples_get_buffer_size(NULL, c->channels,decoded_frame->nb_samples,c->sample_fmt, 1); + //fwrite(decoded_frame->data[0], 1, data_size, outfile); + + //here is where we do something with the data + //is it best to buffer it or do do all processing within a loop like this? + + } + avpkt.size -= len; + avpkt.data += len; + avpkt.dts = + avpkt.pts = AV_NOPTS_VALUE; + if (avpkt.size < AUDIO_REFILL_THRESH) { + /* Refill the input buffer, to avoid trying to decode + * incomplete frames. Instead of this, one could also use + * a parser, or use a proper container format through + * libavformat. */ + memmove(inbuf, avpkt.data, avpkt.size); + avpkt.data = inbuf; + len = fread(avpkt.data + avpkt.size, 1,AUDIO_INBUF_SIZE - avpkt.size, f); + if (len > 0) + avpkt.size += len; + } + } + + fclose(f); + + avcodec_close(c); + av_free(c); + avcodec_free_frame(&decoded_frame); + + return true; +} \ No newline at end of file diff --git a/avcodec_audio_example2.c b/avcodec_audio_example2.c new file mode 100644 index 0000000..6546d3f --- /dev/null +++ b/avcodec_audio_example2.c @@ -0,0 +1,143 @@ +//this seems to be obsolete!!!!!!!!!!!!!!!!! + + + + +bool Render_context::load_audio(string &filename){ + //load audio into memory and create thumbnail + + av_register_all(); + avcodec_register_all(); + //av_register_all(); + //av_ini + + AVFormatContext* container=avformat_alloc_context(); + if(avformat_open_input(&container,filename.c_str(),NULL,NULL)<0){ + cerr << "Could not open file" << endl; + return false; + } + +//header in avformat.h definition in utils.c in ffmpeg/libavformat +//int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options) +//set input or dictionary?? + + + if(av_find_stream_info(container)<0){ + cerr << "Could not find file info"<< endl; + return false; + } + + //added to try and locate missing audio streams + //container->flags |= AVFMT_FLAG_NONBLOCK; + // + cerr << "found " << container->nb_streams << " streams" << endl; + av_dump_format(container,0,filename.c_str(),false); + cerr << "found " << container->nb_streams << " streams" << endl; + + //typedef struct AVFormatContext { + // unsigned int nb_streams; + //AVStream **streams; + /** + * A list of all streams in the file. New streams are created with + * avformat_new_stream(). + * + * decoding: streams are created by libavformat in avformat_open_input(). + * If AVFMTCTX_NOHEADER is set in ctx_flags, thandroid on linux i386en new streams may also + * appear in av_read_frame(). + * encoding: streams are created by the user before avformat_write_header(). + */ + + int stream_id=-1; + int i; + for(i=0;inb_streams;i++){ + cerr << "stream " << i << ": codec type " << container->streams[i]->codec->codec_type << endl; + if(container->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){ + stream_id=i; + break; + } + } + if(stream_id==-1){ + cerr << "Could not find Audio Stream"<< endl; + return false; + } + + AVDictionary *metadata=container->metadata; + + AVCodecContext *ctx=container->streams[stream_id]->codec; + AVCodec *codec=avcodec_find_decoder(ctx->codec_id); + + if(codec==NULL){ + cerr <<"cannot find codec!"<< endl; + return false; + } + + if(avcodec_open(ctx,codec)<0){ + cerr <<"Codec cannot be found"<< endl; + return false; + } + + //ctx=avcodec_alloc_context3(codec); + + AVSampleFormat sfmt=ctx->sample_fmt; + if(sfmt==AV_SAMPLE_FMT_U8){ + printf("U8\n"); + + //sformat.bits=8; + }else if(sfmt==AV_SAMPLE_FMT_S16){ + printf("S16\n"); + //sformat.bits=16; + }else if(sfmt==AV_SAMPLE_FMT_S32){ + printf("S32\n"); + //sformat.bits=32; + } + + // sformat.channels=ctx->channels; + // sformat.rate=ctx->sample_rate; + // sformat.byte_format=AO_FMT_NATIVE; +// sformat.matrix=0; + + + + AVPacket packet; + av_init_packet(&packet); + + AVFrame *frame=avcodec_alloc_frame(); + + + + int buffer_size=AVCODEC_MAX_AUDIO_FRAME_SIZE+ FF_INPUT_BUFFER_PADDING_SIZE;; + uint8_t buffer[buffer_size]; + packet.data=buffer; + packet.size =buffer_size; + + + + int len; + int frameFinished=0; + while(av_read_frame(container,&packet)>=0) + { + + if(packet.stream_index==stream_id){ + //printf("Audio Frame read \n"); + int len=avcodec_decode_audio4(ctx,frame,&frameFinished,&packet); + //frame-> + if(frameFinished){ + //printf("Finished reading Frame len : %d , nb_samples:%d buffer_size:%d line size: %d \n",len,frame->nb_samples,buffer_size,frame->linesize[0]); + //ao_play(adevice, (char*)frame->extended_data[0],frame->linesize[0] ); + + //DO SOMETHING WITH THE DATA HERE + }else{ + //printf("Not Finished\n"); + } + + }else { + cerr << "Avcodec found some other packet possibly Video\n"; + } + + + } + + av_close_input_file(container); + + return true; +} \ No newline at end of file diff --git a/avcodec_audio_example3-c++11.c b/avcodec_audio_example3-c++11.c new file mode 100644 index 0000000..ddb88db --- /dev/null +++ b/avcodec_audio_example3-c++11.c @@ -0,0 +1,120 @@ + av_register_all(); + + std::shared_ptr avFormat(avformat_alloc_context(), &avformat_free_context); + + auto avFormatPtr = avFormat.get(); + if (avformat_open_input(&avFormatPtr,filename.c_str(),nullptr, nullptr) != 0) { + cerr <<"Rotor: Error while calling avformat_open_input (probably invalid file format)" << endl; + return false; + } + + if (avformat_find_stream_info(avFormat.get(), nullptr) < 0) { + cerr << "Rotor: Error while calling avformat_find_stream_info" << endl; + return false; + } + + av_dump_format(avFormat.get(), 0, 0, false); //avformat.h line 1256 + + AVStream* stream = nullptr; + for (unsigned int i = 0; i < avFormat->nb_streams; ++i) { + if (avFormat->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + // we've found a video stream! + stream = avFormat->streams[i]; + break; + } + } + if (!stream) { + cerr <<"Rotor: Didn't find any audio stream in the file"<< endl; + return false; + } + + // getting the required codec structure + const auto codec = avcodec_find_decoder(stream->codec->codec_id); //returns AVCodec* + if (codec == nullptr) { + cerr <<"Rotor: Audio codec not available"<< endl; + return false; + } + + //AVCodecContext?? avFormat->streams[i]->codec + + // allocating a structure + std::shared_ptr audioCodec(avcodec_alloc_context3(codec), [](AVCodecContext* c) { avcodec_close(c); av_free(c); }); + + /* extradata??? + // we need to make a copy of videoStream->codec->extradata and give it to the context + // make sure that this vector exists as long as the avVideoCodec exists + std::vector codecContextExtraData(stream->codec->extradata, stream->codec->extradata + stream->codec->extradata_size); + audioCodec->extradata = reinterpret_cast(codecContextExtraData.data()); + audioCodec->extradata_size = codecContextExtraData.size(); + + // initializing the structure by opening the codec + if (avcodec_open2(avVideoCodec.get(), codec, nullptr) < 0) { + cerr <<"Rotor: Could not open codec"<< endl; + return false; + } + */ + + //avcodec.h line 1026 + + + Packet packet(avFormat.get()); + if (packet.packet.data == nullptr) { + //done + return true; + } + + cerr << "audio codec context - sample rate: "<< audioCodec->sample_rate <<", channels: "<channels<<", sample format: "<sample_fmt<index) + continue; // the packet is not about the video stream we want, let's jump again the start of the loop + } while(0); + + // allocating an AVFrame + std::shared_ptr avFrame(avcodec_alloc_frame(), &av_free); + + // the current packet of data + //Packet packet; + // data in the packet of data already processed + size_t offsetInData = 0; + + + + /* + // the decoding loop, running until EOF + while (true) { + // reading a packet using libavformat + if (offsetInData >= packet.packet.size) { + do { + packet.reset(avFormat.get()); + if (packet.stream_index != videoStream->index) + continue; + } while(0); + } + + // preparing the packet that we will send to libavcodec + AVPacket packetToSend; + packetToSend.data = packet.packet.data + offsetInData; + packetToSend.size = packet.packet.size - offsetInData; + + // sending data to libavcodec + int isFrameAvailable = 0; + const auto processedLength = avcodec_decode_video2(avVideoCodec.get(), avFrame.get(), &isFrameAvailable, &packetToSend); + if (processedLength < 0) { + av_free_packet(&packet); + throw std::runtime_error("Error while processing the data"); + } + offsetInData += processedLength; + + // processing the image if available + if (isFrameAvailable) { + // display image on the screen + + // sleeping until next frame + const auto msToWait = avVideoContext->ticks_per_frame * 1000 * avVideoContext->time_base.num / avVideoContext->time_base.den; + std::this_thread::sleep(std::chrono::milliseconds(msToWait)); + } + } + */ \ No newline at end of file diff --git a/notes b/notes new file mode 100644 index 0000000..64741b6 --- /dev/null +++ b/notes @@ -0,0 +1,8 @@ +ffmpeg server configuration + +https://ffmpeg.org/trac/ffmpeg/wiki/UbuntuCompilationGuide +http://stackoverflow.com/questions/9506046/ffmpeg-install-on-centos-64-bit-install-with-fpic-error + +GST headers are at:- + +/usr/include/gstreamer-1.0/gst/gst.h \ No newline at end of file diff --git a/output-example.c b/output-example.c new file mode 100644 index 0000000..dd61cfe --- /dev/null +++ b/output-example.c @@ -0,0 +1,554 @@ +/* + * Libavformat API example: Output a media file in any supported + * libavformat format. The default codecs are used. + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include + +#include "libavformat/avformat.h" +#include "libswscale/swscale.h" + +#undef exit + +/* 5 seconds stream duration */ +#define STREAM_DURATION 5.0 +#define STREAM_FRAME_RATE 25 /* 25 images/s */ +#define STREAM_NB_FRAMES ((int)(STREAM_DURATION * STREAM_FRAME_RATE)) +#define STREAM_PIX_FMT PIX_FMT_YUV420P /* default pix_fmt */ + +static int sws_flags = SWS_BICUBIC; + +/**************************************************************/ +/* audio output */ + +float t, tincr, tincr2; +int16_t *samples; +uint8_t *audio_outbuf; +int audio_outbuf_size; +int audio_input_frame_size; + +/* + * add an audio output stream + */ +static AVStream *add_audio_stream(AVFormatContext *oc, enum CodecID codec_id) +{ + AVCodecContext *c; + AVStream *st; + + st = av_new_stream(oc, 1); + if (!st) { + fprintf(stderr, "Could not alloc stream\n"); + exit(1); + } + + c = st->codec; + c->codec_id = codec_id; + c->codec_type = AVMEDIA_TYPE_AUDIO; + + /* put sample parameters */ + c->bit_rate = 64000; + c->sample_rate = 44100; + c->channels = 2; + + // some formats want stream headers to be separate + if(oc->oformat->flags & AVFMT_GLOBALHEADER) + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + + return st; +} + +static void open_audio(AVFormatContext *oc, AVStream *st) +{ + AVCodecContext *c; + AVCodec *codec; + + c = st->codec; + + /* find the audio encoder */ + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "codec not found\n"); + exit(1); + } + + /* open it */ + if (avcodec_open(c, codec) < 0) { + fprintf(stderr, "could not open codec\n"); + exit(1); + } + + /* init signal generator */ + t = 0; + tincr = 2 * M_PI * 110.0 / c->sample_rate; + /* increment frequency by 110 Hz per second */ + tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate; + + audio_outbuf_size = 10000; + audio_outbuf = av_malloc(audio_outbuf_size); + + /* ugly hack for PCM codecs (will be removed ASAP with new PCM + support to compute the input frame size in samples */ + if (c->frame_size <= 1) { + audio_input_frame_size = audio_outbuf_size / c->channels; + switch(st->codec->codec_id) { + case CODEC_ID_PCM_S16LE: + case CODEC_ID_PCM_S16BE: + case CODEC_ID_PCM_U16LE: + case CODEC_ID_PCM_U16BE: + audio_input_frame_size >>= 1; + break; + default: + break; + } + } else { + audio_input_frame_size = c->frame_size; + } + samples = av_malloc(audio_input_frame_size * 2 * c->channels); +} + +/* prepare a 16 bit dummy audio frame of 'frame_size' samples and + 'nb_channels' channels */ +static void get_audio_frame(int16_t *samples, int frame_size, int nb_channels) +{ + int j, i, v; + int16_t *q; + + q = samples; + for(j=0;jcodec; + + get_audio_frame(samples, audio_input_frame_size, c->channels); + + pkt.size= avcodec_encode_audio(c, audio_outbuf, audio_outbuf_size, samples); + + if (c->coded_frame && c->coded_frame->pts != AV_NOPTS_VALUE) + pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base); + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index= st->index; + pkt.data= audio_outbuf; + + /* write the compressed frame in the media file */ + if (av_interleaved_write_frame(oc, &pkt) != 0) { + fprintf(stderr, "Error while writing audio frame\n"); + exit(1); + } +} + +static void close_audio(AVFormatContext *oc, AVStream *st) +{ + avcodec_close(st->codec); + + av_free(samples); + av_free(audio_outbuf); +} + +/**************************************************************/ +/* video output */ + +AVFrame *picture, *tmp_picture; +uint8_t *video_outbuf; +int frame_count, video_outbuf_size; + +/* add a video output stream */ +static AVStream *add_video_stream(AVFormatContext *oc, enum CodecID codec_id) +{ + AVCodecContext *c; + AVStream *st; + + st = av_new_stream(oc, 0); + if (!st) { + fprintf(stderr, "Could not alloc stream\n"); + exit(1); + } + + c = st->codec; + c->codec_id = codec_id; + c->codec_type = AVMEDIA_TYPE_VIDEO; + + /* put sample parameters */ + c->bit_rate = 400000; + /* resolution must be a multiple of two */ + c->width = 352; + c->height = 288; + /* time base: this is the fundamental unit of time (in seconds) in terms + of which frame timestamps are represented. for fixed-fps content, + timebase should be 1/framerate and timestamp increments should be + identically 1. */ + c->time_base.den = STREAM_FRAME_RATE; + c->time_base.num = 1; + c->gop_size = 12; /* emit one intra frame every twelve frames at most */ + c->pix_fmt = STREAM_PIX_FMT; + if (c->codec_id == CODEC_ID_MPEG2VIDEO) { + /* just for testing, we also add B frames */ + c->max_b_frames = 2; + } + if (c->codec_id == 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; + + return st; +} + +static AVFrame *alloc_picture(enum PixelFormat pix_fmt, int width, int height) +{ + AVFrame *picture; + uint8_t *picture_buf; + int size; + + picture = avcodec_alloc_frame(); + if (!picture) + return NULL; + size = avpicture_get_size(pix_fmt, width, height); + picture_buf = av_malloc(size); + if (!picture_buf) { + av_free(picture); + return NULL; + } + avpicture_fill((AVPicture *)picture, picture_buf, + pix_fmt, width, height); + return picture; +} + +static void open_video(AVFormatContext *oc, AVStream *st) +{ + AVCodec *codec; + AVCodecContext *c; + + c = st->codec; + + /* find the video encoder */ + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "codec not found\n"); + exit(1); + } + + /* open the codec */ + if (avcodec_open(c, codec) < 0) { + fprintf(stderr, "could not open codec\n"); + exit(1); + } + + video_outbuf = NULL; + if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { + /* allocate output buffer */ + /* XXX: API change will be done */ + /* buffers passed into lav* can be allocated any way you prefer, + as long as they're aligned enough for the architecture, and + they're freed appropriately (such as using av_free for buffers + allocated with av_malloc) */ + video_outbuf_size = 200000; + video_outbuf = av_malloc(video_outbuf_size); + } + + /* allocate the encoded raw picture */ + picture = alloc_picture(c->pix_fmt, c->width, c->height); + if (!picture) { + fprintf(stderr, "Could not allocate picture\n"); + exit(1); + } + + /* if the output format is not YUV420P, then a temporary YUV420P + picture is needed too. It is then converted to the required + output format */ + tmp_picture = NULL; + if (c->pix_fmt != PIX_FMT_YUV420P) { + tmp_picture = alloc_picture(PIX_FMT_YUV420P, c->width, c->height); + if (!tmp_picture) { + fprintf(stderr, "Could not allocate temporary picture\n"); + exit(1); + } + } +} + +/* prepare a dummy image */ +static void fill_yuv_image(AVFrame *pict, int frame_index, int width, int height) +{ + int x, y, i; + + i = frame_index; + + /* Y */ + for(y=0;ydata[0][y * pict->linesize[0] + x] = x + y + i * 3; + } + } + + /* Cb and Cr */ + for(y=0;ydata[1][y * pict->linesize[1] + x] = 128 + y + i * 2; + pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5; + } + } +} + +static void write_video_frame(AVFormatContext *oc, AVStream *st) +{ + int out_size, ret; + AVCodecContext *c; + static struct SwsContext *img_convert_ctx; + + c = st->codec; + + if (frame_count >= STREAM_NB_FRAMES) { + /* no more frame to compress. The codec has a latency of a few + frames if using B frames, so we get the last frames by + passing the same picture again */ + } else { + if (c->pix_fmt != PIX_FMT_YUV420P) { + /* as we only generate a YUV420P picture, we must convert it + to the codec pixel format if needed */ + if (img_convert_ctx == NULL) { + img_convert_ctx = sws_getContext(c->width, c->height, + PIX_FMT_YUV420P, + c->width, c->height, + c->pix_fmt, + sws_flags, NULL, NULL, NULL); + if (img_convert_ctx == NULL) { + fprintf(stderr, "Cannot initialize the conversion context\n"); + exit(1); + } + } + fill_yuv_image(tmp_picture, frame_count, c->width, c->height); + sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize, + 0, c->height, picture->data, picture->linesize); + } else { + fill_yuv_image(picture, frame_count, c->width, c->height); + } + } + + + if (oc->oformat->flags & AVFMT_RAWPICTURE) { + /* raw video case. The API will change slightly in the near + futur for that */ + AVPacket pkt; + av_init_packet(&pkt); + + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index= st->index; + pkt.data= (uint8_t *)picture; + pkt.size= sizeof(AVPicture); + + ret = av_interleaved_write_frame(oc, &pkt); + } else { + /* encode the image */ + out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); + /* if zero size, it means the image was buffered */ + if (out_size > 0) { + AVPacket pkt; + av_init_packet(&pkt); + + if (c->coded_frame->pts != AV_NOPTS_VALUE) + pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base); + if(c->coded_frame->key_frame) + pkt.flags |= AV_PKT_FLAG_KEY; + pkt.stream_index= st->index; + pkt.data= video_outbuf; + pkt.size= out_size; + + /* write the compressed frame in the media file */ + ret = av_interleaved_write_frame(oc, &pkt); + } else { + ret = 0; + } + } + if (ret != 0) { + fprintf(stderr, "Error while writing video frame\n"); + exit(1); + } + frame_count++; +} + +static void close_video(AVFormatContext *oc, AVStream *st) +{ + avcodec_close(st->codec); + av_free(picture->data[0]); + av_free(picture); + if (tmp_picture) { + av_free(tmp_picture->data[0]); + av_free(tmp_picture); + } + av_free(video_outbuf); +} + +/**************************************************************/ +/* media file output */ + +int main(int argc, char **argv) +{ + const char *filename; + AVOutputFormat *fmt; + AVFormatContext *oc; + AVStream *audio_st, *video_st; + double audio_pts, video_pts; + int i; + + /* initialize libavcodec, and register all codecs and formats */ + av_register_all(); + + if (argc != 2) { + printf("usage: %s output_file\n" + "API example program to output a media file with libavformat.\n" + "The output format is automatically guessed according to the file extension.\n" + "Raw images can also be output by using '%%d' in the filename\n" + "\n", argv[0]); + exit(1); + } + + filename = argv[1]; + + /* auto detect the output format from the name. default is + mpeg. */ + fmt = av_guess_format(NULL, filename, NULL); + if (!fmt) { + printf("Could not deduce output format from file extension: using MPEG.\n"); + fmt = av_guess_format("mpeg", NULL, NULL); + } + if (!fmt) { + fprintf(stderr, "Could not find suitable output format\n"); + exit(1); + } + + /* allocate the output media context */ + oc = avformat_alloc_context(); + if (!oc) { + fprintf(stderr, "Memory error\n"); + exit(1); + } + oc->oformat = fmt; + snprintf(oc->filename, sizeof(oc->filename), "%s", filename); + + /* add the audio and video streams using the default format codecs + and initialize the codecs */ + video_st = NULL; + audio_st = NULL; + if (fmt->video_codec != CODEC_ID_NONE) { + video_st = add_video_stream(oc, fmt->video_codec); + } + if (fmt->audio_codec != CODEC_ID_NONE) { + audio_st = add_audio_stream(oc, fmt->audio_codec); + } + + /* set the output parameters (must be done even if no + parameters). */ + if (av_set_parameters(oc, NULL) < 0) { + fprintf(stderr, "Invalid output format parameters\n"); + exit(1); + } + + dump_format(oc, 0, filename, 1); + + /* now that all the parameters are set, we can open the audio and + video codecs and allocate the necessary encode buffers */ + if (video_st) + open_video(oc, video_st); + if (audio_st) + open_audio(oc, audio_st); + + /* open the output file, if needed */ + if (!(fmt->flags & AVFMT_NOFILE)) { + if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) { + fprintf(stderr, "Could not open '%s'\n", filename); + exit(1); + } + } + + /* write the stream header, if any */ + av_write_header(oc); + + for(;;) { + /* compute current audio and video time */ + if (audio_st) + audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den; + else + audio_pts = 0.0; + + if (video_st) + video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den; + else + video_pts = 0.0; + + if ((!audio_st || audio_pts >= STREAM_DURATION) && + (!video_st || video_pts >= STREAM_DURATION)) + break; + + /* write interleaved audio and video frames */ + if (!video_st || (video_st && audio_st && audio_pts < video_pts)) { + write_audio_frame(oc, audio_st); + } else { + write_video_frame(oc, video_st); + } + } + + /* write the trailer, if any. the trailer must be written + * before you close the CodecContexts open when you wrote the + * header; otherwise write_trailer may try to use memory that + * was freed on av_codec_close() */ + av_write_trailer(oc); + + /* close each codec */ + if (video_st) + close_video(oc, video_st); + if (audio_st) + close_audio(oc, audio_st); + + /* free the streams */ + for(i = 0; i < oc->nb_streams; i++) { + av_freep(&oc->streams[i]->codec); + av_freep(&oc->streams[i]); + } + + if (!(fmt->flags & AVFMT_NOFILE)) { + /* close the output file */ + url_fclose(oc->pb); + } + + /* free the stream */ + av_free(oc); + + return 0; +} diff --git a/rotord/rotor.cpp b/rotord/rotor.cpp index 07ea2b7..81b0716 100644 --- a/rotord/rotor.cpp +++ b/rotord/rotor.cpp @@ -307,10 +307,12 @@ bool Render_context::load_audio(const string &filename,vectornb_streams; ++i) { if (avFormat->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - // we've found a video stream! + // we've found a stream! stream = avFormat->streams[i]; break; } @@ -332,37 +334,37 @@ bool Render_context::load_audio(const string &filename,vector audioCodec(avcodec_alloc_context3(codec), [](AVCodecContext* c) { avcodec_close(c); av_free(c); }); - /* extradata??? + // extradata??? // we need to make a copy of videoStream->codec->extradata and give it to the context // make sure that this vector exists as long as the avVideoCodec exists - std::vector codecContextExtraData(stream->codec->extradata, stream->codec->extradata + stream->codec->extradata_size); - audioCodec->extradata = reinterpret_cast(codecContextExtraData.data()); + std::vector codecContextExtraData; + codecContextExtraData.assign(stream->codec->extradata, stream->codec->extradata + stream->codec->extradata_size); + + audioCodec->extradata = reinterpret_cast(codecContextExtraData.data()); audioCodec->extradata_size = codecContextExtraData.size(); // initializing the structure by opening the codec - if (avcodec_open2(avVideoCodec.get(), codec, nullptr) < 0) { + if (avcodec_open2(audioCodec.get(), codec, nullptr) < 0) { cerr <<"Rotor: Could not open codec"<< endl; return false; } - */ - //avcodec.h line 1026 + //avcodec.h line 1026 - Packet packet(avFormat.get()); - if (packet.packet.data == nullptr) { - //done - return true; - } - - cerr << "audio codec context - sample rate: "<< audioCodec->sample_rate <<", channels: "<channels<<", sample format: "<sample_fmt<sample_rate <<", channels: "<channels<<", sample format: "<sample_fmt<index) - continue; // the packet is not about the video stream we want, let's jump again the start of the loop + continue; // the packet is not about the stream we want, let's jump again the start of the loop } while(0); + */ // allocating an AVFrame std::shared_ptr avFrame(avcodec_alloc_frame(), &av_free); @@ -401,8 +403,15 @@ bool Render_context::load_audio(const string &filename,vectorsample_fmt) * CHAR_BIT; // 16 or 32 + unsigned int numberOfChannels = stream.codecContext->channels; // 1 for mono, 2 for stereo, or more + unsigned int numberOfSamplesContainedInTheFrame = stream.frame->nb_samples * stream.codecContext->channels; + unsigned int numberOfSamplesToPlayPerSecond = stream.codecContext->sample_rate; // usually 44100 or 48000 + const void* data = stream.frame->data[0]; + */ if (processedLength < 0) { //av_free_packet(&packet); shouldn't have to because of type safe wrapper @@ -410,7 +419,7 @@ bool Render_context::load_audio(const string &filename,vector