Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
F
ffmpeg.wasm-core
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Linshizhi
ffmpeg.wasm-core
Commits
3f6dc4a1
Commit
3f6dc4a1
authored
Apr 29, 2011
by
Joshua Warner
Committed by
Michael Niedermayer
Apr 29, 2011
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
encoder for adobe's flash ScreenVideo2 codec
parent
85ff3394
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
918 additions
and
0 deletions
+918
-0
Changelog
Changelog
+1
-0
configure
configure
+1
-0
general.texi
doc/general.texi
+1
-0
Makefile
libavcodec/Makefile
+1
-0
allcodecs.c
libavcodec/allcodecs.c
+1
-0
flashsv2enc.c
libavcodec/flashsv2enc.c
+913
-0
No files found.
Changelog
View file @
3f6dc4a1
...
...
@@ -342,6 +342,7 @@ version 0.5:
- Gopher client support
- MXF D-10 muxer
- generic metadata API
- flash ScreenVideo2 encoder
version 0.4.9-pre1:
...
...
configure
View file @
3f6dc4a1
...
...
@@ -1275,6 +1275,7 @@ flac_decoder_select="golomb"
flac_encoder_select
=
"golomb lpc"
flashsv_decoder_select
=
"zlib"
flashsv_encoder_select
=
"zlib"
flashsv2_encoder_select
=
"zlib"
flv_decoder_select
=
"h263_decoder"
flv_encoder_select
=
"h263_encoder"
fraps_decoder_select
=
"huffman"
...
...
doc/general.texi
View file @
3f6dc4a1
...
...
@@ -401,6 +401,7 @@ following image formats are supported:
@tab experimental lossless codec (fourcc: FFV1)
@item Flash Screen Video v1 @tab X @tab X
@tab fourcc: FSV1
@item Flash Screen Video v2 @tab X
@item Flash Video (FLV) @tab X @tab X
@tab Sorenson H.263 used in Flash
@item Fraps @tab @tab X
...
...
libavcodec/Makefile
View file @
3f6dc4a1
...
...
@@ -145,6 +145,7 @@ OBJS-$(CONFIG_FLAC_DECODER) += flacdec.o flacdata.o flac.o vorbis_dat
OBJS-$(CONFIG_FLAC_ENCODER)
+=
flacenc.o
flacdata.o
flac.o
OBJS-$(CONFIG_FLASHSV_DECODER)
+=
flashsv.o
OBJS-$(CONFIG_FLASHSV_ENCODER)
+=
flashsvenc.o
OBJS-$(CONFIG_FLASHSV2_ENCODER)
+=
flashsv2enc.o
OBJS-$(CONFIG_FLIC_DECODER)
+=
flicvideo.o
OBJS-$(CONFIG_FOURXM_DECODER)
+=
4xm.o
OBJS-$(CONFIG_FRAPS_DECODER)
+=
fraps.o
...
...
libavcodec/allcodecs.c
View file @
3f6dc4a1
...
...
@@ -108,6 +108,7 @@ void avcodec_register_all(void)
REGISTER_ENCDEC
(
FFV1
,
ffv1
);
REGISTER_ENCDEC
(
FFVHUFF
,
ffvhuff
);
REGISTER_ENCDEC
(
FLASHSV
,
flashsv
);
REGISTER_ENCODER
(
FLASHSV2
,
flashsv2
);
REGISTER_DECODER
(
FLIC
,
flic
);
REGISTER_ENCDEC
(
FLV
,
flv
);
REGISTER_DECODER
(
FOURXM
,
fourxm
);
...
...
libavcodec/flashsv2enc.c
0 → 100644
View file @
3f6dc4a1
/*
* Flash Screen Video Version 2 encoder
* Copyright (C) 2009 Joshua Warner
*
* 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 libavcodec/flashsv2enc.c
* Flash Screen Video Version 2 encoder
* @author Joshua Warner
*/
/* Differences from version 1 stream:
* NOTE: Currently, the only player that supports version 2 streams is Adobe Flash Player itself.
* * Supports sending only a range of scanlines in a block,
* indicating a difference from the corresponding block in the last keyframe.
* * Supports initializing the zlib dictionary with data from the corresponding
* block in the last keyframe, to improve compression.
* * Supports a hybrid 15-bit rgb / 7-bit palette color space.
*/
/* TODO:
* Don't keep Block structures for both current frame and keyframe.
* Make better heuristics for deciding stream parameters (optimum_* functions). Currently these return constants.
* Figure out how to encode palette information in the stream, choose an optimum palette at each keyframe.
* Figure out how the zlibPrimeCompressCurrent flag works, implement support.
* Find other sample files (that weren't generated here), develop a decoder.
*/
#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>
#include "libavutil/imgutils.h"
#include "avcodec.h"
#include "put_bits.h"
#include "bytestream.h"
#define HAS_IFRAME_IMAGE 0x02
#define HAS_PALLET_INFO 0x01
#define COLORSPACE_BGR 0x00
#define COLORSPACE_15_7 0x10
#define HAS_DIFF_BLOCKS 0x04
#define ZLIB_PRIME_COMPRESS_CURRENT 0x02
#define ZLIB_PRIME_COMPRESS_PREVIOUS 0x01
// Disables experimental "smart" parameter-choosing code, as well as the statistics that it depends on.
// At the moment, the "smart" code is a great example of how the parameters *shouldn't* be chosen.
#define FLASHSV2_DUMB
typedef
struct
Block
{
uint8_t
*
enc
;
uint8_t
*
sl_begin
,
*
sl_end
;
int
enc_size
;
uint8_t
*
data
;
unsigned
long
data_size
;
uint8_t
start
,
len
;
uint8_t
dirty
;
uint8_t
col
,
row
,
width
,
height
;
uint8_t
flags
;
}
Block
;
typedef
struct
Palette
{
unsigned
colors
[
128
];
uint8_t
index
[
1
<<
15
];
}
Palette
;
typedef
struct
FlashSV2Context
{
AVCodecContext
*
avctx
;
uint8_t
*
current_frame
;
uint8_t
*
key_frame
;
AVFrame
frame
;
uint8_t
*
encbuffer
;
uint8_t
*
keybuffer
;
uint8_t
*
databuffer
;
Block
*
frame_blocks
;
Block
*
key_blocks
;
int
frame_size
;
int
blocks_size
;
int
use15_7
,
dist
,
comp
;
int
rows
,
cols
;
int
last_key_frame
;
int
image_width
,
image_height
;
int
block_width
,
block_height
;
uint8_t
flags
;
uint8_t
use_custom_palette
;
uint8_t
palette_type
;
///< 0=>default, 1=>custom - changed when palette regenerated.
Palette
palette
;
#ifndef FLASHSV2_DUMB
double
tot_blocks
;
///< blocks encoded since last keyframe
double
diff_blocks
;
///< blocks that were different since last keyframe
double
tot_lines
;
///< total scanlines in image since last keyframe
double
diff_lines
;
///< scanlines that were different since last keyframe
double
raw_size
;
///< size of raw frames since last keyframe
double
comp_size
;
///< size of compressed data since last keyframe
double
uncomp_size
;
///< size of uncompressed data since last keyframe
double
total_bits
;
///< total bits written to stream so far
#endif
}
FlashSV2Context
;
static
av_cold
void
cleanup
(
FlashSV2Context
*
s
)
{
if
(
s
->
encbuffer
)
av_free
(
s
->
encbuffer
);
if
(
s
->
keybuffer
)
av_free
(
s
->
keybuffer
);
if
(
s
->
databuffer
)
av_free
(
s
->
databuffer
);
if
(
s
->
current_frame
)
av_free
(
s
->
current_frame
);
if
(
s
->
key_frame
)
av_free
(
s
->
key_frame
);
if
(
s
->
frame_blocks
)
av_free
(
s
->
frame_blocks
);
if
(
s
->
key_blocks
)
av_free
(
s
->
key_blocks
);
}
static
void
init_blocks
(
FlashSV2Context
*
s
,
Block
*
blocks
,
uint8_t
*
encbuf
,
uint8_t
*
databuf
)
{
int
row
,
col
;
Block
*
b
;
for
(
col
=
0
;
col
<
s
->
cols
;
col
++
)
{
for
(
row
=
0
;
row
<
s
->
rows
;
row
++
)
{
b
=
blocks
+
(
col
+
row
*
s
->
cols
);
b
->
width
=
(
col
<
s
->
cols
-
1
)
?
s
->
block_width
:
s
->
image_width
-
col
*
s
->
block_width
;
b
->
height
=
(
row
<
s
->
rows
-
1
)
?
s
->
block_height
:
s
->
image_height
-
row
*
s
->
block_height
;
b
->
row
=
row
;
b
->
col
=
col
;
b
->
enc
=
encbuf
;
b
->
data
=
databuf
;
encbuf
+=
b
->
width
*
b
->
height
*
3
;
databuf
+=
!
databuf
?
0
:
b
->
width
*
b
->
height
*
6
;
}
}
}
static
void
reset_stats
(
FlashSV2Context
*
s
)
{
#ifndef FLASHSV2_DUMB
s
->
diff_blocks
=
0
.
1
;
s
->
tot_blocks
=
1
;
s
->
diff_lines
=
0
.
1
;
s
->
tot_lines
=
1
;
s
->
raw_size
=
s
->
comp_size
=
s
->
uncomp_size
=
10
;
#endif
}
static
av_cold
int
flashsv2_encode_init
(
AVCodecContext
*
avctx
)
{
FlashSV2Context
*
s
=
avctx
->
priv_data
;
s
->
avctx
=
avctx
;
s
->
comp
=
avctx
->
compression_level
;
if
(
s
->
comp
==
-
1
)
s
->
comp
=
9
;
if
(
s
->
comp
<
0
||
s
->
comp
>
9
)
{
av_log
(
avctx
,
AV_LOG_ERROR
,
"Compression level should be 0-9, not %d
\n
"
,
s
->
comp
);
return
-
1
;
}
if
((
avctx
->
width
>
4095
)
||
(
avctx
->
height
>
4095
))
{
av_log
(
avctx
,
AV_LOG_ERROR
,
"Input dimensions too large, input must be max 4096x4096 !
\n
"
);
return
-
1
;
}
if
(
av_image_check_size
(
avctx
->
width
,
avctx
->
height
,
0
,
avctx
)
<
0
)
return
-
1
;
s
->
last_key_frame
=
0
;
s
->
image_width
=
avctx
->
width
;
s
->
image_height
=
avctx
->
height
;
s
->
block_width
=
(
s
->
image_width
/
12
)
&
~
15
;
s
->
block_height
=
(
s
->
image_height
/
12
)
&
~
15
;
s
->
rows
=
(
s
->
image_height
+
s
->
block_height
-
1
)
/
s
->
block_height
;
s
->
cols
=
(
s
->
image_width
+
s
->
block_width
-
1
)
/
s
->
block_width
;
s
->
frame_size
=
s
->
image_width
*
s
->
image_height
*
3
;
s
->
blocks_size
=
s
->
rows
*
s
->
cols
*
sizeof
(
Block
);
s
->
encbuffer
=
av_mallocz
(
s
->
frame_size
);
s
->
keybuffer
=
av_mallocz
(
s
->
frame_size
);
s
->
databuffer
=
av_mallocz
(
s
->
frame_size
*
6
);
s
->
current_frame
=
av_mallocz
(
s
->
frame_size
);
s
->
key_frame
=
av_mallocz
(
s
->
frame_size
);
s
->
frame_blocks
=
av_mallocz
(
s
->
blocks_size
);
s
->
key_blocks
=
av_mallocz
(
s
->
blocks_size
);
init_blocks
(
s
,
s
->
frame_blocks
,
s
->
encbuffer
,
s
->
databuffer
);
init_blocks
(
s
,
s
->
key_blocks
,
s
->
keybuffer
,
0
);
reset_stats
(
s
);
#ifndef FLASHSV2_DUMB
s
->
total_bits
=
1
;
#endif
s
->
use_custom_palette
=
0
;
s
->
palette_type
=
-
1
;
// so that the palette will be generated in reconfigure_at_keyframe
if
(
!
s
->
encbuffer
||
!
s
->
keybuffer
||
!
s
->
databuffer
||
!
s
->
current_frame
||
!
s
->
key_frame
||
!
s
->
key_blocks
||
!
s
->
frame_blocks
)
{
av_log
(
avctx
,
AV_LOG_ERROR
,
"Memory allocation failed.
\n
"
);
cleanup
(
s
);
return
-
1
;
}
return
0
;
}
static
int
new_key_frame
(
FlashSV2Context
*
s
)
{
int
i
;
FFSWAP
(
uint8_t
*
,
s
->
keybuffer
,
s
->
encbuffer
);
memcpy
(
s
->
key_blocks
,
s
->
frame_blocks
,
s
->
blocks_size
);
memcpy
(
s
->
key_frame
,
s
->
current_frame
,
s
->
frame_size
);
for
(
i
=
0
;
i
<
s
->
rows
*
s
->
cols
;
i
++
)
{
s
->
key_blocks
[
i
].
enc
+=
(
s
->
keybuffer
-
s
->
encbuffer
);
s
->
key_blocks
[
i
].
sl_begin
=
0
;
s
->
key_blocks
[
i
].
sl_end
=
0
;
s
->
key_blocks
[
i
].
data
=
0
;
}
return
0
;
}
static
int
write_palette
(
FlashSV2Context
*
s
,
uint8_t
*
buf
,
int
buf_size
)
{
//this isn't implemented yet! Default palette only!
return
-
1
;
}
static
int
write_header
(
FlashSV2Context
*
s
,
uint8_t
*
buf
,
int
buf_size
)
{
PutBitContext
pb
;
int
buf_pos
,
len
;
if
(
buf_size
<
5
)
return
-
1
;
init_put_bits
(
&
pb
,
buf
,
buf_size
*
8
);
put_bits
(
&
pb
,
4
,
(
s
->
block_width
>>
4
)
-
1
);
put_bits
(
&
pb
,
12
,
s
->
image_width
);
put_bits
(
&
pb
,
4
,
(
s
->
block_height
>>
4
)
-
1
);
put_bits
(
&
pb
,
12
,
s
->
image_height
);
flush_put_bits
(
&
pb
);
buf_pos
=
4
;
buf
[
buf_pos
++
]
=
s
->
flags
;
if
(
s
->
flags
&
HAS_PALLET_INFO
)
{
len
=
write_palette
(
s
,
buf
+
buf_pos
,
buf_size
-
buf_pos
);
if
(
len
<
0
)
return
-
1
;
buf_pos
+=
len
;
}
return
buf_pos
;
}
static
int
write_block
(
Block
*
b
,
uint8_t
*
buf
,
int
buf_size
)
{
int
buf_pos
=
0
;
unsigned
block_size
=
b
->
data_size
;
if
(
b
->
flags
&
HAS_DIFF_BLOCKS
)
block_size
+=
2
;
if
(
b
->
flags
&
ZLIB_PRIME_COMPRESS_CURRENT
)
block_size
+=
2
;
if
(
block_size
>
0
)
block_size
+=
1
;
if
(
buf_size
<
block_size
+
2
)
return
-
1
;
buf
[
buf_pos
++
]
=
block_size
>>
8
;
buf
[
buf_pos
++
]
=
block_size
;
if
(
block_size
==
0
)
return
buf_pos
;
buf
[
buf_pos
++
]
=
b
->
flags
;
if
(
b
->
flags
&
HAS_DIFF_BLOCKS
)
{
buf
[
buf_pos
++
]
=
(
uint8_t
)
(
b
->
start
);
buf
[
buf_pos
++
]
=
(
uint8_t
)
(
b
->
len
);
}
if
(
b
->
flags
&
ZLIB_PRIME_COMPRESS_CURRENT
)
{
//This feature of the format is poorly understood, and as of now, unused.
buf
[
buf_pos
++
]
=
(
uint8_t
)
(
b
->
col
);
buf
[
buf_pos
++
]
=
(
uint8_t
)
(
b
->
row
);
}
memcpy
(
buf
+
buf_pos
,
b
->
data
,
b
->
data_size
);
buf_pos
+=
b
->
data_size
;
return
buf_pos
;
}
static
int
encode_zlib
(
Block
*
b
,
uint8_t
*
buf
,
unsigned
long
*
buf_size
,
int
comp
)
{
int
res
=
compress2
(
buf
,
buf_size
,
b
->
sl_begin
,
b
->
sl_end
-
b
->
sl_begin
,
comp
);
return
res
==
Z_OK
?
0
:
-
1
;
}
static
int
encode_zlibprime
(
Block
*
b
,
Block
*
prime
,
uint8_t
*
buf
,
int
*
buf_size
,
int
comp
)
{
z_stream
s
;
int
res
;
s
.
zalloc
=
NULL
;
s
.
zfree
=
NULL
;
s
.
opaque
=
NULL
;
res
=
deflateInit
(
&
s
,
comp
);
if
(
res
<
0
)
return
-
1
;
s
.
next_in
=
prime
->
enc
;
s
.
avail_in
=
prime
->
enc_size
;
while
(
s
.
avail_in
>
0
)
{
s
.
next_out
=
buf
;
s
.
avail_out
=
*
buf_size
;
res
=
deflate
(
&
s
,
Z_SYNC_FLUSH
);
if
(
res
<
0
)
return
-
1
;
}
s
.
next_in
=
b
->
sl_begin
;
s
.
avail_in
=
b
->
sl_end
-
b
->
sl_begin
;
s
.
next_out
=
buf
;
s
.
avail_out
=
*
buf_size
;
res
=
deflate
(
&
s
,
Z_FINISH
);
deflateEnd
(
&
s
);
*
buf_size
-=
s
.
avail_out
;
if
(
res
!=
Z_STREAM_END
)
return
-
1
;
return
0
;
}
static
int
encode_bgr
(
Block
*
b
,
const
uint8_t
*
src
,
int
stride
)
{
int
i
;
uint8_t
*
ptr
=
b
->
enc
;
for
(
i
=
0
;
i
<
b
->
start
;
i
++
)
memcpy
(
ptr
+
i
*
b
->
width
*
3
,
src
+
i
*
stride
,
b
->
width
*
3
);
b
->
sl_begin
=
ptr
+
i
*
b
->
width
*
3
;
for
(;
i
<
b
->
start
+
b
->
len
;
i
++
)
memcpy
(
ptr
+
i
*
b
->
width
*
3
,
src
+
i
*
stride
,
b
->
width
*
3
);
b
->
sl_end
=
ptr
+
i
*
b
->
width
*
3
;
for
(;
i
<
b
->
height
;
i
++
)
memcpy
(
ptr
+
i
*
b
->
width
*
3
,
src
+
i
*
stride
,
b
->
width
*
3
);
b
->
enc_size
=
ptr
+
i
*
b
->
width
*
3
-
b
->
enc
;
return
b
->
enc_size
;
}
static
inline
unsigned
pixel_color15
(
const
uint8_t
*
src
)
{
return
(
src
[
0
]
>>
3
)
|
((
src
[
1
]
&
0xf8
)
<<
2
)
|
((
src
[
2
]
&
0xf8
)
<<
7
);
}
static
inline
unsigned
int
chroma_diff
(
unsigned
int
c1
,
unsigned
int
c2
)
{
unsigned
int
t1
=
(
c1
&
0x000000ff
)
+
((
c1
&
0x0000ff00
)
>>
8
)
+
((
c1
&
0x00ff0000
)
>>
16
);
unsigned
int
t2
=
(
c2
&
0x000000ff
)
+
((
c2
&
0x0000ff00
)
>>
8
)
+
((
c2
&
0x00ff0000
)
>>
16
);
return
abs
(
t1
-
t2
)
+
abs
((
c1
&
0x000000ff
)
-
(
c2
&
0x000000ff
))
+
abs
(((
c1
&
0x0000ff00
)
>>
8
)
-
((
c2
&
0x0000ff00
)
>>
8
))
+
abs
(((
c1
&
0x00ff0000
)
>>
16
)
-
((
c2
&
0x00ff0000
)
>>
16
));
}
static
inline
int
pixel_color7_fast
(
Palette
*
palette
,
unsigned
c15
)
{
return
palette
->
index
[
c15
];
}
static
int
pixel_color7_slow
(
Palette
*
palette
,
unsigned
color
)
{
int
i
,
min
=
0x7fffffff
;
int
minc
=
-
1
;
for
(
i
=
0
;
i
<
128
;
i
++
)
{
int
c1
=
palette
->
colors
[
i
];
int
diff
=
chroma_diff
(
c1
,
color
);
if
(
diff
<
min
)
{
min
=
diff
;
minc
=
i
;
}
}
return
minc
;
}
static
inline
unsigned
pixel_bgr
(
const
uint8_t
*
src
)
{
return
(
src
[
0
])
|
(
src
[
1
]
<<
8
)
|
(
src
[
2
]
<<
16
);
}
static
int
write_pixel_15_7
(
Palette
*
palette
,
uint8_t
*
dest
,
const
uint8_t
*
src
,
int
dist
)
{
unsigned
c15
=
pixel_color15
(
src
);
unsigned
color
=
pixel_bgr
(
src
);
int
d15
=
chroma_diff
(
color
,
color
&
0x00f8f8f8
);
int
c7
=
pixel_color7_fast
(
palette
,
c15
);
int
d7
=
chroma_diff
(
color
,
palette
->
colors
[
c7
]);
if
(
dist
+
d15
>=
d7
)
{
dest
[
0
]
=
(
uint8_t
)
c7
;
return
1
;
}
else
{
dest
[
0
]
=
0x80
|
(
uint8_t
)
(
c15
>>
8
);
dest
[
1
]
=
c15
&
0xff
;
return
2
;
}
}
static
int
update_palette_index
(
Palette
*
palette
)
{
int
r
,
g
,
b
;
unsigned
int
bgr
,
c15
,
index
;
for
(
r
=
4
;
r
<
256
;
r
+=
8
)
{
for
(
g
=
4
;
g
<
256
;
g
+=
8
)
{
for
(
b
=
4
;
b
<
256
;
b
+=
8
)
{
bgr
=
b
|
(
g
<<
8
)
|
(
r
<<
16
);
c15
=
(
b
>>
3
)
|
((
g
&
0xf8
)
<<
2
)
|
((
r
&
0xf8
)
<<
7
);
index
=
pixel_color7_slow
(
palette
,
bgr
);
palette
->
index
[
c15
]
=
index
;
}
}
}
return
0
;
}
static
const
unsigned
int
default_screen_video_v2_palette
[
128
]
=
{
0x00000000
,
0x00333333
,
0x00666666
,
0x00999999
,
0x00CCCCCC
,
0x00FFFFFF
,
0x00330000
,
0x00660000
,
0x00990000
,
0x00CC0000
,
0x00FF0000
,
0x00003300
,
0x00006600
,
0x00009900
,
0x0000CC00
,
0x0000FF00
,
0x00000033
,
0x00000066
,
0x00000099
,
0x000000CC
,
0x000000FF
,
0x00333300
,
0x00666600
,
0x00999900
,
0x00CCCC00
,
0x00FFFF00
,
0x00003333
,
0x00006666
,
0x00009999
,
0x0000CCCC
,
0x0000FFFF
,
0x00330033
,
0x00660066
,
0x00990099
,
0x00CC00CC
,
0x00FF00FF
,
0x00FFFF33
,
0x00FFFF66
,
0x00FFFF99
,
0x00FFFFCC
,
0x00FF33FF
,
0x00FF66FF
,
0x00FF99FF
,
0x00FFCCFF
,
0x0033FFFF
,
0x0066FFFF
,
0x0099FFFF
,
0x00CCFFFF
,
0x00CCCC33
,
0x00CCCC66
,
0x00CCCC99
,
0x00CCCCFF
,
0x00CC33CC
,
0x00CC66CC
,
0x00CC99CC
,
0x00CCFFCC
,
0x0033CCCC
,
0x0066CCCC
,
0x0099CCCC
,
0x00FFCCCC
,
0x00999933
,
0x00999966
,
0x009999CC
,
0x009999FF
,
0x00993399
,
0x00996699
,
0x0099CC99
,
0x0099FF99
,
0x00339999
,
0x00669999
,
0x00CC9999
,
0x00FF9999
,
0x00666633
,
0x00666699
,
0x006666CC
,
0x006666FF
,
0x00663366
,
0x00669966
,
0x0066CC66
,
0x0066FF66
,
0x00336666
,
0x00996666
,
0x00CC6666
,
0x00FF6666
,
0x00333366
,
0x00333399
,
0x003333CC
,
0x003333FF
,
0x00336633
,
0x00339933
,
0x0033CC33
,
0x0033FF33
,
0x00663333
,
0x00993333
,
0x00CC3333
,
0x00FF3333
,
0x00003366
,
0x00336600
,
0x00660033
,
0x00006633
,
0x00330066
,
0x00663300
,
0x00336699
,
0x00669933
,
0x00993366
,
0x00339966
,
0x00663399
,
0x00996633
,
0x006699CC
,
0x0099CC66
,
0x00CC6699
,
0x0066CC99
,
0x009966CC
,
0x00CC9966
,
0x0099CCFF
,
0x00CCFF99
,
0x00FF99CC
,
0x0099FFCC
,
0x00CC99FF
,
0x00FFCC99
,
0x00111111
,
0x00222222
,
0x00444444
,
0x00555555
,
0x00AAAAAA
,
0x00BBBBBB
,
0x00DDDDDD
,
0x00EEEEEE
};
static
int
generate_default_palette
(
Palette
*
palette
)
{
memcpy
(
palette
->
colors
,
default_screen_video_v2_palette
,
sizeof
(
default_screen_video_v2_palette
));
return
update_palette_index
(
palette
);
}
static
int
generate_optimum_palette
(
Palette
*
palette
,
const
uint8_t
*
image
,
int
width
,
int
height
,
int
stride
)
{
//this isn't implemented yet! Default palette only!
return
-
1
;
}
static
inline
int
encode_15_7_sl
(
Palette
*
palette
,
uint8_t
*
dest
,
const
uint8_t
*
src
,
int
width
,
int
dist
)
{
int
len
=
0
,
x
;
for
(
x
=
0
;
x
<
width
;
x
++
)
{
len
+=
write_pixel_15_7
(
palette
,
dest
+
len
,
src
+
3
*
x
,
dist
);
}
return
len
;
}
static
int
encode_15_7
(
Palette
*
palette
,
Block
*
b
,
const
uint8_t
*
src
,
int
stride
,
int
dist
)
{
int
i
;
uint8_t
*
ptr
=
b
->
enc
;
for
(
i
=
0
;
i
<
b
->
start
;
i
++
)
ptr
+=
encode_15_7_sl
(
palette
,
ptr
,
src
+
i
*
stride
,
b
->
width
,
dist
);
b
->
sl_begin
=
ptr
;
for
(;
i
<
b
->
start
+
b
->
len
;
i
++
)
ptr
+=
encode_15_7_sl
(
palette
,
ptr
,
src
+
i
*
stride
,
b
->
width
,
dist
);
b
->
sl_end
=
ptr
;
for
(;
i
<
b
->
height
;
i
++
)
ptr
+=
encode_15_7_sl
(
palette
,
ptr
,
src
+
i
*
stride
,
b
->
width
,
dist
);
b
->
enc_size
=
ptr
-
b
->
enc
;
return
b
->
enc_size
;
}
static
int
encode_block
(
Palette
*
palette
,
Block
*
b
,
Block
*
prev
,
const
uint8_t
*
src
,
int
stride
,
int
comp
,
int
dist
,
int
keyframe
)
{
unsigned
buf_size
=
b
->
width
*
b
->
height
*
6
;
uint8_t
buf
[
buf_size
];
int
res
;
if
(
b
->
flags
&
COLORSPACE_15_7
)
{
encode_15_7
(
palette
,
b
,
src
,
stride
,
dist
);
}
else
{
encode_bgr
(
b
,
src
,
stride
);
}
if
(
b
->
len
>
0
)
{
b
->
data_size
=
buf_size
;
res
=
encode_zlib
(
b
,
b
->
data
,
&
b
->
data_size
,
comp
);
if
(
res
)
return
res
;
if
(
!
keyframe
)
{
res
=
encode_zlibprime
(
b
,
prev
,
buf
,
&
buf_size
,
comp
);
if
(
res
)
return
res
;
if
(
buf_size
<
b
->
data_size
)
{
b
->
data_size
=
buf_size
;
memcpy
(
b
->
data
,
buf
,
buf_size
);
b
->
flags
|=
ZLIB_PRIME_COMPRESS_PREVIOUS
;
}
}
}
else
{
b
->
data_size
=
0
;
}
return
0
;
}
static
int
compare_sl
(
FlashSV2Context
*
s
,
Block
*
b
,
const
uint8_t
*
src
,
uint8_t
*
frame
,
uint8_t
*
key
,
int
y
,
int
keyframe
)
{
if
(
memcmp
(
src
,
frame
,
b
->
width
*
3
)
!=
0
)
{
b
->
dirty
=
1
;
memcpy
(
frame
,
src
,
b
->
width
*
3
);
#ifndef FLASHSV2_DUMB
s
->
diff_lines
++
;
#endif
}
if
(
memcmp
(
src
,
key
,
b
->
width
*
3
)
!=
0
)
{
if
(
b
->
len
==
0
)
b
->
start
=
y
;
b
->
len
=
y
+
1
-
b
->
start
;
}
return
0
;
}
static
int
mark_all_blocks
(
FlashSV2Context
*
s
,
const
uint8_t
*
src
,
int
stride
,
int
keyframe
)
{
int
sl
,
rsl
,
col
,
pos
,
possl
;
Block
*
b
;
for
(
sl
=
s
->
image_height
-
1
;
sl
>=
0
;
sl
--
)
{
for
(
col
=
0
;
col
<
s
->
cols
;
col
++
)
{
rsl
=
s
->
image_height
-
sl
-
1
;
b
=
s
->
frame_blocks
+
col
+
rsl
/
s
->
block_height
*
s
->
cols
;
possl
=
stride
*
sl
+
col
*
s
->
block_width
*
3
;
pos
=
s
->
image_width
*
rsl
*
3
+
col
*
s
->
block_width
*
3
;
compare_sl
(
s
,
b
,
src
+
possl
,
s
->
current_frame
+
pos
,
s
->
key_frame
+
pos
,
rsl
%
s
->
block_height
,
keyframe
);
}
}
#ifndef FLASHSV2_DUMB
s
->
tot_lines
+=
s
->
image_height
*
s
->
cols
;
#endif
return
0
;
}
static
int
encode_all_blocks
(
FlashSV2Context
*
s
,
int
keyframe
)
{
int
row
,
col
,
res
;
uint8_t
*
data
;
Block
*
b
,
*
prev
;
for
(
row
=
0
;
row
<
s
->
rows
;
row
++
)
{
for
(
col
=
0
;
col
<
s
->
cols
;
col
++
)
{
b
=
s
->
frame_blocks
+
(
row
*
s
->
cols
+
col
);
prev
=
s
->
key_blocks
+
(
row
*
s
->
cols
+
col
);
if
(
keyframe
)
{
b
->
start
=
0
;
b
->
len
=
b
->
height
;
b
->
flags
=
s
->
use15_7
?
COLORSPACE_15_7
:
0
;
}
else
if
(
!
b
->
dirty
)
{
b
->
start
=
0
;
b
->
len
=
0
;
b
->
data_size
=
0
;
b
->
flags
=
s
->
use15_7
?
COLORSPACE_15_7
:
0
;
continue
;
}
else
{
b
->
flags
=
s
->
use15_7
?
COLORSPACE_15_7
|
HAS_DIFF_BLOCKS
:
HAS_DIFF_BLOCKS
;
}
data
=
s
->
current_frame
+
s
->
image_width
*
3
*
s
->
block_height
*
row
+
s
->
block_width
*
col
*
3
;
res
=
encode_block
(
&
s
->
palette
,
b
,
prev
,
data
,
s
->
image_width
*
3
,
s
->
comp
,
s
->
dist
,
keyframe
);
#ifndef FLASHSV2_DUMB
if
(
b
->
dirty
)
s
->
diff_blocks
++
;
s
->
comp_size
+=
b
->
data_size
;
s
->
uncomp_size
+=
b
->
enc_size
;
#endif
if
(
res
)
return
res
;
}
}
#ifndef FLASHSV2_DUMB
s
->
raw_size
+=
s
->
image_width
*
s
->
image_height
*
3
;
s
->
tot_blocks
+=
s
->
rows
*
s
->
cols
;
#endif
return
0
;
}
static
int
write_all_blocks
(
FlashSV2Context
*
s
,
uint8_t
*
buf
,
int
buf_size
)
{
int
row
,
col
,
buf_pos
=
0
,
len
;
Block
*
b
;
for
(
row
=
0
;
row
<
s
->
rows
;
row
++
)
{
for
(
col
=
0
;
col
<
s
->
cols
;
col
++
)
{
b
=
s
->
frame_blocks
+
row
*
s
->
cols
+
col
;
len
=
write_block
(
b
,
buf
+
buf_pos
,
buf_size
-
buf_pos
);
b
->
start
=
b
->
len
=
b
->
dirty
=
0
;
if
(
len
<
0
)
return
len
;
buf_pos
+=
len
;
}
}
return
buf_pos
;
}
static
int
write_bitstream
(
FlashSV2Context
*
s
,
const
uint8_t
*
src
,
int
stride
,
uint8_t
*
buf
,
int
buf_size
,
int
keyframe
)
{
int
buf_pos
,
res
;
res
=
mark_all_blocks
(
s
,
src
,
stride
,
keyframe
);
if
(
res
)
return
res
;
res
=
encode_all_blocks
(
s
,
keyframe
);
if
(
res
)
return
res
;
res
=
write_header
(
s
,
buf
,
buf_size
);
if
(
res
<
0
)
{
return
res
;
}
else
{
buf_pos
=
res
;
}
res
=
write_all_blocks
(
s
,
buf
+
buf_pos
,
buf_size
-
buf_pos
);
if
(
res
<
0
)
return
res
;
buf_pos
+=
res
;
#ifndef FLASHSV2_DUMB
s
->
total_bits
+=
((
double
)
buf_pos
)
*
8
.
0
;
#endif
return
buf_pos
;
}
static
void
recommend_keyframe
(
FlashSV2Context
*
s
,
int
*
keyframe
)
{
#ifndef FLASHSV2_DUMB
double
block_ratio
,
line_ratio
,
enc_ratio
,
comp_ratio
,
data_ratio
;
if
(
s
->
avctx
->
gop_size
>
0
)
{
block_ratio
=
s
->
diff_blocks
/
s
->
tot_blocks
;
line_ratio
=
s
->
diff_lines
/
s
->
tot_lines
;
enc_ratio
=
s
->
uncomp_size
/
s
->
raw_size
;
comp_ratio
=
s
->
comp_size
/
s
->
uncomp_size
;
data_ratio
=
s
->
comp_size
/
s
->
raw_size
;
if
((
block_ratio
>=
0
.
5
&&
line_ratio
/
block_ratio
<=
0
.
5
)
||
line_ratio
>=
0
.
95
)
{
*
keyframe
=
1
;
return
;
}
}
#else
return
;
#endif
}
static
const
double
block_size_fraction
=
1
.
0
/
300
;
static
int
optimum_block_width
(
FlashSV2Context
*
s
)
{
#ifndef FLASHSV2_DUMB
double
save
=
(
1
-
pow
(
s
->
diff_lines
/
s
->
diff_blocks
/
s
->
block_height
,
0
.
5
))
*
s
->
comp_size
/
s
->
tot_blocks
;
double
width
=
block_size_fraction
*
sqrt
(
0
.
5
*
save
*
s
->
rows
*
s
->
cols
)
*
s
->
image_width
;
int
pwidth
=
((
int
)
width
);
return
FFCLIP
(
pwidth
&
~
15
,
256
,
16
);
#else
return
64
;
#endif
}
static
int
optimum_block_height
(
FlashSV2Context
*
s
)
{
#ifndef FLASHSV2_DUMB
double
save
=
(
1
-
pow
(
s
->
diff_lines
/
s
->
diff_blocks
/
s
->
block_height
,
0
.
5
))
*
s
->
comp_size
/
s
->
tot_blocks
;
double
height
=
block_size_fraction
*
sqrt
(
0
.
5
*
save
*
s
->
rows
*
s
->
cols
)
*
s
->
image_height
;
int
pheight
=
((
int
)
height
);
return
FFCLIP
(
pheight
&
~
15
,
256
,
16
);
#else
return
64
;
#endif
}
static
const
double
use15_7_threshold
=
8192
;
static
int
optimum_use15_7
(
FlashSV2Context
*
s
)
{
#ifndef FLASHSV2_DUMB
double
ideal
=
((
double
)(
s
->
avctx
->
bit_rate
*
s
->
avctx
->
time_base
.
den
*
s
->
avctx
->
ticks_per_frame
))
/
((
double
)
s
->
avctx
->
time_base
.
num
)
*
s
->
avctx
->
frame_number
;
if
(
ideal
+
use15_7_threshold
<
s
->
total_bits
)
{
return
1
;
}
else
{
return
0
;
}
#else
return
s
->
avctx
->
global_quality
==
0
;
#endif
}
static
const
double
color15_7_factor
=
100
;
static
int
optimum_dist
(
FlashSV2Context
*
s
)
{
#ifndef FLASHSV2_DUMB
double
ideal
=
s
->
avctx
->
bit_rate
*
s
->
avctx
->
time_base
.
den
*
s
->
avctx
->
ticks_per_frame
;
int
dist
=
pow
((
s
->
total_bits
/
ideal
)
*
color15_7_factor
,
3
);
av_log
(
s
->
avctx
,
AV_LOG_DEBUG
,
"dist: %d
\n
"
,
dist
);
return
dist
;
#else
return
15
;
#endif
}
static
int
reconfigure_at_keyframe
(
FlashSV2Context
*
s
,
const
uint8_t
*
image
,
int
stride
)
{
int
update_palette
=
0
;
int
res
;
s
->
block_width
=
optimum_block_width
(
s
);
s
->
block_height
=
optimum_block_height
(
s
);
s
->
rows
=
(
s
->
image_height
+
s
->
block_height
-
1
)
/
s
->
block_height
;
s
->
cols
=
(
s
->
image_width
+
s
->
block_width
-
1
)
/
s
->
block_width
;
if
(
s
->
rows
*
s
->
cols
!=
s
->
blocks_size
/
sizeof
(
Block
))
{
if
(
s
->
rows
*
s
->
cols
>
s
->
blocks_size
/
sizeof
(
Block
))
{
s
->
frame_blocks
=
av_realloc
(
s
->
frame_blocks
,
s
->
rows
*
s
->
cols
*
sizeof
(
Block
));
s
->
key_blocks
=
av_realloc
(
s
->
key_blocks
,
s
->
cols
*
s
->
rows
*
sizeof
(
Block
));
if
(
!
s
->
frame_blocks
||
!
s
->
key_blocks
)
{
av_log
(
s
->
avctx
,
AV_LOG_ERROR
,
"Memory allocation failed.
\n
"
);
return
-
1
;
}
s
->
blocks_size
=
s
->
rows
*
s
->
cols
*
sizeof
(
Block
);
}
init_blocks
(
s
,
s
->
frame_blocks
,
s
->
encbuffer
,
s
->
databuffer
);
init_blocks
(
s
,
s
->
key_blocks
,
s
->
keybuffer
,
0
);
}
s
->
use15_7
=
optimum_use15_7
(
s
);
if
(
s
->
use15_7
)
{
if
((
s
->
use_custom_palette
&&
s
->
palette_type
!=
1
)
||
update_palette
)
{
res
=
generate_optimum_palette
(
&
s
->
palette
,
image
,
s
->
image_width
,
s
->
image_height
,
stride
);
if
(
res
)
return
res
;
s
->
palette_type
=
1
;
av_log
(
s
->
avctx
,
AV_LOG_DEBUG
,
"Generated optimum palette
\n
"
);
}
else
if
(
!
s
->
use_custom_palette
&&
s
->
palette_type
!=
0
)
{
res
=
generate_default_palette
(
&
s
->
palette
);
if
(
res
)
return
res
;
s
->
palette_type
=
0
;
av_log
(
s
->
avctx
,
AV_LOG_DEBUG
,
"Generated default palette
\n
"
);
}
}
reset_stats
(
s
);
return
0
;
}
static
int
flashsv2_encode_frame
(
AVCodecContext
*
avctx
,
uint8_t
*
buf
,
int
buf_size
,
void
*
data
)
{
FlashSV2Context
*
const
s
=
avctx
->
priv_data
;
AVFrame
*
pict
=
data
;
AVFrame
*
const
p
=
&
s
->
frame
;
int
res
;
int
keyframe
=
0
;
*
p
=
*
pict
;
/* First frame needs to be a keyframe */
if
(
avctx
->
frame_number
==
0
)
keyframe
=
1
;
/* Check the placement of keyframes */
if
(
avctx
->
gop_size
>
0
)
{
if
(
avctx
->
frame_number
>=
s
->
last_key_frame
+
avctx
->
gop_size
)
keyframe
=
1
;
}
if
(
buf_size
<
s
->
frame_size
)
{
//Conservative upper bound check for compressed data
av_log
(
avctx
,
AV_LOG_ERROR
,
"buf_size %d < %d
\n
"
,
buf_size
,
s
->
frame_size
);
return
-
1
;
}
if
(
!
keyframe
&&
avctx
->
frame_number
>
s
->
last_key_frame
+
avctx
->
keyint_min
)
{
recommend_keyframe
(
s
,
&
keyframe
);
if
(
keyframe
)
av_log
(
avctx
,
AV_LOG_DEBUG
,
"Recommending key frame at frame %d
\n
"
,
avctx
->
frame_number
);
}
if
(
keyframe
)
{
res
=
reconfigure_at_keyframe
(
s
,
p
->
data
[
0
],
p
->
linesize
[
0
]);
if
(
res
)
return
res
;
}
if
(
s
->
use15_7
)
s
->
dist
=
optimum_dist
(
s
);
res
=
write_bitstream
(
s
,
p
->
data
[
0
],
p
->
linesize
[
0
],
buf
,
buf_size
,
keyframe
);
if
(
keyframe
)
{
new_key_frame
(
s
);
p
->
pict_type
=
FF_I_TYPE
;
p
->
key_frame
=
1
;
s
->
last_key_frame
=
avctx
->
frame_number
;
av_log
(
avctx
,
AV_LOG_DEBUG
,
"Inserting key frame at frame %d
\n
"
,
avctx
->
frame_number
);
}
else
{
p
->
pict_type
=
FF_P_TYPE
;
p
->
key_frame
=
0
;
}
avctx
->
coded_frame
=
p
;
return
res
;
}
static
av_cold
int
flashsv2_encode_end
(
AVCodecContext
*
avctx
)
{
FlashSV2Context
*
s
=
avctx
->
priv_data
;
cleanup
(
s
);
return
0
;
}
AVCodec
ff_flashsv2_encoder
=
{
"flashsv2"
,
AVMEDIA_TYPE_VIDEO
,
CODEC_ID_FLASHSV2
,
sizeof
(
FlashSV2Context
),
flashsv2_encode_init
,
flashsv2_encode_frame
,
flashsv2_encode_end
,
.
pix_fmts
=
(
enum
PixelFormat
[])
{
PIX_FMT_BGR24
,
PIX_FMT_NONE
},
.
long_name
=
NULL_IF_CONFIG_SMALL
(
"Flash Screen Video Version 2"
),
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment