Extending Media Foundation Encoder to support 10-bit video encoding - winapi

The HEVC Media Foundation Encoder in Windows will only encode 8-bit video. My GFX NVidia card also supports 10-bit HDR and alpha-mode video encoding, so I decided to create my own IMFTransform to use the NVidia SDK.
I 've registered my DLL using MFTRegister:
MFT_REGISTER_TYPE_INFO aMediaTypesIn[] =
{
{ MFMediaType_Video, MFVideoFormat_ARGB32 },
{ MFMediaType_Video, MFVideoFormat_RGB32 },
{ MFMediaType_Video, MFVideoFormat_RGB32 },
{ MFMediaType_Video, MFVideoFormat_RGB10 },
{ MFMediaType_Video, MyFakeFmt },
};
MFT_REGISTER_TYPE_INFO aMediaTypesOut[] =
{
{ MFMediaType_Video, MFVideoFormat_H264 },
{ MFMediaType_Video, MFVideoFormat_H265 },
{ MFMediaType_Video, MFVideoFormat_HEVC },
};
// Size of the array.
const DWORD cNumMediaTypesI = ARRAY_SIZE(aMediaTypesIn);
const DWORD cNumMediaTypesO = ARRAY_SIZE(aMediaTypesOut);
hr = MFTRegister(
GUID_NVidiaEncoder, // CLSID.
MFT_CATEGORY_VIDEO_ENCODER, // Category.
The MyFakeFmt is a non existing input type to fool the Sink Writer to pick my encoder when calling SetInputMediaType instead of the predefined Microsoft's transform. This works OK.
int wi = 1920;
int he = 1080;
int fps = 30;
int br = 4000;
auto fmt = MFVideoFormat_H264;
bool Our = 1;
const wchar_t* fil = L"r:\\1.mp4";
std::vector<DWORD> frame;
frame.resize(wi * he);
// Test
CComPtr<IMFSinkWriter> wr;
DeleteFile(fil);
CComPtr<IMFAttributes> attrs;
MFCreateAttributes(&attrs, 0);
auto hr = MFCreateSinkWriterFromURL(fil, 0, attrs, &wr);
DWORD str = (DWORD)-1;
CComPtr<IMFMediaType> mt2;
MFCreateMediaType(&mt2);
mt2->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
mt2->SetGUID(MF_MT_SUBTYPE, fmt);
MFSetAttributeRatio(mt2, MF_MT_FRAME_RATE, fps, 1);
hr = MFSetAttributeSize(mt2, MF_MT_FRAME_SIZE,wi, he);
MFSetAttributeRatio(mt2, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
mt2->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
mt2->SetUINT32(MF_MT_VIDEO_NOMINAL_RANGE, MFNominalRange_Normal);
mt2->SetUINT32(MF_MT_AVG_BITRATE, br*1000);
hr = wr->AddStream(mt2, &str);
CComPtr<IMFMediaType> mt1;
MFCreateMediaType(&mt1);
mt1->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
mt1->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
hr = MFSetAttributeSize(mt1, MF_MT_FRAME_SIZE, wi, he);
// Force our selection
if (Our)
{
mt1->SetGUID(MF_MT_SUBTYPE, MyFakeFmt);
hr = wr->SetInputMediaType(str, mt1, 0);
}
mt1->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_ARGB32);
hr = wr->SetInputMediaType(str, mt1, 0);
hr = wr->BeginWriting();
for(int i = 0 ; i < 15 ; i++)
{
auto i2 = i % 5;
if (i2 == 0) Frm(frame, wi, he, 0xFFFFFFFF);
if (i2 == 1 || i2 == 4) Frm(frame, wi, he, 0xFF0000FF); // some colors
if (i2 == 2) Frm(frame, wi, he, 0xFFFF00FF); //
if (i2 == 3) Frm(frame, wi, he, 0xFF00FFFF); //
CComPtr<IMFSample> s;
MFCreateSample(&s);
int secs = 1;
hr = s->SetSampleDuration(10 * 1000 * 1000 * secs);
hr = s->SetSampleTime(10 * 1000 * 1000 * i);
CComPtr<IMFMediaBuffer> b;
MFCreateMemoryBuffer((DWORD)(frame.size() * 4), &b);
b->SetCurrentLength((DWORD)(frame.size() * 4));
BYTE* by = 0;
DWORD ml = 0, cl = 0;
b->Lock(&by, &ml, &cl);
memcpy(by, frame.data(), frame.size() * 4);
b->Unlock();
hr = s->AddBuffer(b);
b = 0;
hr = wr->WriteSample(str, s);
}
hr = wr->Finalize();
wr = 0;
The problems start with the call to Finalize to end the writing. At that point, everything seems to work normally. Note that I have tested the NVidia IMFTransform I 've created with input frames and it encodes and outputs them correctly as raw data.
When I call Finalize and the type is MFVideoFormat_H264 , the call succeeds. However the generated mp4 plays weirdly:
For some reason also, MediaInfo shows 1 FPS. Why?
When the output is MFVideoFormat_HEVC, then Finalize fails with `0xc00d4a45 : Sink could not create valid output file because required headers were not provided to the sink.'.
I 've also tried to convert the raw .h264 file I 'm saving with ffmpeg to mp4, and this works. The mp4 generated plays correctly.
Adding a MF_MT_MPEG_SEQUENCE_HEADER didn't help (besides, I think this is only needed for H.264)
const char* bl4 = "\x00\x00\x00\x01\x67\x42\xC0\x28\x95\xB0\x1E\x00\x89\xF9\x70\x16\xC8\x00\x00\x03\x00\x08\x00\x00\x03\x01\xE0\x6D\x04\x42\x37\x00\x00\x00\x01\x68\xCA\x8F\x20";
mt2->SetBlob(MF_MT_MPEG_SEQUENCE_HEADER, (UINT8*)bl4, 39);
What do you make of all that?
Thanks

Related

MP4 output from h264 has invalid framerate

I am using ffmpeg as a library (libavcodec, libavformat) in my C++ application to put a raw h264 stream (from a camera) into an mp4 container. Essentially I am trying to mimic the behavior of the following call to the ffmpeg executable: ffmpeg -framerate 30 -i camera.h264 -c copy -f mp4 -movflags frag_keyframe+empty_moov -
The code below works more or less and there are no warnings or errors from libavformat/libavcodec printed to stderr. It seems to create an acceptable mp4 file, however all players fail to show it. VLC shows the first frame or so a bit distorted and then stops, other players complain, that the file has an invalid format. File size from ffmpeg and my code slightly differ.
I create the input stream like this (every call is checked in the real code but for simplicity I removed the error handling):
_io_buffer_size = getpagesize();
_io_buffer = std::shared_ptr<unsigned char>((unsigned char *) av_malloc(_io_buffer_size), [](unsigned char *ptr) { av_free(ptr); });
_io_context = std::shared_ptr<AVIOContext>(
avio_alloc_context(_io_buffer.get(), _io_buffer_size, 0, this, H264Stream::on_read_buffer, nullptr, nullptr),
[](AVIOContext *ctx) { av_free(ctx); }
);
_format_context = std::shared_ptr<AVFormatContext>(
avformat_alloc_context(),
[](AVFormatContext *ctx) { avformat_free_context(ctx); }
);
const auto h264_input_format = av_find_input_format("h264");
_format_context->pb = _io_context.get();
std::stringstream fps_stream;
std::stringstream size_stream;
fps_stream << fps;
size_stream << width << "x" << height;
AVDictionary *input_options = nullptr;
av_dict_set(&input_options, "framerate", fps_stream.str().c_str(), 0);
av_dict_set(&input_options, "r", fps_stream.str().c_str(), 0);
av_dict_set(&input_options, "s", size_stream.str().c_str(), 0);
auto formatPtr = _format_context.get();
auto res = avformat_open_input(&formatPtr, "(memory file)", h264_input_format, &input_options);
AVCodec* decoder = nullptr;
res = av_find_best_stream(formatPtr, AVMEDIA_TYPE_VIDEO, 0, -1, &decoder, 0);
And the output format context is created like this:
_io_buffer_size = getpagesize();
_io_buffer = std::shared_ptr<unsigned char>((unsigned char *) av_malloc(_io_buffer_size), [](unsigned char *ptr) { av_free(ptr); });
_io_context = std::shared_ptr<AVIOContext>(
avio_alloc_context(_io_buffer.get(), _io_buffer_size, 1, this, nullptr, H264Conversion::on_write_data, nullptr),
[](AVIOContext *ctx) { av_free(ctx); }
);
auto output_format = av_guess_format(output_extension.c_str(), nullptr, nullptr);
AVFormatContext* format_context = nullptr;
const auto rc = avformat_alloc_output_context2(&format_context, output_format, nullptr, nullptr);
const auto codec = avcodec_find_encoder(AV_CODEC_ID_H264);
log->info("Output format: {}", codec->name);
auto video_stream = avformat_new_stream(format_context, codec);
video_stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
video_stream->codecpar->width = _stream->width();
video_stream->codecpar->height = _stream->height();
video_stream->codecpar->codec_id = AV_CODEC_ID_H264;
AVStream** streams = new AVStream*[1];
streams[0] = video_stream;
format_context->nb_streams = 1;
format_context->streams = streams;
_format_context = std::shared_ptr<AVFormatContext>(format_context, [](AVFormatContext* ctx) { avformat_free_context(ctx); });
_format_context->pb = _io_context.get();
_converter_thread = std::thread{[=]() { process_conversion(); }};
The method process_conversion essentially performs a read packet -> write packet loop that looks like this:
AVDictionary* dict = nullptr;
av_dict_set(&dict, "movflags", "frag_keyframe+empty_moov", 0);
av_dict_set(&dict, "r", "30", 0);
av_dict_set(&dict, "framerate", "30", 0);
auto rc = avformat_write_header(_format_context.get(), &dict);
av_dict_free(&dict);
auto did_complete_regularly = false;
std::shared_ptr<AVPacket> packet = std::shared_ptr<AVPacket>(av_packet_alloc(), [](AVPacket *packet) { av_packet_free(&packet); });
while(_is_running) {
if (!_stream->read_next_packet(*packet.get())) {
did_complete_regularly = true;
break;
}
av_write_frame(_format_context.get(), packet.get());
av_packet_unref(packet.get());
}
if(did_complete_regularly) {
av_write_trailer(_format_context.get());
}

Convert from NV12 to RGB/YUV420P using libswscale

I'm developing an application which needs to transform NV12 frames from h264_cuvid decoder to RGB in order to modify those frames. I checked this question but I don't not the 'Stride' value.
My code is the following:
uint8_t *inData[2] = { videoFrame->data[0], videoFrame->data[0] + videoFrame->width * videoFrame->height };
int inLinesize[2] = { videoFrame->width, videoFrame->width };
sws_scale(convert_yuv_to_rgb, inData, inLinesize, 0, videoFrame->height, aux_frame->data, aux_frame->linesize);
But it does not work. Although the problem is on colours because I can see the luminance plane correctly.
I ended up using a video filter based on this example.
char args[512];
int ret;
AVFilter *buffersrc = avfilter_get_by_name("buffer");
AVFilter *buffersink = avfilter_get_by_name("buffersink");
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
AVFilterGraph *filter_graph = avfilter_graph_alloc();
AVBufferSinkParams *buffersink_params;
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE };
/* buffer video source: the decoded frames from the decoder will be inserted here. */
snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
inStream.width, inStream.height, inStream.pix_fmt,
inStream.time_base.num, inStream.time_base.den,
inStream.sample_aspect_ratio.num, inStream.sample_aspect_ratio.den);
ret = avfilter_graph_create_filter(&buffersrc_ctx_to_rgb_, buffersrc, "in", args, NULL, filter_graph);
if (ret < 0) {
throw SVSException(QString("Could not create filter graph, error: %1").arg(svsAvErrorToFormattedString(ret)));
}
/* buffer video sink: to terminate the filter chain. */
buffersink_params = av_buffersink_params_alloc();
buffersink_params->pixel_fmts = pix_fmts;
ret = avfilter_graph_create_filter(&buffersink_ctx_to_rgb_, buffersink, "out", NULL, buffersink_params, filter_graph);
if (ret < 0) {
throw SVSException(QString("Cannot create buffer sink, error: %1").arg(svsAvErrorToFormattedString(ret)));
}
/* Endpoints for the filter graph. */
outputs -> name = av_strdup("in");
outputs -> filter_ctx = buffersrc_ctx_to_rgb_;
outputs -> pad_idx = 0;
outputs -> next = NULL;
/* Endpoints for the filter graph. */
inputs -> name = av_strdup("out");
inputs -> filter_ctx = buffersink_ctx_to_rgb_;
inputs -> pad_idx = 0;
inputs -> next = NULL;
QString filter_description = "format=pix_fmts=rgb32";
if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_description.toStdString().c_str(), &inputs, &outputs, NULL)) < 0) {
svsCritical("", QString("Could not add the filter to graph, error: %1").arg(svsAvErrorToFormattedString(ret)))
}
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) {
svsCritical("", QString("Could not configure the graph, error: %1").arg(svsAvErrorToFormattedString(ret)))
}
return;
I created another one to convert from RGB to YUV420P before encoding in a similar way.

AV_PIX_FMT_YUVJ422P to jpeg conversion

i am able to convert image from AV_PIX_FMT_YUVJ422P to jpeg format (below Code) but the resultant image having green shade on complete bottom half plz suggest where i am doing wrong.
Following step i have taken
Initially i have AV_PIX_FMT_UYVY422 image from camera, i have convert it in AV_PIX_FMT_YUVJ422P format and able to see this image on http://rawpixels.net/ the parameters shown by website is size 2448X2050, Bpp1= 8,Bpp2 = 8 and Bpp3 = 8,alignment 1, SubSampling H =2, and SubSampling V = 1, format: YUV422P
so input image is Correct AV_PIX_FMT_YUVJ422P format. & also able to see on "YUV image viewer Software" using YUV422 format.
Now i am trying to convert it in jpeg format using below Code and attached is the resultant Image having green shade on complete bottom half.
AVFormatContext* pFormatCtx;
AVOutputFormat* fmt;
AVStream* video_st;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
uint8_t* picture_buf;
AVFrame* picture;
AVPacket pkt;
int y_size;
int size;
int got_picture=0;
int ret=0;
int main( int argc, char* argv[] )
{
FILE *in_file = NULL;
unsigned int in_width = 2448;
unsigned int in_height = 2050;
const char* out_file = "encoded_pic.jpg";
in_file = fopen("c:\\test_Planar.yuv","rb");
if(in_file == NULL) { printf("\n\tFile Opening error...!!"); exit(1); }
else printf("\n\tYUV File Open Sucessfully...!!\n\n");
av_register_all(); // Loads the whole database of available codecs and formats.
pFormatCtx = avformat_alloc_context();
fmt = NULL;
fmt = av_guess_format("mjpeg",NULL,NULL);
pFormatCtx->oformat = fmt;
//------Output URL-------------------------
if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0)
{
printf("Couldn't open output file.");
return -1;
}
video_st = avformat_new_stream(pFormatCtx, 0);
if (video_st==NULL) return -1;
pCodecCtx = video_st->codec;
pCodecCtx->codec_id = fmt->video_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ422P;
//--------------------------MY SOURCE PIXEL FORMAT--------------
pCodecCtx->width = in_width;
pCodecCtx->height = in_height;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 1;//25;
//Output some information
av_dump_format(pFormatCtx, 0, out_file, 1);
// Determine if desired video encoder is installed
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec)
{
printf("Codec not found.");
return -1;
}
printf("\nCodec Identified done\n");
if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){
printf("Could not open codec.\n");
return -1;
}
picture = av_frame_alloc();
size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
picture_buf = (uint8_t *)av_malloc(size);
if (!picture_buf) return -1;
avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
printf("\t\nWrite Header..");
avformat_write_header(pFormatCtx,NULL);
y_size = pCodecCtx->width * pCodecCtx->height;
av_new_packet(&pkt,y_size*3);
//Read YUV
if (fread(picture_buf, 1, y_size*3/2, in_file) <=0)
{
printf("Could not read input file.");
return -1;
}
//--------------------------------------------input image format UYVY
picture->data[0] = picture_buf; // Y
picture->data[1] = picture_buf+ y_size; // U
picture->data[2] = picture_buf+ y_size*5/4; // V
//-----------------------------------------------
printf("\t\n Encode the image..\n");
ret = avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);
if(ret < 0)
{
printf("Encode Error.\n");
return -1;
}
if (got_picture==1)
{
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
}
av_free_packet(&pkt);
//Write Trailer
av_write_trailer(pFormatCtx);
printf("Encode Successful.\n");
if (video_st)
{
avcodec_close(video_st->codec);
av_free(picture);
av_free(picture_buf);
}
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
fclose(in_file);
printf("\n\tYUV File Close Sucessfully...!!");
}
Resultant output jpeg encoded image from yuvj422p image having green shade
Changing the input Pixel format from AV_PIX_FMT_YUVJ422P to AV_PIX_FMT_YUVJ420P resolve the issue.

Recording audio in wav format on windows

I'm trying to record audio in a wav format on windows. However the recorded bytes are always equal to zero. Am I misinterpreting somethings from the windows API?
void RecordSound(void)
{
static int ChannelOn = 0;
int err;
int fp;
static WAVEFORMATEX fmt;
static WAVEHDR hdr;
HWAVEIN hwi = 1;
fmt.nSamplesPerSec = 44100;
fmt.wBitsPerSample = 16;
fmt.wFormatTag= WAVE_FORMAT_PCM;
fmt.nChannels= 2;
fmt.nBlockAlign = fmt.wBitsPerSample*fmt.nChannels/8;
fmt.nAvgBytesPerSec= fmt.nSamplesPerSec*fmt.nBlockAlign;
hdr.lpData = (LPSTR)buf;
hdr.dwBufferLength = BUF_SIZE;
if(!ChannelOn) {
err = waveInOpen(&hwi, 0, &fmt, 0, 0, 0);
printf("\nerr = %x\n", err);
if(!err) {
waveInAddBuffer(hwi, &hdr, 0);
waveInStart(hwi);
puts("Record channel on\n");
ChannelOn = 1;
}
} else {
waveInClose(hwi);
puts("Record channel off\n");
ChannelOn = 0;
puts("Writing wav to test.wav");
fp = open("test.wav" , "w");
write(fp, &fmt, sizeof(WAVEFORMATEX));
write(fp, &buf, hdr.dwBytesRecorded);
memset(&hdr, 0 , sizeof(WAVEHDR));
memset(&fmt, 0 , sizeof(WAVEFORMATEX));
}
}

How to get picture buffer data in ffmpeg?

I'm trying to pass bitmap from ffmpeg to android.
It already works but it's displaying picture right on surface passed from java to native code.
How can i get frame buffer bitmap data to pass it to java?
I've tried to save out_frame buffer data:
unsigned char bmpFileHeader[14] = {'B', 'M', 0,0,0,0, 0,0, 0,0, 54, 0,0,0};
unsigned char bmpInfoHeader[40] = {40,0,0,0, 0,0,0,0, 0,0,0,0, 1,0, 24,0};
unsigned char bmpPad[3] = {0, 0, 0};
void saveBuffer(int fileIndex, int width, int height, unsigned char *buffer, int buffer_size) {
unsigned char filename[1024];
sprintf(filename, "/storage/sdcard0/3d_player_%d.bmp", fileIndex);
LOGI(10, "saving ffmpeg bitmap file: %d to %s", fileIndex, filename);
FILE *bitmapFile = fopen(filename, "wb");
if (!bitmapFile) {
LOGE(10, "failed to create ffmpeg bitmap file");
return;
}
unsigned char filesize = 54 + 3 * width * height; // 3 = (r,g,b)
bmpFileHeader[2] = (unsigned char)(filesize);
bmpFileHeader[3] = (unsigned char)(filesize >> 8);
bmpFileHeader[4] = (unsigned char)(filesize >> 16);
bmpFileHeader[5] = (unsigned char)(filesize >> 24);
bmpInfoHeader[4] = (unsigned char)(width);
bmpInfoHeader[5] = (unsigned char)(width >> 8);
bmpInfoHeader[6] = (unsigned char)(width >> 16);
bmpInfoHeader[7] = (unsigned char)(width >> 24);
bmpInfoHeader[8] = (unsigned char)(height);
bmpInfoHeader[9] = (unsigned char)(height >> 8);
bmpInfoHeader[10] = (unsigned char)(height >> 16);
bmpInfoHeader[11] = (unsigned char)(height >> 24);
fwrite(bmpFileHeader, 1, 14, bitmapFile);
fwrite(bmpInfoHeader, 1, 40, bitmapFile);
int i;
for (i=0; i<height; i++) {
fwrite(buffer + width * (height - 1) * 3, 3, width, bitmapFile);
fwrite(bmpPad, 1, (4-(width * 3) % 4) % 4, bitmapFile);
}
fflush(bitmapFile);
fclose(bitmapFile);
}
int player_decode_video(struct DecoderData * decoder_data, JNIEnv * env,
struct PacketData *packet_data) {
int got_frame_ptr;
struct Player *player = decoder_data->player;
int stream_no = decoder_data->stream_no;
AVCodecContext * ctx = player->input_codec_ctxs[stream_no];
AVFrame * frame = player->input_frames[stream_no];
AVStream * stream = player->input_streams[stream_no];
int interrupt_ret;
int to_write;
int err = 0;
AVFrame *rgb_frame = player->rgb_frame;
ANativeWindow_Buffer buffer;
ANativeWindow * window;
#ifdef MEASURE_TIME
struct timespec timespec1, timespec2, diff;
#endif // MEASURE_TIME
LOGI(10, "player_decode_video decoding");
int frameFinished;
#ifdef MEASURE_TIME
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timespec1);
#endif // MEASURE_TIME
int ret = avcodec_decode_video2(ctx, frame, &frameFinished,
packet_data->packet);
#ifdef MEASURE_TIME
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timespec2);
diff = timespec_diff(timespec1, timespec2);
LOGI(3, "decode_video timediff: %d.%9ld", diff.tv_sec, diff.tv_nsec);
#endif // MEASURE_TIME
if (ret < 0) {
LOGE(1, "player_decode_video Fail decoding video %d\n", ret);
return -ERROR_WHILE_DECODING_VIDEO;
}
if (!frameFinished) {
LOGI(10, "player_decode_video Video frame not finished\n");
return 0;
}
// saving in buffer converted video frame
LOGI(7, "player_decode_video copy wait");
#ifdef MEASURE_TIME
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timespec1);
#endif // MEASURE_TIME
pthread_mutex_lock(&player->mutex_queue);
window = player->window;
if (window == NULL) {
pthread_mutex_unlock(&player->mutex_queue);
goto skip_frame;
}
ANativeWindow_setBuffersGeometry(window, ctx->width, ctx->height,
WINDOW_FORMAT_RGBA_8888);
if (ANativeWindow_lock(window, &buffer, NULL) != 0) {
pthread_mutex_unlock(&player->mutex_queue);
goto skip_frame;
}
pthread_mutex_unlock(&player->mutex_queue);
int format = buffer.format;
if (format < 0) {
LOGE(1, "Could not get window format")
}
enum PixelFormat out_format;
if (format == WINDOW_FORMAT_RGBA_8888) {
out_format = PIX_FMT_RGBA;
LOGI(6, "Format: WINDOW_FORMAT_RGBA_8888");
} else if (format == WINDOW_FORMAT_RGBX_8888) {
out_format = PIX_FMT_RGB0;
LOGE(1, "Format: WINDOW_FORMAT_RGBX_8888 (not supported)");
} else if (format == WINDOW_FORMAT_RGB_565) {
out_format = PIX_FMT_RGB565;
LOGE(1, "Format: WINDOW_FORMAT_RGB_565 (not supported)");
} else {
LOGE(1, "Unknown window format");
}
avpicture_fill((AVPicture *) rgb_frame, buffer.bits, out_format,
buffer.width, buffer.height);
rgb_frame->data[0] = buffer.bits;
if (format == WINDOW_FORMAT_RGBA_8888) {
rgb_frame->linesize[0] = buffer.stride * 4;
} else {
LOGE(1, "Unknown window format");
}
LOGI(6,
"Buffer: width: %d, height: %d, stride: %d",
buffer.width, buffer.height, buffer.stride);
int i = 0;
#ifdef MEASURE_TIME
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timespec2);
diff = timespec_diff(timespec1, timespec2);
LOGI(1,
"lockPixels and fillimage timediff: %d.%9ld", diff.tv_sec, diff.tv_nsec);
#endif // MEASURE_TIME
#ifdef MEASURE_TIME
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &timespec1);
#endif // MEASURE_TIME
LOGI(7, "player_decode_video copying...");
AVFrame * out_frame;
int rescale;
if (ctx->width == buffer.width && ctx->height == buffer.height) {
// This always should be true
out_frame = rgb_frame;
rescale = FALSE;
} else {
out_frame = player->tmp_frame2;
rescale = TRUE;
}
if (ctx->pix_fmt == PIX_FMT_YUV420P) {
__I420ToARGB(frame->data[0], frame->linesize[0], frame->data[2],
frame->linesize[2], frame->data[1], frame->linesize[1],
out_frame->data[0], out_frame->linesize[0], ctx->width,
ctx->height);
} else if (ctx->pix_fmt == PIX_FMT_NV12) {
__NV21ToARGB(frame->data[0], frame->linesize[0], frame->data[1],
frame->linesize[1], out_frame->data[0], out_frame->linesize[0],
ctx->width, ctx->height);
} else {
LOGI(3, "Using slow conversion: %d ", ctx->pix_fmt);
struct SwsContext *sws_context = player->sws_context;
sws_context = sws_getCachedContext(sws_context, ctx->width, ctx->height,
ctx->pix_fmt, ctx->width, ctx->height, out_format,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
player->sws_context = sws_context;
if (sws_context == NULL) {
LOGE(1, "could not initialize conversion context from: %d"
", to :%d\n", ctx->pix_fmt, out_format);
// TODO some error
}
sws_scale(sws_context, (const uint8_t * const *) frame->data,
frame->linesize, 0, ctx->height, out_frame->data,
out_frame->linesize);
}
if (rescale) {
// Never occurs
__ARGBScale(out_frame->data[0], out_frame->linesize[0], ctx->width,
ctx->height, rgb_frame->data[0], rgb_frame->linesize[0],
buffer.width, buffer.height, __kFilterNone);
out_frame = rgb_frame;
}
// TODO: (4ntoine) frame decoded and rescaled, ready to call callback with frame picture from buffer
int bufferSize = buffer.width * buffer.height * 3; // 3 = (r,g,b);
static int bitmapCounter = 0;
if (bitmapCounter < 10) {
saveBuffer(bitmapCounter++, buffer.width, buffer.height, (unsigned char *)out_frame->data, bufferSize);
}
but out_frame is empty and file has header and 0x00 bytes body.
How to get picture buffer data in ffmpeg?
Solved, in short: you should take buffer from ANativeWindow_Buffer - buffer.bits. Pay attention buffer is (rgba) but BMP is usually (rgb) - 3 bytes. To save it as BMP one need to add BMP header and save lines with padding.

Resources