Moby Dick深海ロボットから送信されたビデオ信号を表示および記録するためのソフトウェアを開発した経験をコミュニティと共有したいと思います。 開発は
、クジラ水中ロボット研究所によって委託されました。 プロジェクトは以下を提供するように設計されました。
-RTSPプロトコルをサポートするIPカメラで動作します。
-複数のIPカメラからのビデオの表示と記録。
-2台の専用IPカメラからのステレオビデオの表示と記録。
-画面からのビデオ録画。
-データ転送速度の短期的な低下を伴うビデオの快適な視聴。

ロシアの
VKSトランスポンダー
「ムーンレインボー」のボードから着陸したプロジェクト1-0-1深海ロボットMoby Dickは、
ヨーロッパの海を探索します(アーティスト、コラージュによる)
コードのメガトンを恐れていない場合は、猫にようこそ。
モビーディック
Moby Dickは、水中ロボット
The Whaleの研究室で開発された遠隔操作の
水中車両 (水中遠隔操作車両(水中ROVまたはUROV))です。 Moby Dickの設計と作成の主な目標は、世界の海の最大深度(将来)に潜水可能な小型ロボットを作成し(リフティングおよびトリガーメカニズムの一部として)、高解像度ビデオ(Full HD)を配信し、サンプル、サンプルを採取し、救助活動を実施することでした。

正面のMoby Dick深海ロボット(シリンダーの下の上部に2台のフロントカメラが見える)

背面のMoby Dick深海ロボット(ケーブルマウントの下の下部カメラが見えます)
原則として、3台のデジタルビデオカメラがMoby Dickに取り付けられており、2台は3つの軸すべてで360°回転する回転サスペンションに取り付けられています。 3つの軸すべてで360°回転することで、機械のエンジンとフレームの周囲および内部の一般的な状況を検査でき、カメラがロボットの動きに対して垂直に配置されている場合は、長いオブジェクト(パイプライン、船など)をすばやく検査できます また、この機能を使用すると、海底の立体画像を取得でき、モニター画面からオブジェクトまでの距離とサイズを直接推定することが可能になります。
球体全体の概要を受け取った後、合計9台のデジタルカメラをMoby Dickにインストールできます。 確かに、最大構成では、1台のカメラのみがフルHDで満足する必要があります-ケーブル上のデータ転送速度の制限により、残りは720pとして接続する必要があります。
これは、私たちの国でステレオビデオを送信できる唯一の水中ロボットであることに注意してください。 同様の機能を備えたロボットは海外でも入手可能ですが、Moby Dickと比較すると非常に大きなサイズです。
ビデオを受信して記録する
ビデオを受信して記録するために、
FFMPEGライブラリが選択されました。 開発者を特定のFFMPEG APIから分離するために、DLL中間体が使用されます(古き良きMicrosoft Visual C ++ 2008 Express Editionでコンパイルされています)。 彼のコードの最も興味深い部分を検討してください。
このコードは、高精度のタイムスタンプを取得するために使用されます。
static bool timer_supported;
static LARGE_INTEGER timer_f;
static bool timer_init(void)
{
return timer_supported = QueryPerformanceFrequency(&timer_f);
}
static LARGE_INTEGER timer_t;
static bool timer_get(void)
{
return QueryPerformanceCounter(&timer_t);
}
static LONGLONG get_microseconds_hi(void)
{
return timer_get()? timer_t.QuadPart * 1000000 / timer_f.QuadPart : 0;
}
static LONGLONG get_microseconds_lo(void)
{
return GetTickCount() * 1000i64;
}
static LONGLONG (*get_microseconds)(void) = get_microseconds_lo;
Sleep. /
static TIMECAPS tc;
...
memset(&tc, 0, sizeof(tc));
...
if (flag)
{
get_microseconds = timer_init()? get_microseconds_hi : get_microseconds_lo;
if (!tc.wPeriodMin && timeGetDevCaps(&tc, sizeof(tc)) == MMSYSERR_NOERROR)
{
timeBeginPeriod(tc.wPeriodMin);
}
}
else
{
if (tc.wPeriodMin)
{
timeEndPeriod(tc.wPeriodMin);
memset(&tc, 0, sizeof(tc));
}
}
ctx_t.
class ctx_t:
public mt_obj
{
public:
AVFormatContext *format_ctx;
int stream;
AVCodecContext *codec_ctx;
AVFrame *frame[2];
int frame_idx;
AVFrame *x_bgr_frame;
SwsContext *x_bgr_ctx;
AVFormatContext *ofcx;
AVStream *ost;
int rec_started;
mt_obj rec_cs;
int64_t pts[2];
int continue_on_error;
int disable_decode;
void (*cb_fn)(int cb_param);
int cb_param;
ms_rec_ctx_t *owner;
int out_stream;
int frames_count;
DWORD base_ms;
DWORD timeout_ms;
HANDLE thread_h;
int terminated;
};
FFMPEG
static int interrupt_cb(void *_ctx)
{
ctx_t *ctx = (ctx_t *)_ctx;
if (GetTickCount() - ctx->base_ms > ctx->timeout_ms) return 1;
return 0;
}
FFMPEG avformat_open_input / avformat_find_stream_info / av_read_frame / avformat_close_input
format_ctx = avformat_alloc_context();
if (!format_ctx)
{...}
format_ctx->interrupt_callback.callback = interrupt_cb;
format_ctx->interrupt_callback.opaque = this;
AVDictionary *opts = 0;
if (tcp_flag) av_dict_set(&opts, "rtsp_transport", "tcp", 0);
base_ms = GetTickCount();
if (avformat_open_input(&format_ctx, src, 0, &opts) != 0)
{...}
avformat_open_input timeout_ms. IP-. , TCP. UDP ( ).
AVPacket packet;
int finished;
while (!ctx->terminated)
{
ctx->base_ms = GetTickCount();
if (av_read_frame(ctx->format_ctx, &packet) != 0)
{
if (ctx->continue_on_error)
{}
else
{
ctx->terminated = 1;
ExitThread(0);
}
}
else if (packet.stream_index == ctx->stream)
{
int64_t original_pts = packet.pts;
if (ctx->disable_decode)
{
ctx->rec_cs.lock();
if (ctx->owner) lock(ctx->owner);
if (ctx->ofcx)
{
if (!ctx->rec_started && (packet.flags & AV_PKT_FLAG_KEY))
{
ctx->rec_started = 1;
}
if (ctx->rec_started)
{
int64_t pts = packet.pts;
int64_t delta = packet.dts - packet.pts;
if (ctx->owner)
{
LONGLONG start;
get_start(ctx->owner, &start);
LONGLONG dt = (get_microseconds() - start) / 1000;
pts = dt * ctx->format_ctx->streams[ctx->stream]->time_base.den / 1000;
}
packet.stream_index = ctx->out_stream;
packet.pts = av_rescale_q(pts, ctx->format_ctx->streams[ctx->stream]->time_base, ctx->ost->time_base);
packet.dts = av_rescale_q(packet.dts == AV_NOPTS_VALUE? AV_NOPTS_VALUE : (pts + delta), ctx->format_ctx->streams[ctx->stream]->time_base, ctx->ost->time_base);
packet.duration = av_rescale_q(packet.duration, ctx->format_ctx->streams[ctx->stream]->time_base, ctx->ost->time_base);
packet.pos = -1;
av_interleaved_write_frame( ctx->ofcx, &packet );
packet.stream_index = ctx->stream;
}
}
if (ctx->owner) unlock(ctx->owner);
ctx->rec_cs.unlock();
}
else if (avcodec_decode_video2(ctx->codec_ctx, ctx->frame[ctx->frame_idx], &finished, &packet) > 0)
{
ctx->rec_cs.lock();
if (ctx->owner) lock(ctx->owner);
if (ctx->ofcx)
{
if (!ctx->rec_started && (packet.flags & AV_PKT_FLAG_KEY))
{
ctx->rec_started = 1;
}
if (ctx->rec_started)
{
int64_t pts = packet.pts;
int64_t delta = packet.dts - packet.pts;
if (ctx->owner)
{
LONGLONG start;
get_start(ctx->owner, &start);
LONGLONG dt = (get_microseconds() - start) / 1000;
pts = dt * ctx->format_ctx->streams[ctx->stream]->time_base.den / 1000;
}
packet.stream_index = ctx->out_stream;
packet.pts = av_rescale_q(pts, ctx->format_ctx->streams[ctx->stream]->time_base, ctx->ost->time_base);
packet.dts = av_rescale_q(packet.dts == AV_NOPTS_VALUE? AV_NOPTS_VALUE : (pts + delta), ctx->format_ctx->streams[ctx->stream]->time_base, ctx->ost->time_base);
packet.duration = av_rescale_q(packet.duration, ctx->format_ctx->streams[ctx->stream]->time_base, ctx->ost->time_base);
packet.pos = -1;
av_interleaved_write_frame( ctx->ofcx, &packet );
packet.stream_index = ctx->stream;
}
}
if (ctx->owner) unlock(ctx->owner);
ctx->rec_cs.unlock();
if (finished)
{
ctx->lock();
ctx->pts[ctx->frame_idx] = original_pts;
ctx->frame_idx = !ctx->frame_idx;
if (ctx->cb_fn) ctx->cb_fn(ctx->cb_param);
ctx->unlock();
if (ctx->frames_count + 1 < 0) ctx->frames_count = 1;
else ctx->frames_count++;
}
}
}
av_free_packet(&packet);
}
:
- ( IP-, );
- ( ).
. . 1280 x 720, 24 fps, 2727 kbps Intel Core2 Duo E8300 2.83 17%. ffplay . .
- IP- ( ).
( moov atom not found)
AVOutputFormat *ofmt = av_guess_format( "VOB", NULL, NULL );
ofcx = avformat_alloc_context();
if (!ofcx)
{...}
ofcx->oformat = ofmt;
int ffmpeg_get_bmp_NB_(int _ctx, void *bmp_bits, int w, int h, int br, int co, int sa, void *pts)
{
ctx_t *ctx = (ctx_t *)_ctx;
if (!ctx || !ctx->frames_count) return 0;
if (!ctx->x_bgr_frame || ctx->x_bgr_frame->width != w || ctx->x_bgr_frame->height != h)
{
sws_freeContext(x_bgr_ctx);
av_free(x_bgr_frame);
x_bgr_frame = 0;
x_bgr_ctx = 0;
ctx->x_bgr_frame = av_frame_alloc();
if (!ctx->x_bgr_frame)
{
sws_freeContext(x_bgr_ctx);
av_free(x_bgr_frame);
x_bgr_frame = 0;
x_bgr_ctx = 0;
return 0;
}
ctx->x_bgr_frame->width = w;
ctx->x_bgr_frame->height = h;
if
(
avpicture_fill((AVPicture *)ctx->x_bgr_frame, 0, PIX_FMT_RGB32, w, h) < 0
)
{
sws_freeContext(x_bgr_ctx);
av_free(x_bgr_frame);
x_bgr_frame = 0;
x_bgr_ctx = 0;
return 0;
}
ctx->x_bgr_ctx = sws_getContext(ctx->codec_ctx->width, ctx->codec_ctx->height, ctx->codec_ctx->pix_fmt, w, h, PIX_FMT_RGB32, SWS_BICUBIC, 0, 0, 0);
if (!ctx->x_bgr_ctx)
{
sws_freeContext(x_bgr_ctx);
av_free(x_bgr_frame);
x_bgr_frame = 0;
x_bgr_ctx = 0;
return 0;
}
}
ctx->x_bgr_frame->data[0] = (uint8_t *)bmp_bits;
ctx->x_bgr_frame->data[0] += ctx->x_bgr_frame->linesize[0] * (h - 1);
ctx->x_bgr_frame->linesize[0] = -ctx->x_bgr_frame->linesize[0];
int *table;
int *inv_table;
int brightness, contrast, saturation, srcRange, dstRange;
sws_getColorspaceDetails(ctx->x_bgr_ctx, &inv_table, &srcRange, &table, &dstRange, &brightness, &contrast, &saturation);
brightness = ((br<<16) + 50) / 100;
contrast = (((co+100)<<16) + 50) / 100;
saturation = (((sa+100)<<16) + 50) / 100;
sws_setColorspaceDetails(ctx->x_bgr_ctx, inv_table, srcRange, table, dstRange, brightness, contrast, saturation);
sws_scale(ctx->x_bgr_ctx, ctx->frame[!ctx->frame_idx]->data, ctx->frame[!ctx->frame_idx]->linesize, 0, ctx->codec_ctx->height, ctx->x_bgr_frame->data, ctx->x_bgr_frame->linesize);
ctx->x_bgr_frame->linesize[0] = -ctx->x_bgr_frame->linesize[0];
*(int64_t *)pts = ctx->pts[!ctx->frame_idx] * 1000 / ctx->format_ctx->streams[ctx->stream]->time_base.den;
return 1;
}
int ffmpeg_get_bmp(int _ctx, void *bmp_bits, int w, int h, int br, int co, int sa, void *pts)
{
ctx_t *ctx = (ctx_t *)_ctx;
if (!ctx || !ctx->frames_count) return 0;
ctx->lock();
int res = ffmpeg_get_bmp_NB_(_ctx, bmp_bits, w, h, br, co, sa, pts);
ctx->unlock();
return res;
}
( Borland C++ Builder 6.0 Enterprise Suite).

( , — — ; )
( , )
, , , , , , . : , , FPS , , , ( , , ), () ..
WM_PAINT . WM_PAINT . .
class t_buf_t
{
public:
int n;
__int64 *t;
int idx;
int ttl;
t_buf_t(void): n(0), t(0), idx(0), ttl(0) {}
virtual ~t_buf_t(void) {delete [] t;}
void reset_size(int n)
{
delete [] t;
this->n = n;
t = new __int64[n];
idx = 0;
ttl = 0;
}
void set_t(__int64 t)
{
this->t[idx] = t;
idx++;
if (idx == n) idx = 0;
if (ttl < n) ttl++;
}
__int64 get_dt(void)
{
if (ttl != n) return 0;
int idx_2 = idx - 1;
if (idx_2 < 0) idx_2 = n - 1;
return (t[idx_2] - t[idx]) / n;
}
};
. .
— , . . — . — . ( ). .
char **form_bmp_bits;
HBITMAP *form_bmp_h;
bool *frame_good;
int frame_buffer_size;
int frame_buffer_size_2;
int in_frames_count_ttl;
int in_frame_idx;
int out_frame_idx;
int in_frames_count;
int out_frames_count;
int in_base_idx;
int out_base_idx;
int buffer_w, buffer_h;
t_buf_t in_t_buf;
__int64 out_dt;
__int64 pts;
__int64 dpts;
HANDLE h_a;
HANDLE h_b;
int n_param;
int active_dt_param;
void reset_frame_buffer(void)
{
in_frames_count_ttl = frame_buffer_size == 1? 1 : 0;
in_frame_idx = 0;
out_frame_idx = 0;
in_frames_count = frame_buffer_size == 1? 1 : 0;
out_frames_count = frame_buffer_size == 1? 1 : 0;
in_base_idx = 0;
out_base_idx = frame_buffer_size == 1? 0 : frame_buffer_size;
in_t_buf.reset_size(n_param);
out_dt = 20;
}
void create_frame_buffer(void)
{
reset_frame_buffer();
frame_buffer_size_2 = frame_buffer_size == 1? 1 : frame_buffer_size * 2;
buffer_w = ClientWidth;
buffer_h = ClientHeight;
int n = frame_buffer_size == 1? 1 : frame_buffer_size * 4;
form_bmp_bits = new char *[n];
form_bmp_h = new HBITMAP[n];
frame_good = new bool[n];
for (int i = 0; i < n; i++)
{
BITMAPINFOHEADER form_bmi_hdr;
form_bmi_hdr.biSize = sizeof(BITMAPINFOHEADER);
form_bmi_hdr.biWidth = buffer_w;
form_bmi_hdr.biHeight = buffer_h;
form_bmi_hdr.biPlanes = 1;
form_bmi_hdr.biBitCount = 32;
form_bmi_hdr.biCompression = BI_RGB;
form_bmi_hdr.biSizeImage = form_bmi_hdr.biWidth * form_bmi_hdr.biHeight * 4;
form_bmi_hdr.biXPelsPerMeter = 0;
form_bmi_hdr.biYPelsPerMeter = 0;
form_bmi_hdr.biClrUsed = 0;
form_bmi_hdr.biClrImportant = 0;
form_bmp_h[i] = CreateDIBSection(0, (BITMAPINFO *)&form_bmi_hdr, DIB_RGB_COLORS, (void **)&form_bmp_bits[i], 0, 0);
frame_good[i] = false;
}
}
void destroy_frame_buffer(void)
{
int n = frame_buffer_size == 1? 1 : frame_buffer_size * 4;
for (int i = 0; i < n; i++)
DeleteObject(form_bmp_h[i]);
delete [] form_bmp_bits;
delete [] form_bmp_h;
delete [] frame_good;
}
( ) .
. DLL-
bool u_obj_t::get_frame(const bool _NB_, void *pts)
{
bool res;
if (_NB_)
{
res = ffmpeg_get_bmp_NB_(form->ctx, form->form_bmp_bits[form->in_frame_idx + form->in_base_idx], form->buffer_w, form->buffer_h, form->br, form->co, form->sa, pts);
}
else
{
res = ffmpeg_get_bmp(form->ctx, form->form_bmp_bits[form->in_frame_idx + form->in_base_idx], form->buffer_w, form->buffer_h, form->br, form->co, form->sa, pts);
}
return res;
}
void u_obj_t::exec(const bool _NB_)
{
__int64 pts;
if (form->form_bmp_h[form->in_frame_idx + form->in_base_idx] && get_frame(_NB_, &pts))
{
if (pts != form->pts)
{
form->frame_good[form->in_frame_idx + form->in_base_idx] = true;
form->dpts = pts - form->pts;
form->pts = pts;
form->in_t_buf.set_t(ffmpeg_get_microseconds() / 1000);
form->out_dt = form->in_t_buf.get_dt();
if (form->out_dt > 250) form->out_dt = 250;
form->in_frame_idx++;
if (form->in_frame_idx == form->frame_buffer_size_2) form->in_frame_idx--;
form->in_frames_count++;
if (form->in_frames_count_ttl != form->frame_buffer_size) form->in_frames_count_ttl++;
}
}
else
{
form->frame_good[form->in_frame_idx + form->in_base_idx] = false;
}
}
void cb_fn(int cb_param)
{
Tmain_form *form = (Tmain_form *)cb_param;
form->u_obj->lock();
form->u_obj->exec(true);
form->u_obj->unlock();
if (form->frame_buffer_size == 1)
{
ResetEvent(form->h_b);
SetEvent(form->h_a);
ResetEvent(form->h_a);
SetEvent(form->h_b);
}
}
WM_PAINT
void __fastcall repaint_thread_t::repaint(void)
{
form->Repaint();
if (form->frame_buffer_size == 1)
{}
else
{
form->out_frame_idx++;
if (form->out_frame_idx >= form->out_frames_count || form->in_frames_count >= form->frame_buffer_size_2)
{
form->u_obj->lock();
if (form->in_frames_count)
{
int n = form->in_frames_count >= form->frame_buffer_size_2? form->frame_buffer_size_2 : form->in_frames_count;
form->out_frame_idx = 0;
form->out_frames_count = n;
form->out_base_idx = form->out_base_idx? 0 : form->frame_buffer_size_2;
form->in_frame_idx = 0;
form->in_frames_count = 0;
form->in_base_idx = form->in_base_idx? 0 : form->frame_buffer_size_2;
}
else
{
form->out_frame_idx--;
}
form->u_obj->unlock();
}
}
}
void __fastcall repaint_thread_t::Execute(void)
{
while (!Terminated)
{
if (form->frame_buffer_size == 1)
{
WaitForSingleObject(form->h_a, 250);
Synchronize(repaint);
WaitForSingleObject(form->h_b, 250);
}
else
{
form->active_dt_param =
(
form->frame_buffer_size -
(form->out_frames_count - form->out_frame_idx + form->in_frames_count)
) * form->out_dt / form->frame_buffer_size;
LONGLONG t = ffmpeg_get_microseconds() / 1000;
Synchronize(repaint);
LONGLONG dt = ffmpeg_get_microseconds() / 1000 - t;
int delay = form->out_dt + form->active_dt_param - dt;
if (delay > 0) Sleep(delay);
}
}
}
.
mem_c = new TCanvas;
...
void Tmain_form::draw_bg_img(TCanvas *c, int w, int h)
{
c->Brush->Style = bsSolid;
c->Brush->Color = clBlack;
c->FillRect(TRect(0, 0, w, h));
}
void Tmain_form::draw_no_signal_img(TCanvas *c, int w, int h)
{
AnsiString s = "NO SIGNAL";
c->Brush->Style = bsClear;
c->Font->Color = clWhite;
c->Font->Size = 24;
c->Font->Style = TFontStyles()<< fsBold;
c->TextOutA(w / 2 - c->TextWidth(s) / 2, h / 2 - c->TextHeight(s) / 2, s);
}
...
void __fastcall Tmain_form::WMPaint(TWMPaint& Message)
{
if (form_bmp_h[out_frame_idx + out_base_idx])
{
PAINTSTRUCT ps;
HDC paint_hdc = BeginPaint(Handle, &ps);
HDC hdc_1 = CreateCompatibleDC(paint_hdc);
HDC hdc_2 = CreateCompatibleDC(paint_hdc);
mem_c->Handle = hdc_1;
HBITMAP h_1 = CreateCompatibleBitmap(paint_hdc, buffer_w, buffer_h);
HBITMAP old_h_1 = (HBITMAP)SelectObject(hdc_1, h_1);
HBITMAP old_h_2 = (HBITMAP)SelectObject(hdc_2, form_bmp_h[out_frame_idx + out_base_idx]);
BitBlt(hdc_1, 0, 0, buffer_w, buffer_h, hdc_2, 0, 0, SRCCOPY);
if (start_form->stereo_test && (start_form->left_form == this || start_form->right_form == this))
{
mem_c->CopyMode = cmSrcCopy;
mem_c->StretchDraw(TRect(0, 0, buffer_w, buffer_h), start_form->left_form == this? start_form->left_img : start_form->right_img);
}
else
{
if (!ffmpeg_get_status(ctx))
{
draw_bg_img(mem_c, buffer_w, buffer_h);
draw_no_signal_img(mem_c, buffer_w, buffer_h);
}
else if (!out_frames_count) {
draw_bg_img(mem_c, buffer_w, buffer_h);
draw_buffering_img(mem_c, buffer_w, buffer_h);
}
else if (!frame_good[out_frame_idx + out_base_idx])
{
draw_bg_img(mem_c, buffer_w, buffer_h);
draw_no_signal_img(mem_c, buffer_w, buffer_h);
}
}
if (area_type) draw_area_img(mem_c, buffer_w, buffer_h);
if (show_ui) draw_info_img(mem_c, buffer_w, buffer_h);
BitBlt(paint_hdc, 0, 0, buffer_w, buffer_h, hdc_1, 0, 0, SRCCOPY);
SelectObject(hdc_1, old_h_1);
SelectObject(hdc_2, old_h_2);
DeleteObject(h_1);
DeleteDC(hdc_1);
DeleteDC(hdc_2);
EndPaint(Handle, &ps);
}
}
— (!), (BMP) . ? , (, , ) , …
. . .
-
- IP- : ( ). . ().

()
( — )
- :
- VR Android;
-
iDisplay ();
- iDisplay ();
- USB ( ADB ) Wi-Fi « » ( — );
- « » VR .
- :
- (VR 0 USD :), iDisplay 5 USD);
- — .
:
-« » — — — , , iDisplay , : Intel Core2 Duo E8300 2.83 iDisplay 50% ( ; , Intel Core2 Quad Q9400 2.66 25%) , ; ( ) — iDisplay - ( ); iDisplay « »;
- VR , .
: DLL- .
void scr_rec_thread_t::main(void)
{
LONGLONG t = ffmpeg_get_microseconds() / 1000;
HBITMAP old_h = (HBITMAP)SelectObject(scr_bmp_hdc, scr_bmp_h);
if (!BitBlt
(
scr_bmp_hdc,
0,
0,
scr_w,
scr_h,
scr_hdc,
scr_x,
scr_y,
CAPTUREBLT | SRCCOPY
))
{}
SelectObject(scr_bmp_hdc, old_h);
ffmpeg_rec_bmp(scr_rec_ctx, scr_bmp_bits);
LONGLONG dt = ffmpeg_get_microseconds() / 1000 - t;
int ms_per_frame = 1000 / scr_rec_max_fps;
if (dt < ms_per_frame)
{
dt = ms_per_frame - dt;
if (dt < scr_rec_min_delay) dt = scr_rec_min_delay;
}
else
{
dt = scr_rec_min_delay;
}
Sleep(dt);
frames_count++;
if (ffmpeg_get_microseconds() / 1000 - start > 1000)
{
start = ffmpeg_get_microseconds() / 1000;
fps = frames_count;
frames_count = 0;
}
}
scr_rec_thread_t *scr_rec_thread;
int scr_x;
int scr_y;
int scr_w;
int scr_h;
HDC scr_hdc;
HDC scr_bmp_hdc;
char *scr_bmp_bits;
HBITMAP scr_bmp_h;
void scr_rec_init(void)
{
scr_rec_ctx = ffmpeg_alloc_rec_ctx();
scr_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
scr_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
scr_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
scr_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
scr_hdc = GetDC(0);
scr_bmp_hdc = CreateCompatibleDC(scr_hdc);
BITMAPINFOHEADER scr_bmi_hdr;
scr_bmi_hdr.biSize = sizeof(BITMAPINFOHEADER);
scr_bmi_hdr.biWidth = scr_w;
scr_bmi_hdr.biHeight = scr_h;
scr_bmi_hdr.biPlanes = 1;
scr_bmi_hdr.biBitCount = 32;
scr_bmi_hdr.biCompression = BI_RGB;
scr_bmi_hdr.biSizeImage = scr_bmi_hdr.biWidth * scr_bmi_hdr.biHeight * 4;
scr_bmi_hdr.biXPelsPerMeter = 0;
scr_bmi_hdr.biYPelsPerMeter = 0;
scr_bmi_hdr.biClrUsed = 0;
scr_bmi_hdr.biClrImportant = 0;
scr_bmp_h = CreateDIBSection(0, (BITMAPINFO *)&scr_bmi_hdr, DIB_RGB_COLORS, (void **)&scr_bmp_bits, 0, 0);
scr_rec_thread = new scr_rec_thread_t();
}
void scr_rec_uninit(void)
{
delete scr_rec_thread;
DeleteObject(scr_bmp_h);
DeleteDC(scr_bmp_hdc);
DeleteDC(scr_hdc);
ffmpeg_free_ctx(scr_rec_ctx);
}
int scr_rec_ctx;
. DLL-
class rec_ctx_t:
public mt_obj
{
public:
AVFormatContext *ofcx;
AVStream *ost;
LONGLONG start;
AVPacket pkt;
AVFrame *frame;
AVFrame *_frame;
uint8_t *_buffer;
SwsContext *ctx;
int codec_id;
char *preset;
int crf;
rec_ctx_t(void):
ofcx(0),
ost(0),
frame(0),
_frame(0),
_buffer(0),
ctx(0),
codec_id(CODEC_ID_H264),
preset(strdup("ultrafast")),
crf(23)
{
memset(&start, 0, sizeof(start));
av_init_packet( &pkt );
}
virtual ~rec_ctx_t()
{
ffmpeg_stop_rec2((int)this);
free(preset);
}
void clean(void)
{
sws_freeContext(ctx);
av_free(_buffer);
av_free(frame);
av_free(_frame);
if (ost) avcodec_close( ost->codec );
avformat_free_context( ofcx );
ofcx = 0;
ost = 0;
memset(&start, 0, sizeof(start));
av_init_packet( &pkt );
frame = 0;
_frame = 0;
_buffer = 0;
ctx = 0;
}
int prepare(char *dst, int w, int h, int den, int gop_size)
{
AVOutputFormat *ofmt = av_guess_format( "VOB", NULL, NULL );
if (!ofmt)
{
clean();
return 0;
}
ofcx = avformat_alloc_context();
if (!ofcx)
{
clean();
return 0;
}
ofcx->oformat = ofmt;
AVCodec *ocodec = avcodec_find_encoder( (AVCodecID)codec_id );
if (!ocodec || !ocodec->pix_fmts || ocodec->pix_fmts[0] == -1)
{
clean();
return 0;
}
ost = avformat_new_stream( ofcx, ocodec );
if (!ost)
{
clean();
return 0;
}
ost->codec->width = w;
ost->codec->height = h;
ost->codec->pix_fmt = ocodec->pix_fmts[0];
ost->codec->time_base.num = 1;
ost->codec->time_base.den = den;
ost->time_base.num = 1;
ost->time_base.den = den;
ost->codec->gop_size = gop_size;
if ( ofcx->oformat->flags & AVFMT_GLOBALHEADER ) ost->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
AVDictionary *opts = 0;
av_dict_set(&opts, "preset", preset, 0);
char crf_str[3];
sprintf(crf_str, "%i", crf);
av_dict_set(&opts, "crf", crf_str, 0);
if (avcodec_open2( ost->codec, ocodec, &opts ) != 0)
{
clean();
return 0;
}
frame = av_frame_alloc();
_frame = av_frame_alloc();
if (!frame || !_frame)
{
clean();
return 0;
}
frame->format = PIX_FMT_RGB32;
frame->width = w;
frame->height = h;
_frame->format = PIX_FMT_YUV420P;
_frame->width = w;
_frame->height = h;
int _buffer_size = avpicture_get_size(PIX_FMT_YUV420P, w, h);
if (_buffer_size < 0)
{
clean();
return 0;
}
uint8_t *_buffer = (uint8_t *)av_malloc(_buffer_size * sizeof(uint8_t));
if (!_buffer)
{
clean();
return 0;
}
if
(
avpicture_fill((AVPicture *)frame, 0, PIX_FMT_RGB32, w, h) < 0 ||
avpicture_fill((AVPicture *)_frame, _buffer, PIX_FMT_YUV420P, w, h) < 0
)
{
clean();
return 0;
}
ctx = sws_getContext(w, h, PIX_FMT_RGB32, w, h, PIX_FMT_YUV420P, SWS_BICUBIC, 0, 0, 0);
if (!ctx)
{
clean();
return 0;
}
if (avio_open2( &ofcx->pb, dst, AVIO_FLAG_WRITE, NULL, NULL ) < 0)
{
clean();
return 0;
}
if (avformat_write_header( ofcx, NULL ) != 0)
{
avio_close( ofcx->pb );
clean();
return 0;
}
start = get_microseconds();
return 1;
}
};
int ffmpeg_start_rec2
(
int rec_ctx,
char *dst, int w, int h, int den, int gop_size
)
{
rec_ctx_t *ctx = (rec_ctx_t *)rec_ctx;
if (!ctx) return 0;
ffmpeg_stop_rec2(rec_ctx);
return ctx->prepare(dst, w, h, den, gop_size);
}
void ffmpeg_stop_rec2(int rec_ctx)
{
rec_ctx_t *ctx = (rec_ctx_t *)rec_ctx;
if (!ctx || !ctx->ofcx) return;
av_write_trailer( ctx->ofcx );
avio_close( ctx->ofcx->pb );
ctx->clean();
}
int ffmpeg_rec_bmp(int rec_ctx, void *bmp_bits)
{
rec_ctx_t *ctx = (rec_ctx_t *)rec_ctx;
if (!ctx) return 0;
ctx->frame->data[0] = (uint8_t *)bmp_bits;
ctx->frame->data[0] += ctx->frame->linesize[0] * (ctx->frame->height - 1);
ctx->frame->linesize[0] = -ctx->frame->linesize[0];
sws_scale(ctx->ctx, ctx->frame->data, ctx->frame->linesize, 0, ctx->frame->height, ctx->_frame->data, ctx->_frame->linesize);
ctx->frame->linesize[0] = -ctx->frame->linesize[0];
LONGLONG dt = (get_microseconds() - ctx->start) / 1000;
ctx->_frame->pts = dt * ctx->ost->time_base.den / 1000;
int got_packet;
if (avcodec_encode_video2(ctx->ost->codec, &ctx->pkt, ctx->_frame, &got_packet) != 0) return 0;
int res = 1;
if (got_packet)
{
res = av_interleaved_write_frame( ctx->ofcx, &ctx->pkt ) == 0? 1 : 0;
av_free_packet( &ctx->pkt );
}
return res;
}
, (1280 x 1024, 25 fps) Intel Core2 Duo E8300 2.83 30% FPS, , .
. . .
:
.