C++ ffmpeg decoder
/* | |
* encoder.cpp | |
* | |
* Created on: Jul 12, 2012 | |
* Author : Abhishek Hingnikar | |
* @TODO: remove the file deps. | |
* ADD STREAM DEPS so that u can buffer output. | |
* Structure | |
* @JSAPI | |
* ffmpeg{ | |
* decoder:Constructor | |
* (codec,required_specs{ | |
* Sample_Rate, | |
* etc. | |
* }){ | |
* decode:(buffer toDecode,CallBack AfterDecode); | |
* decodeSync( buffer toDecode); | |
* | |
* }; | |
* | |
* encoder:Constructor | |
* (codec) | |
* { | |
* encode( buffer ToEncode , CallBack AfterEncode ); | |
* encodeSync ( buffer toEncode ) | |
* } | |
* | |
* } | |
* | |
*/ | |
#include<iostream> | |
#include<vector> | |
#define __STDC_CONSTANT_MACROS | |
#ifndef INT64_C | |
#define INT64_C(c) (c ## LL) | |
#define UINT64_C(c) (c ## ULL) | |
#endif | |
# include <stdint.h> | |
# include <inttypes.h> | |
extern"C"{ | |
#include "libavutil/imgutils.h" | |
#include"libavcodec/avcodec.h" | |
#include"libavformat/avformat.h" | |
#include"libavutil/error.h" | |
#include"libavutil/mathematics.h" | |
#include "libavutil/samplefmt.h" | |
} | |
#define INBUF_SIZE 4096 | |
#define AUDIO_INBUF_SIZE 20480 | |
#define AUDIO_REFILL_THRESH 4096 | |
using namespace std; | |
class ffDecFile{ | |
private: | |
AVCodec *codec; | |
AVCodecContext *c; | |
int len; | |
vector<uint8_t> inBuf; | |
AVPacket * avpkt; | |
FILE *f; | |
int finished; | |
AVFrame *decoded_frame; | |
void cleanup(){ | |
if(this->c){ | |
avcodec_close(this->c); | |
av_free(this->c); | |
av_free(this->decoded_frame); | |
} | |
if(this->f){ | |
fclose(this->f); | |
} | |
} | |
int decode(){ | |
/* Decode a part */ | |
if( this->avpkt->size > 0 ){ | |
int got_packet = 0; | |
if(!this->decoded_frame){ | |
if(!(this->decoded_frame = avcodec_alloc_frame())){ | |
cout<<"\n Failed to allocate memory ... Add more ram add more ram!"; | |
return -4; | |
} | |
}else | |
avcodec_get_frame_defaults(this->decoded_frame); | |
this->len = avcodec_decode_audio4(this->c,this->decoded_frame,&got_packet,this->avpkt); | |
if(this->len < 0 ){ | |
cout<<"\n Error Reading packet"; | |
return -2; | |
} | |
this->avpkt->size -= len; | |
this->avpkt->data += len; | |
this->avpkt->pts = this->avpkt->dts = AV_NOPTS_VALUE; | |
/* Prepare for net routine */ | |
if(this->avpkt->size < AUDIO_REFILL_THRESH){ | |
memmove(this->inBuf.data(),this->avpkt->data,this->avpkt->size); | |
this->avpkt->data = this->inBuf.data(); | |
this->len = fread(this->avpkt->data+this->avpkt->size,1,AUDIO_INBUF_SIZE - this->avpkt->size,f); | |
if( this->len > 0){ | |
this->avpkt->size += len; | |
} | |
} | |
return 0; | |
} | |
return 1; // If finished. | |
} | |
public: | |
int init(char *file,CodecID codec_enum){ | |
/* Initialize defs */ | |
this->finished = 0; | |
this->c = new AVCodecContext; | |
this->len = 0; | |
this->f = NULL; | |
this->avpkt = new AVPacket; | |
this->decoded_frame = new AVFrame; | |
this->inBuf.resize(AUDIO_INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); | |
av_init_packet(this->avpkt); | |
this->codec = avcodec_find_decoder(codec_enum); | |
if(!this->codec){ | |
cout<<"\n Can't find the codec"; | |
this->cleanup(); | |
return -1; | |
} | |
this->c = avcodec_alloc_context3(this->codec); | |
if( avcodec_open2(this->c,this->codec,NULL)< 0) | |
{ | |
cout<<"\n Error opening codec"; | |
this->cleanup(); | |
return -2; | |
}; | |
f = fopen(file,"rb"); | |
if(!f){ | |
cout<<"\n Error opening file"; | |
this->cleanup(); | |
return -3; | |
} | |
/* Initial read */ | |
this->avpkt->size = fread(this->inBuf.data(),1,AUDIO_INBUF_SIZE,f); | |
this->avpkt->data = inBuf.data(); | |
return 0; | |
} | |
AVCodecContext * getContext(){ | |
return this->c; | |
} | |
AVFrame * getDecodedFrame(){ | |
int state = this->decode(); | |
if(!finished) | |
if( state == 1 || state == 0){ | |
if(state) | |
this->finished = 1; | |
return (this->decoded_frame); | |
}else{ | |
this->cleanup(); | |
} | |
return NULL; | |
} | |
}; | |
struct codecData{ | |
int bitRate; | |
int sampleRate; | |
int channels; | |
enum AVSampleFormat sample_fmt; | |
}; | |
class ffEnc{ | |
AVCodec *codec; | |
AVCodecContext *c= NULL; | |
AVFrame * frame; | |
AVPacket * encoded_packet; | |
int frame_size, i, j, out_size; | |
void cleanup(){ | |
if(this->c){ | |
avcodec_close(this->c); | |
av_free(this->c); | |
} | |
} | |
/* | |
* Private function that actually encodes and returns the stuff. | |
*/ | |
int encode(){ | |
int got_packet = 0; | |
this->out_size = avcodec_encode_audio2(this->c,this->encoded_packet,this->frame,&got_packet); | |
if( out_size < 0 ){ | |
return -2; // known error in this case. Will be logged by Libavcodec.. hopefully wont mess with node.js | |
} | |
if( got_packet < 0 ){ | |
cout<<"Contact a Plumber!!!! \n we have got a leakage , and its unknown!!"; | |
return -4; // Unknown leakage get ur seals ready to fix this . | |
} | |
// Success | |
return 0; | |
} | |
public: | |
/* | |
* @Public Api | |
* Constructor for the encoder class | |
* and binding source for the node.js | |
* though node.js will use different parameters | |
* TODO: Change the parameters to v8 based JS objects that come from JavaScript. | |
*/ | |
int init(char* fileName,CodecID codec_id,codecData defs){ | |
this->codec = new AVCodec; | |
this->frame = new AVFrame; | |
this->encoded_packet = new AVPacket; | |
this->encoded_packet->data = NULL; | |
this->codec = avcodec_find_encoder(codec_id); | |
if(!codec){ | |
cout<<"Error opening codec"; | |
this->cleanup(); | |
return -1; | |
} | |
this->c = avcodec_alloc_context3(this->codec); | |
this->c->sample_fmt = defs.sample_fmt; | |
this->c->bit_rate = defs.bitRate; | |
this->c->channels = defs.channels; | |
this->c->sample_rate= defs.sampleRate; | |
/* Open the encoder */ | |
if( avcodec_open2(this->c,this->codec,NULL)< 0){ | |
cout<<"Error opening the codec"; | |
this->cleanup(); | |
return -2; | |
} | |
this->frame_size = this->c->frame_size; | |
// Success. | |
return 0; | |
} | |
/* | |
* @Public API for encoding and also to be binded to node.js | |
*/ | |
AVPacket * getEncodedPacket( AVFrame * toEncode){ | |
// toEncode is the frame to encode . in node.js this will be ported to buffer to encode. | |
this->frame = toEncode; | |
int state = this->encode(); | |
if(state != 0){ | |
return NULL; | |
}else{ | |
return this->encoded_packet; | |
} | |
} | |
}; | |
int main(){ | |
av_register_all(); | |
ffDecFile* ffmpegd = new ffDecFile() ; | |
ffEnc * ffmpege = new ffEnc(); | |
FILE * outFile; | |
AVPacket * out_pack = new AVPacket; | |
outFile = fopen("ghoo.mp3","wb+"); | |
cout<<"\n Worked"; | |
if(ffmpegd->init("buu.mp3",CODEC_ID_MP3)==0){ | |
cout<<"\n File loaded"; | |
} | |
codecData defs; | |
AVFrame * outFrame = NULL; | |
outFrame = ffmpegd->getDecodedFrame(); | |
if(outFrame){ | |
AVCodecContext * dec_con = ffmpegd->getContext(); | |
defs.sample_fmt = dec_con->sample_fmt;// Findout whats our decoder is speaking | |
defs.channels = dec_con->channels; // now start making our encoder speak the same | |
defs.sampleRate = dec_con->sample_rate; // and bing we are good to go. | |
defs.bitRate = 64000; | |
ffmpege->init("ghoo.mp3",CODEC_ID_MP3,defs); | |
out_pack = ffmpege->getEncodedPacket(outFrame); | |
if(out_pack != NULL) | |
fwrite(out_pack->data, 1, out_pack->size, outFile); | |
else{ | |
fclose(outFile); | |
cout<<"No encoded frame , Call Plumber this is a major leak!"; | |
return -2; | |
} | |
}else{ | |
cout<<"\n Unknown error occurred please consult Dr.Debugger."; | |
return -1; | |
} | |
float * samples = NULL; | |
long int total_samp = 0; | |
while(outFrame != NULL){ | |
/* Do some cool stuff with data */ | |
outFrame = ffmpegd->getDecodedFrame(); | |
out_pack = ffmpege->getEncodedPacket(outFrame); | |
if(out_pack != NULL) | |
fwrite(out_pack->data, 1, out_pack->size, outFile); | |
else{ | |
fclose(outFile); | |
cout<<"No encoded frame , Call Plumber this is a major leak!"; | |
return -2; | |
} | |
} | |
cout<<"\n Finished Reading file"; | |
cout<<"\n File writing complete"; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment